Compare commits

...

210 Commits
v10.1 ... v10.3

Author SHA1 Message Date
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
379 changed files with 709229 additions and 11672 deletions

View File

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

1
.gitignore vendored
View File

@ -30,7 +30,6 @@ parsetab.py
compiler/lib/ compiler/lib/
.gradle .gradle
**/BuildVersion.kt
/prog8compiler.jar /prog8compiler.jar
sd*.img sd*.img
*.d64 *.d64

View File

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

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

View File

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

View File

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

2
.idea/modules.xml generated
View File

@ -10,10 +10,8 @@
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" /> <module fileurl="file://$PROJECT_DIR$/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$/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$/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$/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$/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$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> <module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />

View File

@ -9,17 +9,19 @@ version: 2
build: build:
os: ubuntu-22.04 os: ubuntu-22.04
tools: tools:
python: "3.11" python: "3.12"
# 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
# Optionally declare the Python requirements required to build your docs # Optionally declare the Python requirements required to build your docs
python: python:
install: install:
- requirements: docs/requirements.txt - 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/p8compile"
test:
gradle build
@echo "compiler launch script can be found here: compiler/build/install/compiler-shadow/bin/p8compile"

View File

@ -29,6 +29,8 @@ How to get it/build it
- Or, if you want/need a bleeding edge development version, you can: - 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). - 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). - you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
- Alternatively, you can also install the compiler as a package on some linux distros:
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
Community Community
--------- ---------
@ -49,8 +51,8 @@ GNU GPL 3.0 (see file LICENSE), with exception for generated code:
What does Prog8 provide? What does Prog8 provide?
------------------------ ------------------------
- reduction of source code length over raw assembly - all advantages of a higher level language over having to write assembly code manually
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8. - programs run very fast because compilation to native machine code
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - 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 if the target system provides floating point library routines (C64 and Cx16 both do)
@ -58,6 +60,7 @@ What does Prog8 provide?
- automatic static variable allocations, automatic string and array variables and string sharing - automatic static variable allocations, automatic string and array variables and string sharing
- subroutines with input parameters and result values - subroutines with input parameters and result values
- high-level program optimizations - high-level program optimizations
- no need for forward declarations
- small program boilerplate/compilersupport overhead - small program boilerplate/compilersupport overhead
- programs can be run multiple times without reloading because of automatic variable (re)initializations. - programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches - conditional branches
@ -68,7 +71,7 @@ What does Prog8 provide?
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses - convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- inline assembly allows you to have full control when every cycle or byte matters - 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. - 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) - encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
*Rapid edit-compile-run-debug cycle:* *Rapid edit-compile-run-debug cycle:*
@ -116,14 +119,13 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
%import textio %import textio
%zeropage basicsafe %zeropage basicsafe
main { main {
bool[256] sieve
ubyte[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop ubyte candidate_prime = 2 ; is increased in the loop
sub start() { 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") txt.print("prime numbers up to 255:\n\n")
ubyte amount=0 ubyte amount=0
repeat { repeat {
@ -159,9 +161,6 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
} }
} }
when compiled an ran on a C-64 you'll get: when compiled an ran on a C-64 you'll get:
![c64 screen](docs/source/_static/primes_example.png) ![c64 screen](docs/source/_static/primes_example.png)

View File

@ -26,7 +26,7 @@ compileTestKotlin {
dependencies { dependencies {
// should have no dependencies to other modules // should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
} }
sourceSets { sourceSets {

View File

@ -195,7 +195,7 @@ class StStaticVariable(name: String,
require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length) require(onetimeInitializationArrayValue.isEmpty() ||onetimeInitializationArrayValue.size==length)
} }
if(onetimeInitializationNumericValue!=null) { if(onetimeInitializationNumericValue!=null) {
require(dt in NumericDatatypes) require(dt in NumericDatatypes || dt==DataType.BOOL)
} }
if(onetimeInitializationArrayValue!=null) { if(onetimeInitializationArrayValue!=null) {
require(dt in ArrayDatatypes) require(dt in ArrayDatatypes)
@ -221,6 +221,7 @@ class StMemVar(name: String,
StNode(name, StNodeType.MEMVAR, astNode) { StNode(name, StNodeType.MEMVAR, astNode) {
init{ init{
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
if(dt in ArrayDatatypes || dt == DataType.STR) if(dt in ArrayDatatypes || dt == DataType.STR)
require(length!=null) { "memory mapped array or string must have known length" } require(length!=null) { "memory mapped array or string must have known length" }
} }
@ -250,7 +251,7 @@ class StRomSub(name: String,
class StSubroutineParameter(val name: String, val type: DataType) class StSubroutineParameter(val name: String, val type: DataType)
class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType) class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?) class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?)
typealias StString = Pair<String, Encoding> typealias StString = Pair<String, Encoding>
typealias StArray = List<StArrayElement> typealias StArray = List<StArrayElement>

View File

@ -128,10 +128,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
is PtAddressOf -> { is PtAddressOf -> {
if(it.isFromArrayElement) if(it.isFromArrayElement)
TODO("address-of array element $it in initial array value") TODO("address-of array element $it in initial array value")
StArrayElement(null, it.identifier.name) StArrayElement(null, it.identifier.name, null)
} }
is PtIdentifier -> StArrayElement(null, it.name) is PtIdentifier -> StArrayElement(null, it.name, null)
is PtNumber -> StArrayElement(it.number, null) is PtNumber -> StArrayElement(it.number, null, null)
is PtBool -> StArrayElement(null, null, it.value)
else -> throw AssemblyError("invalid array element $it") else -> throw AssemblyError("invalid array element $it")
} }
} }

View File

@ -67,6 +67,7 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>() children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? = fun entrypoint(): PtSub? =
// returns the main.start subroutine if it exists
allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" } allBlocks().firstOrNull { it.name == "main" || it.name=="p8b_main" }
?.children ?.children
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub? ?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8s_start" || it.name=="p8b_main.p8s_start") } as PtSub?

View File

@ -9,8 +9,6 @@ import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init { init {
if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position")
if(type==DataType.UNDEFINED) { if(type==DataType.UNDEFINED) {
@Suppress("LeakingThis") @Suppress("LeakingThis")
when(this) { when(this) {
@ -46,9 +44,10 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
} }
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
is PtMachineRegister -> other is PtMachineRegister && other.type==type && other.register==register is PtIrRegister -> other is PtIrRegister && other.type==type && other.register==register
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
is PtNumber -> other is PtNumber && other.type==type && other.number==number is PtNumber -> other is PtNumber && other.type==type && other.number==number
is PtBool -> other is PtBool && other.value==value
is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value is PtPrefix -> other is PtPrefix && other.type==type && other.operator==operator && other.value isSameAs value
is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step is PtRange -> other is PtRange && other.type==type && other.from==from && other.to==to && other.step==step
is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value is PtTypeCast -> other is PtTypeCast && other.type==type && other.value isSameAs value
@ -71,7 +70,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
} }
} }
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
fun isSimple(): Boolean { fun isSimple(): Boolean {
return when(this) { return when(this) {
@ -88,8 +87,9 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtContainmentCheck -> false is PtContainmentCheck -> false
is PtFunctionCall -> false is PtFunctionCall -> false
is PtIdentifier -> true is PtIdentifier -> true
is PtMachineRegister -> true is PtIrRegister -> true
is PtMemoryByte -> address is PtNumber || address is PtIdentifier is PtMemoryByte -> address is PtNumber || address is PtIdentifier
is PtBool -> true
is PtNumber -> true is PtNumber -> true
is PtPrefix -> value.isSimple() is PtPrefix -> value.isSimple()
is PtRange -> true is PtRange -> true
@ -116,6 +116,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position)) is PtMachineRegister -> return withClonedChildrenFrom(this, PtMachineRegister(register, type, position))
is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position)) is PtMemoryByte -> return withClonedChildrenFrom(this, PtMemoryByte(position))
is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position)) is PtNumber -> return withClonedChildrenFrom(this, PtNumber(type, number, position))
is PtBool -> return withClonedChildrenFrom(this, PtBool(value, position))
is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position)) is PtPrefix -> return withClonedChildrenFrom(this, PtPrefix(operator, type, position))
is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position)) is PtRange -> return withClonedChildrenFrom(this, PtRange(type, position))
is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position)) is PtString -> return withClonedChildrenFrom(this, PtString(value, encoding, position))
@ -138,18 +139,14 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) { class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
val variable: PtIdentifier val variable: PtIdentifier
get() { get() = children[0] as PtIdentifier
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
return children[0] as PtIdentifier
}
val index: PtExpression val index: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
val splitWords: Boolean val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes get() = variable.type in SplitWordArrayTypes
init { init {
require(elementType in NumericDatatypes) require(elementType in NumericDatatypesWithBoolean)
} }
} }
@ -183,15 +180,21 @@ class PtBuiltinFunctionCall(val name: String,
class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) { class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
// note: "and", "or", "xor" do not occur anymore as operators. They've been replaced int the ast by their bitwise versions &, |, ^.
val left: PtExpression val left: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
val right: PtExpression val right: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
}
} }
class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) { class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
val element: PtExpression val element: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
val iterable: PtIdentifier val iterable: PtIdentifier
@ -203,11 +206,6 @@ class PtFunctionCall(val name: String,
val void: Boolean, val void: Boolean,
type: DataType, type: DataType,
position: Position) : PtExpression(type, position) { position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
}
val args: List<PtExpression> val args: List<PtExpression>
get() = children.map { it as PtExpression } get() = children.map { it as PtExpression }
} }
@ -228,6 +226,27 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
} }
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
companion object {
fun fromNumber(number: Number, position: Position): PtBool =
PtBool(if(number==0.0) false else true, position)
}
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtBool)
return false
return value==other.value
}
override fun toString() = "PtBool:$value"
fun asInt(): Int = if(value) 1 else 0
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
companion object { companion object {
@ -237,7 +256,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
init { init {
if(type==DataType.BOOL) if(type==DataType.BOOL)
throw IllegalArgumentException("bool should have become ubyte @$position") throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) { if(type!=DataType.FLOAT) {
val trunc = truncate(number) val trunc = truncate(number)
if (trunc != number) if (trunc != number)
@ -267,8 +286,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression get() = children.single() as PtExpression
init { init {
// note: the "not" operator may no longer occur in the ast; not x should have been replaced with x==0 require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
require(operator in setOf("+", "-", "~")) { "invalid prefix operator: $operator" }
} }
} }
@ -327,8 +345,8 @@ class PtTypeCast(type: DataType, position: Position) : PtExpression(type, positi
// special node that isn't created from compiling user code, but used internally in the Intermediate Code // special node that isn't created from compiling user code, but used internally in the Intermediate Code
class PtMachineRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position)
fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else null fun constValue(expr: PtExpression): Double? = if(expr is PtNumber) expr.number else if(expr is PtBool) expr.asInt().toDouble() else null
fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else null fun constIntValue(expr: PtExpression): Int? = if(expr is PtNumber) expr.number.toInt() else if(expr is PtBool) expr.asInt() else null

View File

@ -10,12 +10,17 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
fun type(dt: DataType) = "!${dt.name.lowercase()}!" fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun txt(node: PtNode): String { fun txt(node: PtNode): String {
return when(node) { return when(node) {
is PtAssignTarget -> "<target>" is PtAssignTarget -> if(node.void) "<void>" else "<target>"
is PtAssignment -> "<assign>" is PtAssignment -> "<assign>"
is PtAugmentedAssign -> "<inplace-assign> ${node.operator}" is PtAugmentedAssign -> "<inplace-assign> ${node.operator}"
is PtBreakpoint -> "%breakpoint" is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&" is PtAddressOf -> {
if(node.isFromArrayElement)
"& array-element"
else
"&"
}
is PtArray -> "array len=${node.children.size} ${type(node.type)}" is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}" is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}" is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
@ -29,12 +34,13 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str + node.name + "()" str + node.name + "()"
} }
is PtIdentifier -> "${node.name} ${type(node.type)}" is PtIdentifier -> "${node.name} ${type(node.type)}"
is PtMachineRegister -> "VMREG#${node.register} ${type(node.type)}" is PtIrRegister -> "IRREG#${node.register} ${type(node.type)}"
is PtMemoryByte -> "@()" is PtMemoryByte -> "@()"
is PtNumber -> { is PtNumber -> {
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex() val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}" "$numstr ${type(node.type)}"
} }
is PtBool -> node.value.toString()
is PtPrefix -> node.operator is PtPrefix -> node.operator
is PtRange -> "<range>" is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\"" is PtString -> "\"${node.value.escape()}\""
@ -84,7 +90,11 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"\nblock '${node.name}' $addr $align" "\nblock '${node.name}' $addr $align"
} }
is PtConstant -> { is PtConstant -> {
val value = if(node.type in IntegerDatatypes) node.value.toInt().toString() else node.value.toString() val value = when(node.type) {
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value" "const ${node.type.name.lowercase()} ${node.name} = $value"
} }
is PtLabel -> "${node.name}:" is PtLabel -> "${node.name}:"
@ -121,9 +131,8 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else else
str str
} }
is PtNodeGroup -> "<group>" is PtNodeGroup -> if(node.children.isNotEmpty()) "<group>" else ""
is PtNop -> "nop" is PtNop -> "nop"
is PtPostIncrDecr -> "<post> ${node.operator}"
is PtProgram -> "PROGRAM ${node.name}" is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat" is PtRepeatLoop -> "repeat"
is PtReturn -> "return" is PtReturn -> "return"

View File

@ -26,10 +26,10 @@ class PtSub(
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer { ) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init { init {
// params and return value should not be str // params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes }) if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
throw AssemblyError("non-numeric parameter") throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes) if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
throw AssemblyError("non-numeric returntype $returntype") throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this } parameters.forEach { it.parent=this }
} }
} }
@ -41,9 +41,18 @@ class PtSubroutineParameter(name: String, val type: DataType, position: Position
sealed interface IPtAssignment { sealed interface IPtAssignment {
val children: MutableList<PtNode> val children: MutableList<PtNode>
val target: PtAssignTarget val target: PtAssignTarget
get() = children[0] as PtAssignTarget get() {
if(children.size==2)
return children[0] as PtAssignTarget
else if(children.size<2)
throw AssemblyError("incomplete node")
else
throw AssemblyError("no singular target")
}
val value: PtExpression val value: PtExpression
get() = children[1] as PtExpression get() = children.last() as PtExpression
val multiTarget: Boolean
get() = children.size>2
} }
class PtAssignment(position: Position) : PtNode(position), IPtAssignment class PtAssignment(position: Position) : PtNode(position), IPtAssignment
@ -51,7 +60,7 @@ class PtAssignment(position: Position) : PtNode(position), IPtAssignment
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
class PtAssignTarget(position: Position) : PtNode(position) { class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
val identifier: PtIdentifier? val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier get() = children.single() as? PtIdentifier
val array: PtArrayIndexer? val array: PtArrayIndexer?
@ -69,7 +78,7 @@ class PtAssignTarget(position: Position) : PtNode(position) {
} }
} }
infix fun isSameAs(expression: PtExpression): Boolean = expression.isSameAs(this) infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
} }
@ -98,6 +107,8 @@ class PtIfElse(position: Position) : PtNode(position) {
get() = children[1] as PtNodeGroup get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup get() = children[2] as PtNodeGroup
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
} }
@ -110,12 +121,6 @@ class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are
} }
class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) {
val target: PtAssignTarget
get() = children.single() as PtAssignTarget
}
class PtRepeatLoop(position: Position) : PtNode(position) { class PtRepeatLoop(position: Position) : PtNode(position) {
val count: PtExpression val count: PtExpression
get() = children[0] as PtExpression get() = children[0] as PtExpression
@ -152,7 +157,11 @@ class PtVariable(name: String, override val type: DataType, val zeropage: Zeropa
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
}
}
class PtWhen(position: Position) : PtNode(position) { class PtWhen(position: Position) : PtNode(position) {

View File

@ -79,27 +79,28 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null), "sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
"reverse" 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 returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_arraycopy" to FSignature(false, listOf(FParam("source", ArrayDatatypes), FParam("target", ArrayDatatypes)), null),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE), "prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD), "prog8_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" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE), "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__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), "abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow: // normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE), "sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE), "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null), "sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "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__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), "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" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), "divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), "divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.BOOL),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), 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), "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), "mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
@ -132,4 +133,9 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), "call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
) )
val InplaceModifyingBuiltinFunctions = setOf("setlsb", "setmsb", "rol", "ror", "rol2", "ror2", "sort", "reverse") val InplaceModifyingBuiltinFunctions = setOf(
"setlsb", "setmsb",
"rol", "ror", "rol2", "ror2",
"sort", "reverse",
"divmod", "divmod__ubyte", "divmod__uword"
)

View File

@ -19,12 +19,15 @@ class CompilationOptions(val output: OutputType,
var asmQuiet: Boolean = false, var asmQuiet: Boolean = false,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false, var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false,
var dumpSymbols: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null, var varsHighBank: Int? = null,
var varsGolden: Boolean = false, var varsGolden: Boolean = false,
var slabsHighBank: Int? = null, var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false, var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false, var splitWordArrays: Boolean = false,
var strictBool: Boolean = true,
var breakpointCpuInstruction: String? = null, var breakpointCpuInstruction: String? = null,
var outputDir: Path = Path(""), var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap() var symbolDefs: Map<String, String> = emptyMap()

View File

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

View File

@ -24,8 +24,8 @@ enum class DataType {
*/ */
infix fun isAssignableTo(targetType: DataType) = infix fun isAssignableTo(targetType: DataType) =
when(this) { when(this) {
BOOL -> targetType.oneOf(BOOL, BYTE, UBYTE, WORD, UWORD, LONG, FLOAT) BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT, BOOL) UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT) BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT) UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT) WORD -> targetType.oneOf(WORD, LONG, FLOAT)
@ -41,9 +41,9 @@ enum class DataType {
infix fun largerThan(other: DataType) = infix fun largerThan(other: DataType) =
when { when {
this == other -> false this == other -> false
this in ByteDatatypes -> false this in ByteDatatypesWithBoolean -> false
this in WordDatatypes -> other in ByteDatatypes this in WordDatatypes -> other in ByteDatatypesWithBoolean
this == LONG -> other in ByteDatatypes+WordDatatypes this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
this == STR && other == UWORD || this == UWORD && other == STR -> false this == STR && other == UWORD || this == UWORD && other == STR -> false
else -> true else -> true
} }
@ -51,7 +51,7 @@ enum class DataType {
infix fun equalsSize(other: DataType) = infix fun equalsSize(other: DataType) =
when { when {
this == other -> true this == other -> true
this in ByteDatatypes -> other in ByteDatatypes this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
this in WordDatatypes -> other in WordDatatypes this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true this== STR && other== UWORD || this== UWORD && other== STR -> true
else -> false else -> false
@ -124,12 +124,13 @@ enum class BranchCondition {
} }
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.BOOL) val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD) val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG) val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypes = IntegerDatatypesNoBool + DataType.BOOL val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT) val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypes = NumericDatatypesNoBool + DataType.BOOL val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT) val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL) val 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 StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
@ -141,7 +142,7 @@ val IterableDatatypes = arrayOf(
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL DataType.ARRAY_F, DataType.ARRAY_BOOL
) )
val PassByValueDatatypes = NumericDatatypes val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf( val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE, DataType.STR to DataType.UBYTE,

View File

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

View File

@ -26,6 +26,9 @@ interface IMachineDefinition {
fun initializeMemoryAreas(compilerOptions: CompilationOptions) fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String fun getFloatAsmBytes(num: Number): String
fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean fun isIOAddress(address: UInt): Boolean

View File

@ -5,7 +5,10 @@ enum class Encoding(val prefix: String) {
PETSCII("petscii"), // c64/c128/cx16 PETSCII("petscii"), // c64/c128/cx16
SCREENCODES("sc"), // c64/c128/cx16 SCREENCODES("sc"), // c64/c128/cx16
ATASCII("atascii"), // atari 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)
} }
interface IStringEncoding { interface IStringEncoding {

View File

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

View File

@ -1,8 +1,8 @@
package prog8.code.core package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "and", "or", "xor") val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not") val LogicalOperators = setOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~") val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not") val PrefixOperators = setOf("+", "-", "~", "not")

View File

@ -1,13 +1,18 @@
package prog8.code.optimize package prog8.code.optimize
import prog8.code.StRomSub
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, errors: IErrorReporter) { fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
if (!options.optimize) if (!options.optimize)
return return
while(errors.noErrors() && optimizeCommonSubExpressions(program, errors)>0) { while (errors.noErrors() &&
(optimizeCommonSubExpressions(program, errors)
+ optimizeAssignTargets(program, st, errors)) > 0
) {
// keep rolling // keep rolling
} }
} }
@ -27,11 +32,15 @@ private var tempVarCounter = 0
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int { private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
fun extractableSubExpr(expr: PtExpression): Boolean { fun extractableSubExpr(expr: PtExpression): Boolean {
if(expr is PtArrayIndexer && expr.index.isSimple())
return false
if (expr is PtMemoryByte && expr.address.isSimple())
return false
val result = if(expr is PtBinaryExpression) val result = if(expr is PtBinaryExpression)
expr.type !in ByteDatatypes || expr.type !in ByteDatatypes ||
!expr.left.isSimple() || !(expr.left.isSimple() && expr.right.isSimple()) ||
!expr.right.isSimple() || (expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes) else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
true true
else else
@ -89,7 +98,7 @@ private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorRepor
singleReplacement2.parent = occurrence2.parent singleReplacement2.parent = occurrence2.parent
val tempassign = PtAssignment(binexpr.position).also { assign -> val tempassign = PtAssignment(binexpr.position).also { assign ->
assign.add(PtAssignTarget(binexpr.position).also { tgt-> assign.add(PtAssignTarget(false, binexpr.position).also { tgt->
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position)) tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
}) })
assign.add(occurrence1) assign.add(occurrence1)
@ -109,6 +118,79 @@ private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorRepor
} }
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
var changes = 0
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtAssignment) {
val value = node.value
val functionName = when(value) {
is PtBuiltinFunctionCall -> value.name
is PtFunctionCall -> value.name
else -> null
}
if(functionName!=null) {
val stNode = st.lookup(functionName)
if (stNode is StRomSub) {
require(node.children.size==stNode.returns.size+1) {
"number of targets must match return values"
}
node.children.zip(stNode.returns).withIndex().forEach { (index, xx) ->
val target = xx.first as PtAssignTarget
val returnedRegister = xx.second.register.registerOrPair
if(returnedRegister!=null && !target.void && target.identifier!=null) {
if(isSame(target.identifier!!, xx.second.type, returnedRegister)) {
// output register is already identical to target register, so it can become void
val voidTarget = PtAssignTarget(true, target.position)
node.children[index] = voidTarget
voidTarget.parent = node
changes++
}
}
}
}
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
val index = node.parent.children.indexOf(node)
val voidCall = PtFunctionCall(functionName, true, value.type, value.position)
value.children.forEach { voidCall.add(it) }
node.parent.children[index] = voidCall
voidCall.parent = node.parent
changes++
}
}
}
true
}
return changes
}
internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister: RegisterOrPair): Boolean {
if(returnedRegister in Cx16VirtualRegisters) {
val regname = returnedRegister.name.lowercase()
val identifierRegName = identifier.name.substringAfterLast('.')
/*
cx16.r? UWORD
cx16.r?s WORD
cx16.r?L UBYTE
cx16.r?H UBYTE
cx16.r?sL BYTE
cx16.r?sH BYTE
*/
if(identifier.type in ByteDatatypes && type in ByteDatatypes) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
}
}
else if(identifier.type in WordDatatypes && type in WordDatatypes) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "s")
}
}
}
return false // there are no identifiers directly corresponding to cpu registers
}
internal fun findScopeName(node: PtNode): String { internal fun findScopeName(node: PtNode): String {
var parent=node var parent=node
while(parent !is PtNamedNode) while(parent !is PtNamedNode)

View File

@ -15,7 +15,7 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int { override fun memorySize(dt: DataType): Int {
return when(dt) { return when(dt) {
in ByteDatatypes -> 1 in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2 in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") else -> throw IllegalArgumentException("invalid datatype")
@ -23,5 +23,9 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
} }

View File

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

View File

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

View File

@ -14,7 +14,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override fun memorySize(dt: DataType): Int { override fun memorySize(dt: DataType): Int {
return when(dt) { return when(dt) {
in ByteDatatypes -> 1 in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2 in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") else -> throw IllegalArgumentException("invalid datatype")
@ -22,5 +22,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun memorySize(arrayDt: DataType, numElements: Int) =
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
} }

View File

@ -22,6 +22,8 @@ class AtariMachineDefinition: IMachineDefinition {
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number") override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.output == OutputType.XEX) return if (compilerOptions.output == OutputType.XEX)

View File

@ -25,6 +25,17 @@ class C128MachineDefinition: IMachineDefinition {
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")

View File

@ -26,6 +26,17 @@ class C64MachineDefinition: IMachineDefinition {
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")

View File

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

View File

@ -24,6 +24,18 @@ class CX16MachineDefinition: IMachineDefinition {
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")

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.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result

View File

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

View File

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

View File

@ -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.Err
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok

View File

@ -25,6 +25,17 @@ class PETMachineDefinition: IMachineDefinition {
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm() 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG) return if (compilerOptions.launcher == CbmPrgLauncherType.BASIC || compilerOptions.output == OutputType.PRG)
listOf("syslib") listOf("syslib")

View File

@ -30,6 +30,26 @@ class VirtualMachineDefinition: IMachineDefinition {
return parts.joinToString(", ") 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> { override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
return listOf("syslib") return listOf("syslib")
} }

View File

@ -1,9 +1,7 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id "org.jetbrains.kotlin.jvm"
id "io.kotest" version "0.3.9"
} }
java { java {
@ -28,9 +26,11 @@ dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }
sourceSets { sourceSets {

File diff suppressed because it is too large Load Diff

View File

@ -58,15 +58,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++ numberOfOptimizations++
} }
mods = optimizeSamePointerIndexing(linesByFourteen) mods = optimizeSamePointerIndexingAndUselessBeq(linesByFourteen)
if(mods.isNotEmpty()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14) linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++ numberOfOptimizations++
} }
// TODO more assembly peephole optimizations
return numberOfOptimizations return numberOfOptimizations
} }
@ -75,23 +73,48 @@ private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz") private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx") 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>) { private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) { for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
if(modification.remove) if(modification.remove) {
lines.removeAt(modification.lineIndex) 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 else
lines[modification.lineIndex] = modification.replacement!! 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) = private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding windows of certain size // 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( private fun optimizeSameAssignments(
linesByFourteen: List<List<IndexedValue<String>>>, linesByFourteen: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: IMachineDefinition,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): List<Modification> {
@ -281,7 +304,7 @@ private fun optimizeSameAssignments(
return mods 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 // 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: // if Y isn't modified in between we can omit the second LDY:
@ -318,31 +341,57 @@ private fun optimizeSamePointerIndexing(linesByFourteen: List<List<IndexedValue<
mods.add(Modification(lines[4].index, true, null)) mods.add(Modification(lines[4].index, true, null))
} }
} }
/*
beq +
lda #1
+
[ optional: label_xxxx_shortcut line here]
beq label_xxxx_shortcut / bne label_xxxx_shortcut
or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq label_") || fourth.startsWith("bne label_")) &&
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
else if(fourth.startsWith("label_") && (fourth.endsWith("_shortcut") || fourth.endsWith("_shortcut:"))) {
if((fifth.startsWith("beq label_") || fifth.startsWith("bne label_")) &&
(fifth.endsWith("_shortcut") || fifth.endsWith("_afterif") || fifth.endsWith("_shortcut:") || fifth.endsWith("_afterif:"))) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
}
} }
return mods return mods
} }
private fun optimizeStoreLoadSame( private fun optimizeStoreLoadSame(
linesByFour: List<List<IndexedValue<String>>>, linesByFour: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: IMachineDefinition,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): 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>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
val first = lines[1].value.trimStart() val first = lines[1].value.trimStart()
val second = lines[2].value.trimStart() val second = lines[2].value.trimStart()
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
if ((first.startsWith("sta ") && second.startsWith("lda ")) || if ((first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("stx ") && second.startsWith("ldx ")) || (first.startsWith("stx ") && second.startsWith("ldx ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) || (first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("lda ") && second.startsWith("lda ")) || (first.startsWith("lda ") && second.startsWith("lda ")) ||
(first.startsWith("ldy ") && second.startsWith("ldy ")) || (first.startsWith("ldy ") && second.startsWith("ldy ")) ||
(first.startsWith("ldx ") && second.startsWith("ldx ")) || (first.startsWith("ldx ") && second.startsWith("ldx "))
(first.startsWith("sta ") && second.startsWith("lda ")) ||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
(first.startsWith("stx ") && second.startsWith("ldx "))
) { ) {
val third = lines[3].value.trimStart() val third = lines[3].value.trimStart()
val attemptRemove = val attemptRemove =
@ -385,6 +434,18 @@ private fun optimizeStoreLoadSame(
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, false, " tya")) mods.add(Modification(lines[2].index, false, " tya"))
} }
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
if ((first.startsWith("lda ") && second.startsWith("sta ")) ||
(first.startsWith("ldx ") && second.startsWith("stx ")) ||
(first.startsWith("ldy ") && second.startsWith("sty "))
) {
val firstLoc = first.substring(4).trimStart()
val secondLoc = second.substring(4).trimStart()
if (firstLoc == secondLoc)
mods.add(Modification(lines[2].index, true, null))
}
} }
return mods return mods
} }
@ -414,7 +475,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. // sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
@ -431,14 +492,17 @@ private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<
mods.add(Modification(lines[0].index, true, null)) mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
} }
} }
return mods 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 // jsr Sub + rts -> jmp Sub
// rts + jmp -> remove jmp // rts + jmp -> remove jmp
// rts + bxx -> remove bxx // rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
// and some other optimizations.
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
for (lines in linesByFour) { for (lines in linesByFour) {
@ -470,11 +534,54 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
else if (" bvc" in second || "\tbvc" in second) else if (" bvc" in second || "\tbvc" in second)
mods += Modification(lines[1].index, true, null) mods += Modification(lines[1].index, true, null)
} }
if (!haslabel(second)) {
if ((" lda" in first || "\tlda" in first) && (" cmp #0" in second || "\tcmp #0" in second) ||
(" ldx" in first || "\tldx" in first) && (" cpx #0" in second || "\tcpx #0" in second) ||
(" ldy" in first || "\tldy" in first) && (" cpy #0" in second || "\tcpy #0" in second)
) {
mods.add(Modification(lines[1].index, true, null))
}
else if(" cmp #0" in second || "\tcmp #0" in second) {
// there are many instructions that modify A and set the bits...
for(instr in arrayOf("lda", "ora", "and", "eor", "adc", "sbc", "asl", "cmp", "inc a", "lsr", "pla", "rol", "ror", "txa", "tya")) {
if(" $instr" in first || "\t$instr" in first) {
mods.add(Modification(lines[1].index, true, null))
}
}
}
}
/*
LDA NUM1
CMP NUM2
BCC LABEL
BEQ LABEL
(or something similar) which branches to LABEL when NUM1 <= NUM2. (In this case NUM1 and NUM2 are unsigned numbers.) However, consider the following sequence:
LDA NUM2
CMP NUM1
BCS LABEL
*/
val tfirst = first.trimStart()
val tsecond = second.trimStart()
val tthird = lines[2].value.trimStart()
val tfourth = lines[3].value.trimStart()
if(tfirst.startsWith("lda") && tsecond.startsWith("cmp") && tthird.startsWith("bcc") && tfourth.startsWith("beq")) {
val label = tthird.substring(4)
if(label==tfourth.substring(4)) {
mods += Modification(lines[0].index, false, " lda ${tsecond.substring(4)}")
mods += Modification(lines[1].index, false, " cmp ${tfirst.substring(4)}")
mods += Modification(lines[2].index, false, " bcs $label")
mods += Modification(lines[3].index, true, null)
}
}
} }
return mods 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>() val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) { fun optimize(register: Char, lines: List<IndexedValue<String>>) {
@ -511,7 +618,7 @@ private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<Stri
return mods return mods
} }
private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<String>>>): List<Modification> { private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX // sequence: sta P8ZP_SCRATCH_XX / lda something / clc / adc P8ZP_SCRATCH_XX
// this can be performed without the scratch variable: clc / adc something // this can be performed without the scratch variable: clc / adc something
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
@ -523,8 +630,8 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: List<List<IndexedValue<Str
val fourth = lines[3].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(first.startsWith("sta P8ZP_SCRATCH_") && second.startsWith("lda") && third.startsWith("clc") && fourth.startsWith("adc P8ZP_SCRATCH_") ) {
if(fourth.substring(4)==first.substring(4)) { if(fourth.substring(4)==first.substring(4)) {
mods.add(Modification(lines[0].index, false, " clc")) mods.add(Modification(lines[0].index, false, " clc"))
mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}")) mods.add(Modification(lines[1].index, false, " adc ${second.substring(3).trimStart()}"))
mods.add(Modification(lines[2].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[3].index, true, null))
} }

View File

@ -71,12 +71,102 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister) "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister) "prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister) "prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
"prog8_lib_arraycopy" -> funcArrayCopy(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
} }
return BuiltinFunctions.getValue(fcall.name).returnType return BuiltinFunctions.getValue(fcall.name).returnType
} }
private fun funcArrayCopy(fcall: PtBuiltinFunctionCall) {
val source = fcall.args[0] as PtIdentifier
val target = fcall.args[1] as PtIdentifier
val sourceSymbol = asmgen.symbolTable.lookup(source.name)
val numElements = when(sourceSymbol) {
is StStaticVariable -> sourceSymbol.length!!
is StMemVar -> sourceSymbol.length!!
else -> 0
}
val sourceAsm = asmgen.asmVariableName(source)
val targetAsm = asmgen.asmVariableName(target)
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split -> split words (copy lsb and msb arrays separately)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numElements and 255}
jsr prog8_lib.memcopy_small""")
}
else if(source.type in SplitWordArrayTypes) {
// split word array to normal word array (copy lsb and msb arrays separately)
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${sourceAsm}_lsb
ldy #>${sourceAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${sourceAsm}_msb
ldy #>${sourceAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${targetAsm}
ldy #>${targetAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_split_to_normal_words""")
}
else if(target.type in SplitWordArrayTypes) {
// normal word array to split array
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
asmgen.out("""
lda #<${targetAsm}_lsb
ldy #>${targetAsm}_lsb
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}_msb
ldy #>${targetAsm}_msb
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<${sourceAsm}
ldy #>${sourceAsm}
ldx #${numElements and 255}
jsr prog8_lib.arraycopy_normal_to_split_words""")
}
else {
// normal array to array copy, various element types
val eltsize = asmgen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
val numBytes = numElements * eltsize
asmgen.out("""
lda #<${sourceAsm}
ldy #>${sourceAsm}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${targetAsm}
ldy #>${targetAsm}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #${numBytes and 255}
jsr prog8_lib.memcopy_small""")
}
}
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) { private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine. // square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
when (resultType) { when (resultType) {
@ -225,6 +315,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number.toInt()}") asmgen.out(" cmp #${arg2.number.toInt()}")
} }
is PtBool -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.asInt()}")
}
is PtMemoryByte -> { is PtMemoryByte -> {
if(arg2.address is PtNumber) { if(arg2.address is PtNumber) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
@ -253,6 +347,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
cmp ${asmgen.asmVariableName(arg2)} cmp ${asmgen.asmVariableName(arg2)}
+""") +""")
} }
is PtBool -> TODO("word compare against bool")
is PtNumber -> { is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
@ -665,6 +760,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
is PtAddressOf -> { is PtAddressOf -> {
val mem = PtMemoryByte(fcall.position) val mem = PtMemoryByte(fcall.position)
if((fcall.args[0] as PtAddressOf).isFromArrayElement)
TODO("address-of arrayelement")
if(msb) { if(msb) {
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position) val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
address.add(fcall.args[0]) address.add(fcall.args[0])
@ -1313,8 +1410,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
ldy #>$identifierName ldy #>$identifierName
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda #$numElements lda #${numElements and 255}""")
""")
} }
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) { private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {

View File

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

View File

@ -359,7 +359,7 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel bne $loopLabel
$endLabel""") $endLabel""")
} }
DataType.ARRAY_UB, DataType.ARRAY_B -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
val indexVar = asmgen.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0

View File

@ -16,8 +16,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
internal fun optimizeIntArgsViaRegisters(sub: PtSub) = internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes) (sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
internal fun translateFunctionCall(call: PtFunctionCall) { internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call // Output only the code to set up the parameters and perform the actual call
@ -46,7 +46,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
else if(sub is PtSub) { else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) { if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
val register = if (sub.parameters[0].type in ByteDatatypes) RegisterOrPair.A else RegisterOrPair.AY val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register) argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
} else { } else {
// 2 byte params, second in Y, first in A // 2 byte params, second in Y, first in A
@ -81,9 +81,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
is PtAddressOf -> false is PtAddressOf -> false
is PtIdentifier -> false is PtIdentifier -> false
is PtMachineRegister -> false is PtIrRegister -> false
is PtMemoryByte -> false is PtMemoryByte -> return usesOtherRegistersWhileEvaluating(arg.address)
is PtNumber -> false is PtNumber -> false
is PtBool -> false
else -> true else -> true
} }
} }
@ -152,7 +153,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
if (statusflag!=null) { if (statusflag!=null) {
if(requiredDt!=value.type) 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) { if (statusflag == Statusflag.Pc) {
// this boolean param needs to be set last, right before the jsr // this boolean param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker // for now, this is already enforced on the subroutine definition by the Ast Checker
@ -161,6 +162,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val carrySet = value.number.toInt() != 0 val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc") asmgen.out(if(carrySet) " sec" else " clc")
} }
is PtBool -> {
asmgen.out(if(value.value) " sec" else " clc")
}
is PtIdentifier -> { is PtIdentifier -> {
val sourceName = asmgen.asmVariableName(value) val sourceName = asmgen.asmVariableName(value)
// note: cannot use X register here to store A because it might be used for other arguments // note: cannot use X register here to store A because it might be used for other arguments
@ -218,6 +222,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType) if(argType isAssignableTo paramType)
return true return true
if(argType==DataType.BOOL && paramType==DataType.BOOL)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes) if(argType in ByteDatatypes && paramType in ByteDatatypes)
return true return true
if(argType in WordDatatypes && paramType in WordDatatypes) if(argType in WordDatatypes && paramType in WordDatatypes)

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -173,6 +173,7 @@ internal class ProgramAndVarsGen(
for(num in 1..count) { for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num) val name = asmgen.buildTempVarName(dt, num)
when (dt) { when (dt) {
DataType.BOOL -> asmgen.out("$name .byte ?")
DataType.BYTE -> asmgen.out("$name .char ?") DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?") DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?") DataType.WORD -> asmgen.out("$name .sint ?")
@ -404,7 +405,7 @@ internal class ProgramAndVarsGen(
if(sub.parameters.size==1) { if(sub.parameters.size==1) {
val dt = sub.parameters[0].type val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name) val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypes) if(dt in ByteDatatypesWithBoolean)
asmgen.assignRegister(RegisterOrPair.A, target) asmgen.assignRegister(RegisterOrPair.A, target)
else else
asmgen.assignRegister(RegisterOrPair.AY, target) asmgen.assignRegister(RegisterOrPair.AY, target)
@ -604,7 +605,7 @@ internal class ProgramAndVarsGen(
private fun uninitializedVariable2asm(variable: StStaticVariable) { private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) { when (variable.dt) {
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?") DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?") DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?") DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?") DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
@ -634,7 +635,7 @@ internal class ProgramAndVarsGen(
} else 0 } else 0
when (variable.dt) { when (variable.dt) {
DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}") DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue") DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}") DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue") DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
@ -658,7 +659,7 @@ internal class ProgramAndVarsGen(
private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) { private fun arrayVariable2asm(varname: String, dt: DataType, value: StArray?, orNumberOfZeros: Int?) {
when(dt) { when(dt) {
DataType.ARRAY_UB -> { DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}") asmgen.out("$varname\t.byte ${data.joinToString()}")
@ -726,7 +727,7 @@ internal class ProgramAndVarsGen(
private fun zeroFilledArray(numElts: Int): StArray { private fun zeroFilledArray(numElts: Int): StArray {
val values = mutableListOf<StArrayElement>() val values = mutableListOf<StArrayElement>()
repeat(numElts) { repeat(numElts) {
values.add(StArrayElement(0.0, null)) values.add(StArrayElement(0.0, null, null))
} }
return values return values
} }
@ -763,6 +764,16 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> { private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) { return when (dt) {
DataType.ARRAY_BOOL ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
if(it.boolean!=null)
if(it.boolean==true) "1" else "0"
else {
val number = it.number!!
if(number==0.0) "0" else "1"
}
}
DataType.ARRAY_UB -> DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
array.map { array.map {

View File

@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) { if(errors.noErrors()) {
val sortedList = varsDontCare.sortedByDescending { it.scopedName } val sortedList = varsDontCare.sortedByDescending { it.scopedName }
for (variable in sortedList) { for (variable in sortedList) {
if(variable.dt in IntegerDatatypes) { if(variable.dt in IntegerDatatypesWithBoolean) {
if(zeropage.free.isEmpty()) { if(zeropage.free.isEmpty()) {
break break
} else { } else {

View File

@ -18,12 +18,12 @@ internal class AnyExprAsmGen(
) { ) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) { when(expr.type) {
in ByteDatatypes -> { in ByteDatatypesWithBoolean -> {
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
return assignByteBinExpr(expr, assign) return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
require(expr.operator in ComparisonOperators) require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled by assignOptimizedComparisonWords()") throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
} }
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) { if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
require(expr.operator in ComparisonOperators) require(expr.operator in ComparisonOperators)
@ -35,7 +35,7 @@ internal class AnyExprAsmGen(
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
"both operands must be words" "both operands must be words"
} }
return assignWordBinExpr(expr) throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) { require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
@ -47,30 +47,6 @@ internal class AnyExprAsmGen(
} }
} }
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
when(expr.operator) {
"+" -> TODO("word + at ${expr.position}")
"-" -> TODO("word - at ${expr.position}")
"*" -> TODO("word * at ${expr.position}")
"/" -> TODO("word / at ${expr.position}")
"<<" -> TODO("word << at ${expr.position}")
">>" -> TODO("word >> at ${expr.position}")
"%" -> TODO("word % at ${expr.position}")
"and" -> TODO("word logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("word logical or (with optional shortcircuit) ${expr.position}")
"&" -> TODO("word and at ${expr.position}")
"|" -> TODO("word or at ${expr.position}")
"^", "xor" -> TODO("word xor at ${expr.position}")
"==" -> TODO("word == at ${expr.position}")
"!=" -> TODO("word != at ${expr.position}")
"<" -> TODO("word < at ${expr.position}")
"<=" -> TODO("word <= at ${expr.position}")
">" -> TODO("word > at ${expr.position}")
">=" -> TODO("word >= at ${expr.position}")
else -> return false
}
}
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) { when(expr.operator) {
"+" -> { "+" -> {
@ -89,21 +65,11 @@ internal class AnyExprAsmGen(
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
} }
"*" -> { "*" -> TODO("byte * at ${expr.position}")
TODO("byte * at ${expr.position}") "/" -> TODO("byte / at ${expr.position}")
} "<<" -> TODO("byte << at ${expr.position}")
"/" -> { ">>" -> TODO("byte >> at ${expr.position}")
TODO("byte / at ${expr.position}") "%" -> TODO("byte % at ${expr.position}")
}
"<<" -> {
TODO("byte << at ${expr.position}")
}
">>" -> {
TODO("byte >> at ${expr.position}")
}
"%" -> {
TODO("byte % at ${expr.position}")
}
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}") "and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}") "or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
"&" -> { "&" -> {
@ -130,24 +96,12 @@ internal class AnyExprAsmGen(
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
} }
"==" -> { "==" -> TODO("byte == at ${expr.position}")
TODO("byte == at ${expr.position}") "!=" -> TODO("byte != at ${expr.position}")
} "<" -> TODO("byte < at ${expr.position}")
"!=" -> { "<=" -> TODO("byte <= at ${expr.position}")
TODO("byte != at ${expr.position}") ">" -> TODO("byte > at ${expr.position}")
} ">=" -> TODO("byte >= at ${expr.position}")
"<" -> {
TODO("byte < at ${expr.position}")
}
"<=" -> {
TODO("byte <= at ${expr.position}")
}
">" -> {
TODO("byte > at ${expr.position}")
}
">=" -> {
TODO("byte >= at ${expr.position}")
}
else -> return false else -> return false
} }
} }

View File

@ -14,6 +14,7 @@ internal enum class TargetStorageKind {
} }
internal enum class SourceStorageKind { internal enum class SourceStorageKind {
LITERALBOOLEAN,
LITERALNUMBER, LITERALNUMBER,
VARIABLE, VARIABLE,
ARRAY, ARRAY,
@ -43,8 +44,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
init { init {
if(register!=null && datatype !in NumericDatatypes) if(register!=null && datatype !in NumericDatatypesWithBoolean)
throw AssemblyError("register must be integer or float type") throw AssemblyError("must be numeric type")
} }
companion object { companion object {
@ -133,6 +134,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val memory: PtMemoryByte? = null, val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null, val register: RegisterOrPair? = null,
val number: PtNumber? = null, val number: PtNumber? = null,
val boolean: PtBool? = null,
val expression: PtExpression? = null val expression: PtExpression? = null
) )
{ {
@ -147,6 +149,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val cv = value as? PtNumber val cv = value as? PtNumber
if(cv!=null) if(cv!=null)
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
val bv = value as? PtBool
if(bv!=null)
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
return when(value) { return when(value) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value") // checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@ -194,7 +199,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
// allow some signed/unsigned relaxations // allow some signed/unsigned relaxations
fun withAdjustedDt(newType: DataType) = fun withAdjustedDt(newType: DataType) =
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression) AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
if(target.datatype!=datatype) { if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) { if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {

View File

@ -5,7 +5,7 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) { override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypes -> 1 in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5 DataType.FLOAT -> 5
else -> 2 else -> 2
} }
@ -58,6 +58,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
} }
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") } infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }

View File

@ -53,7 +53,7 @@ class TestCodegen: FunSpec({
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", Position.DUMMY) val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also { val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
@ -68,7 +68,7 @@ class TestCodegen: FunSpec({
sub.add(assign) sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also { val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
@ -76,7 +76,7 @@ class TestCodegen: FunSpec({
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY) val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
@ -84,7 +84,7 @@ class TestCodegen: FunSpec({
sub.add(numberAssign) sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) cxregAssign.add(cxregAssignTarget)

View File

@ -29,7 +29,7 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
} }

View File

@ -28,9 +28,12 @@ dependencies {
implementation project(':intermediate') implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }
sourceSets { sourceSets {

View File

@ -1,10 +1,7 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.code.core.SplitWordArrayTypes
import prog8.intermediate.* import prog8.intermediate.*
@ -48,10 +45,72 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE) "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_arraycopy" -> funcArrayCopy(call)
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val source = call.args[0] as PtIdentifier
val target = call.args[1] as PtIdentifier
val sourceLength = codeGen.symbolTable.getLength(source.name)!!
val targetLength = codeGen.symbolTable.getLength(target.name)!!
require(sourceLength==targetLength)
val result = mutableListOf<IRCodeChunkBase>()
val fromReg = codeGen.registers.nextFree()
val toReg = codeGen.registers.nextFree()
val countReg = codeGen.registers.nextFree()
if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) {
// split words -> split words, copy lsb and msb arrays separately
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength)
it += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
}
else if(source.type in SplitWordArrayTypes) {
// split -> normal words
require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W)
val fromRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromRegMsb, labelSymbol = source.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to fromRegMsb, IRDataType.WORD to toReg, IRDataType.BYTE to countReg), returns = null)
}
else if(target.type in SplitWordArrayTypes) {
// normal -> split words
require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W)
val toRegMsb = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb")
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toRegMsb, labelSymbol = target.name+"_msb")
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=countReg, immediate = sourceLength)
}
result += codeGen.makeSyscall(IMSyscall.ARRAYCOPY_NORMAL_TO_SPLITW, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to toRegMsb, IRDataType.BYTE to countReg), returns = null)
}
else {
// normal array to array copy (various element types)
val eltsize = codeGen.options.compTarget.memorySize(ArrayToElementTypes.getValue(source.type))
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize)
}
result += codeGen.makeSyscall(IMSyscall.MEMCOPY_SMALL, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.BYTE to (countReg and 255)), returns = null)
}
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult { private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueTr = exprGen.translateExpression(call.args[0]) val valueTr = exprGen.translateExpression(call.args[0])
@ -185,7 +244,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val tr = exprGen.translateExpression(arrayName) val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -213,7 +272,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(arrayName) val tr = exprGen.translateExpression(arrayName)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength!! and 255), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -795,10 +854,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(false, target.position)
assignTarget.children.add(target) assignTarget.children.add(target)
assignment.children.add(assignTarget) assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position)) assignment.children.add(PtIrRegister(register, target.type, target.position))
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
result += codeGen.translateNode(assignment) result += codeGen.translateNode(assignment)
return result return result

File diff suppressed because it is too large Load Diff

View File

@ -52,15 +52,12 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions) || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions) || removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions) || cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations
} while (changed) } while (changed)
} }
} }
removeEmptyChunks(sub) removeEmptyChunks(sub)
} }
// TODO also do register optimization step here at the end?
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
@ -337,10 +334,33 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
} }
} }
// a SNZ etc. whose target register is not used can be removed altogether
if(ins.opcode in OpcodesThatSetRegFromStatusbits) {
val usages = regUsages(ins.reg1!!)
if(usages.toList().sumOf { it.second } <= 1) {
chunk.instructions.removeAt(idx)
changed = true
}
}
} }
return changed return changed
} }
private fun regUsages(register: Int): Map<IRCodeChunkBase, Int> {
val chunks = mutableMapOf<IRCodeChunkBase, Int>()
irprog.foreachSub { sub ->
sub.chunks.forEach { chunk ->
val used = chunk.usedRegisters()
val numUsages = used.readRegs.getOrDefault(register, 0) + used.writeRegs.getOrDefault(register, 0)
if(numUsages>0) {
chunks[chunk] = numUsages
}
}
}
return chunks
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false var changed = false
@ -410,21 +430,34 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun removeDoubleLoadsAndStores(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
return false
/*
var changed = false var changed = false
indexedInstructions.forEach { (idx, ins) -> indexedInstructions.forEach { (idx, ins) ->
if(ins.opcode==Opcode.STOREM) {
val prev = indexedInstructions[idx-1].value
if(prev.opcode==Opcode.LOADM) {
// loadm.X rX,something | storem.X rX,something ?? -> get rid of the store.
if(ins.labelSymbol!=null && ins.labelSymbol==prev.labelSymbol && ins.labelSymbolOffset==prev.labelSymbolOffset) {
changed=true
chunk.instructions.removeAt(idx)
}
else if(ins.address!=null && ins.address==prev.address) {
changed=true
chunk.instructions.removeAt(idx)
}
}
}
// TODO: detect multiple loads to the same target registers, only keep first (if source is not I/O memory) /*
// TODO: detect multiple stores to the same target, only keep first (if target is not I/O memory) Possible other optimizations:
// TODO: detect multiple float ffrom/fto to the same target, only keep first // detect multiple loads to the same target registers, only keep first (if source is not I/O memory)
// TODO: detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out // detect multiple stores to the same target, only keep first (if target is not I/O memory)
// TODO: detect multiple same ands, ors; only keep first // detect multiple float ffrom/fto to the same target, only keep first
// TODO: (hard) detect multiple registers being assigned the same value (and not changed) - use only 1 of them // detect subsequent same xors/nots/negs, remove the pairs completely as they cancel out
// detect multiple same ands, ors; only keep first
// detect multiple registers being assigned the same value (and not changed) - use only 1 of them (hard!)
// ... // ...
*/
} }
return changed return changed
*/
} }
} }

View File

@ -148,6 +148,7 @@ class IRUnusedCodeRemover(
val entrypointSub = irprog.blocks.single { it.label=="main" } val entrypointSub = irprog.blocks.single { it.label=="main" }
.children.single { it is IRSubroutine && it.label=="main.start" } .children.single { it is IRSubroutine && it.label=="main.start" }
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first()) val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
reachable.add(irprog.globalInits)
// all chunks referenced in array initializer values are also 'reachable': // all chunks referenced in array initializer values are also 'reachable':
irprog.st.allVariables() irprog.st.allVariables()
@ -168,7 +169,7 @@ class IRUnusedCodeRemover(
it.instructions.forEach { instr -> it.instructions.forEach { instr ->
if (instr.branchTarget == null) if (instr.branchTarget == null)
instr.labelSymbol?.let { label -> instr.labelSymbol?.let { label ->
val chunk = allLabeledChunks[label.substringBeforeLast('.')] val chunk = allLabeledChunks[label] ?: allLabeledChunks[label.substringBeforeLast('.')]
if(chunk!=null) if(chunk!=null)
new+=chunk new+=chunk
else else
@ -230,6 +231,7 @@ class IRUnusedCodeRemover(
} }
} }
linkedChunks.add(irprog.globalInits)
return removeUnlinkedChunks(linkedChunks) return removeUnlinkedChunks(linkedChunks)
} }

View File

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

View File

@ -3,7 +3,7 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) { override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypes -> 1 in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5 DataType.FLOAT -> 5
else -> 2 else -> 2
} }
@ -56,6 +56,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
} }
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") } infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }

View File

@ -50,7 +50,7 @@ class TestVmCodeGen: FunSpec({
sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY)) sub.add(PtVariable("xx", DataType.WORD, ZeropageWish.DONTCARE, PtNumber(DataType.WORD, 1.0, Position.DUMMY), null, Position.DUMMY))
val assign = PtAugmentedAssign("+=", Position.DUMMY) val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(Position.DUMMY).also { val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
@ -65,7 +65,7 @@ class TestVmCodeGen: FunSpec({
sub.add(assign) sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(Position.DUMMY).also { val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
@ -73,7 +73,7 @@ class TestVmCodeGen: FunSpec({
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY) val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(Position.DUMMY).also { val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
@ -81,7 +81,7 @@ class TestVmCodeGen: FunSpec({
sub.add(numberAssign) sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(Position.DUMMY).also { val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) cxregAssign.add(cxregAssignTarget)
@ -125,7 +125,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -133,7 +133,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@ -141,7 +141,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@ -149,7 +149,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@ -188,7 +188,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -196,7 +196,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@ -204,7 +204,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@ -212,7 +212,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@ -247,7 +247,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -255,7 +255,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@ -294,7 +294,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -302,7 +302,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@ -310,7 +310,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@ -318,7 +318,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@ -357,7 +357,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -365,7 +365,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
@ -373,7 +373,7 @@ class TestVmCodeGen: FunSpec({
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
@ -381,7 +381,7 @@ class TestVmCodeGen: FunSpec({
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY)) cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
@ -416,7 +416,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
@ -424,7 +424,7 @@ class TestVmCodeGen: FunSpec({
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY)) cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)

View File

@ -27,7 +27,7 @@ dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':compilerAst') implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
} }

View File

@ -7,6 +7,7 @@ import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes import prog8.code.core.IntegerDatatypes
import prog8.code.core.IntegerDatatypesWithBoolean
import prog8.code.core.Position import prog8.code.core.Position
import kotlin.math.* import kotlin.math.*
@ -69,15 +70,15 @@ class ConstExprEvaluator {
} }
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) { if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position) return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
} }
} else if(left.type== DataType.UWORD) { } else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
} }
} else if(left.type== DataType.LONG) { } else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position) return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
} }
@ -86,15 +87,15 @@ class ConstExprEvaluator {
} }
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) { if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position) return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
} }
} else if(left.type== DataType.UWORD) { } else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
} }
} else if(left.type== DataType.LONG) { } else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position) return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
} }
@ -103,11 +104,11 @@ class ConstExprEvaluator {
} }
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) { if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position) return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
} }
} else if(left.type== DataType.UWORD) { } else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) { if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
} }

View File

@ -36,9 +36,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
if(parent is Assignment) { if(parent is Assignment) {
val iDt = parent.target.inferType(program) val iDt = parent.target.inferType(program)
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) { if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED)) val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED), true)
if(casted.isValid) { if(casted.isValid) {
return listOf(IAstModification.ReplaceNode(numLiteral, casted.value!!, parent)) return listOf(IAstModification.ReplaceNode(numLiteral, casted.valueOrZero(), parent))
} }
} }
} }
@ -313,23 +313,23 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> { override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? { fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt) val fromCast = rangeFrom.cast(targetDt, true)
val toCast = rangeTo.cast(targetDt) val toCast = rangeTo.cast(targetDt, true)
if(!fromCast.isValid || !toCast.isValid) if(!fromCast.isValid || !toCast.isValid)
return null return null
val newStep = val newStep =
if(stepLiteral!=null) { if(stepLiteral!=null) {
val stepCast = stepLiteral.cast(targetDt) val stepCast = stepLiteral.cast(targetDt, true)
if(stepCast.isValid) if(stepCast.isValid)
stepCast.value!! stepCast.valueOrZero()
else else
range.step range.step
} else { } else {
range.step range.step
} }
return RangeExpression(fromCast.value!!, toCast.value!!, newStep, range.position) return RangeExpression(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
} }
// adjust the datatype of a range expression in for loops to the loop variable. // adjust the datatype of a range expression in for loops to the loop variable.
@ -386,9 +386,9 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val valueDt = numval.inferType(program) val valueDt = numval.inferType(program)
if(valueDt isnot decl.datatype) { if(valueDt isnot decl.datatype) {
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) { if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
val cast = numval.cast(decl.datatype) val cast = numval.cast(decl.datatype, true)
if (cast.isValid) if (cast.isValid)
return listOf(IAstModification.ReplaceNode(numval, cast.value!!, decl)) return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
} }
} }
} }
@ -435,7 +435,6 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
subrightIsConst: Boolean): IAstModification? subrightIsConst: Boolean): IAstModification?
{ {
// NOTE: THESE REORDERINGS ARE ONLY VALID FOR FLOATING POINT CONSTANTS // NOTE: THESE REORDERINGS ARE ONLY VALID FOR FLOATING POINT CONSTANTS
// TODO: this implements only a small set of possible reorderings at this time, we could perhaps add more
if(expr.operator==subExpr.operator) { if(expr.operator==subExpr.operator) {
// both operators are the same. // both operators are the same.

View File

@ -35,12 +35,12 @@ class VarConstantValueTypeAdjuster(
// avoid silent float roundings // avoid silent float roundings
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) { if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position) errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
} else { } else if(decl.datatype!=DataType.BOOL) {
// cast the numeric literal to the appropriate datatype of the variable // cast the numeric literal to the appropriate datatype of the variable if it's not boolean
declConstValue.linkParents(decl) declConstValue.linkParents(decl)
val cast = declConstValue.cast(decl.datatype) val cast = declConstValue.cast(decl.datatype, true)
if (cast.isValid) if (cast.isValid)
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.value!!, decl)) return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
} }
} }
} catch (x: UndefinedSymbolError) { } catch (x: UndefinedSymbolError) {
@ -75,10 +75,11 @@ class VarConstantValueTypeAdjuster(
val declValue = decl.value?.constValue(program) val declValue = decl.value?.constValue(program)
if (declValue != null) { if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant // variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position) errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.position)
decl.value = null
return listOf( return listOf(
IAstModification.ReplaceNode(decl, const, parent), IAstModification.ReplaceNode(decl, const, parent)
) )
} }
} }
@ -94,7 +95,7 @@ class VarConstantValueTypeAdjuster(
) )
} }
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant // variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable is never written to and was replaced by a constant", decl.position) errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position) val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.position)
return listOf( return listOf(
IAstModification.ReplaceNode(decl, const, parent), IAstModification.ReplaceNode(decl, const, parent),
@ -103,15 +104,15 @@ class VarConstantValueTypeAdjuster(
} }
} }
/* /*
TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value) TODO: need to check if there are no variable usages between the declaration and the assignment (because these rely on the original initialization value)
if(writes.size==2) { if(writes.size==2) {
val firstAssignment = writes[0].parent as? Assignment val firstAssignment = writes[0].parent as? Assignment
val secondAssignment = writes[1].parent as? Assignment val secondAssignment = writes[1].parent as? Assignment
if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) { if(firstAssignment?.origin==AssignmentOrigin.VARINIT && secondAssignment?.value?.constValue(program)!=null) {
errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position) errors.warn("variable is only assigned once here, consider using this as the initialization value in the declaration instead", secondAssignment.position)
}
} }
} */
*/
} }
return noModifications return noModifications
@ -270,7 +271,11 @@ class VarConstantValueTypeAdjuster(
// Replace all constant identifiers with their actual value, // Replace all constant identifiers with their actual value,
// and the array var initializer values and sizes. // and the array var initializer values and sizes.
// This is needed because further constant optimizations depend on those. // This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() { internal class ConstantIdentifierReplacer(
private val program: Program,
private val options: CompilationOptions,
private val errors: IErrorReporter
) : AstWalker() {
override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> { override fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
val constValue = addressOf.constValue(program) val constValue = addressOf.constValue(program)
@ -292,7 +297,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
return noModifications return noModifications
val dt = identifier.inferType(program) val dt = identifier.inferType(program)
if(!dt.isKnown || !dt.isNumeric) if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
return noModifications return noModifications
try { try {
@ -305,7 +310,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position) val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
return if(arrayIdx.parent is AssignTarget) { return if(arrayIdx.parent is AssignTarget) {
val memwrite = DirectMemoryWrite(add, identifier.position) val memwrite = DirectMemoryWrite(add, identifier.position)
val assignTarget = AssignTarget(null, null, memwrite, identifier.position) val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position)
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent)) listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
} else { } else {
val memread = DirectMemoryRead(add, identifier.position) val memread = DirectMemoryRead(add, identifier.position)
@ -313,7 +318,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
} }
} }
when (cval.type) { when (cval.type) {
in NumericDatatypes -> { in NumericDatatypesWithBoolean -> {
if(parent is AddressOf) if(parent is AddressOf)
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later. return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
return listOf( return listOf(
@ -369,7 +374,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
DataType.FLOAT -> { DataType.FLOAT -> {
// vardecl: for scalar float vars, promote constant integer initialization values to floats // vardecl: for scalar float vars, promote constant integer initialization values to floats
val litval = decl.value as? NumericLiteral val litval = decl.value as? NumericLiteral
if (litval!=null && litval.type in IntegerDatatypes) { if (litval!=null && litval.type in IntegerDatatypesWithBoolean) {
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position) val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl)) return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
} }
@ -470,12 +475,11 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
val numericLv = decl.value as? NumericLiteral val numericLv = decl.value as? NumericLiteral
val size = decl.arraysize?.constIndex() ?: return null val size = decl.arraysize?.constIndex() ?: return null
if(rangeExpr==null && numericLv!=null) { if(rangeExpr==null && numericLv!=null) {
// arraysize initializer is a single int, and we know the size. // arraysize initializer is a single int, and we know the array size.
val fillvalue = numericLv.number val fillvalue = numericLv.number
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE) if (fillvalue < options.compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > options.compTarget.machine.FLOAT_MAX_POSITIVE)
errors.err("float value overflow", numericLv.position) errors.err("float value overflow", numericLv.position)
else { else {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>() val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position) return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
} }
@ -485,9 +489,13 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
val numericLv = decl.value as? NumericLiteral val numericLv = decl.value as? NumericLiteral
val size = decl.arraysize?.constIndex() ?: return null val size = decl.arraysize?.constIndex() ?: return null
if(numericLv!=null) { if(numericLv!=null) {
// arraysize initializer is a single int, and we know the size. // arraysize initializer is a single value, and we know the array size.
val fillvalue = if(numericLv.number==0.0) 0.0 else 1.0 if(numericLv.type!=DataType.BOOL) {
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.UBYTE, fillvalue, numericLv.position) }.toTypedArray<Expression>() if(options.strictBool || numericLv.type !in ByteDatatypes)
errors.err("initializer value is not a boolean", numericLv.position)
return null
}
val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray<Expression>()
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position) return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
} }
} }

View File

@ -1,13 +1,9 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.IStatementContainer import prog8.ast.*
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.AnonymousScope import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Assignment
import prog8.ast.statements.IfElse import prog8.ast.statements.IfElse
import prog8.ast.statements.Jump import prog8.ast.statements.Jump
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
@ -17,11 +13,7 @@ import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has? class ExpressionSimplifier(private val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
class ExpressionSimplifier(private val program: Program,
private val errors: IErrorReporter,
private val compTarget: ICompilationTarget) : AstWalker() {
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet() private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet() private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
@ -31,9 +23,9 @@ class ExpressionSimplifier(private val program: Program,
// try to statically convert a literal value into one of the desired type // try to statically convert a literal value into one of the desired type
val literal = typecast.expression as? NumericLiteral val literal = typecast.expression as? NumericLiteral
if (literal != null) { if (literal != null) {
val newLiteral = literal.cast(typecast.type) val newLiteral = literal.cast(typecast.type, typecast.implicit)
if (newLiteral.isValid && newLiteral.value!! !== literal) { if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
mods += IAstModification.ReplaceNode(typecast, newLiteral.value!!, parent) mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
} }
} }
@ -114,16 +106,6 @@ class ExpressionSimplifier(private val program: Program,
if (!leftIDt.isKnown || !rightIDt.isKnown) if (!leftIDt.isKnown || !rightIDt.isKnown)
throw FatalAstException("can't determine datatype of both expression operands $expr") throw FatalAstException("can't determine datatype of both expression operands $expr")
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
return listOf(IAstModification.SwapOperands(expr))
}
// X + (-A) --> X - A // X + (-A) --> X - A
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") { if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
return listOf(IAstModification.ReplaceNode( return listOf(IAstModification.ReplaceNode(
@ -255,26 +237,101 @@ class ExpressionSimplifier(private val program: Program,
} }
} }
// boolvar & 1 --> boolvar // optimize boolean constant comparisons
// boolvar & 2 --> false if(expr.operator=="==") {
if(expr.operator=="&" && rightDt in IntegerDatatypes && (leftDt == DataType.BOOL || (expr.left as? TypecastExpression)?.expression?.inferType(program)?.istype(DataType.BOOL)==true)) { if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
if(rightVal?.number==1.0) { val rightConstBool = rightVal?.asBooleanValue
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent)) if(rightConstBool!=null) {
} else if(rightVal?.number!=null && (rightVal.number.toInt() and 1)==0) { return if(rightConstBool)
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent)) listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
else
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
}
}
if (rightVal?.number == 1.0) {
if (options.strictBool) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
} else
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
}
else if (rightVal?.number == 0.0) {
if (options.strictBool) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
}
}
if (expr.operator=="!=") {
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
val rightConstBool = rightVal?.asBooleanValue
if(rightConstBool!=null) {
return if (rightConstBool)
listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent))
else
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
}
}
if (rightVal?.number == 1.0) {
if(options.strictBool) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
}
else if (rightVal?.number == 0.0) {
if(options.strictBool) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
} else
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
} }
} }
if(leftDt==DataType.BOOL) { if(expr.operator in arrayOf("and", "or", "xor")) {
// optimize boolean constant comparisons if(leftVal!=null) {
// if(expr.operator=="==" && rightVal?.number==0.0) val result = if(leftVal.asBooleanValue) {
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent)) when(expr.operator) {
// if(expr.operator=="!=" && rightVal?.number==1.0) "and" -> expr.right
// return listOf(IAstModification.ReplaceNode(expr, PrefixExpression("not", expr.left, expr.position), parent)) "or" -> NumericLiteral.fromBoolean(true, expr.position)
if(expr.operator=="==" && rightVal?.number==1.0) "xor" -> PrefixExpression("not", expr.right, expr.position)
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent)) else -> throw FatalAstException("weird op")
if(expr.operator=="!=" && rightVal?.number==0.0) }
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent)) } else {
when(expr.operator) {
"and" -> NumericLiteral.fromBoolean(false, expr.position)
"or" -> expr.right
"xor" -> expr.right
else -> throw FatalAstException("weird op")
}
}
return listOf(IAstModification.ReplaceNode(expr, result, parent))
}
else if(rightVal!=null) {
val result = if(rightVal.asBooleanValue) {
when(expr.operator) {
"and" -> expr.left
"or" -> NumericLiteral.fromBoolean(true, expr.position)
"xor" -> PrefixExpression("not", expr.left, expr.position)
else -> throw FatalAstException("weird op")
}
} else {
when(expr.operator) {
"and" -> NumericLiteral.fromBoolean(false, expr.position)
"or" -> expr.left
"xor" -> expr.left
else -> throw FatalAstException("weird op")
}
}
return listOf(IAstModification.ReplaceNode(expr, result, parent))
}
} }
// simplify when a term is constant and directly determines the outcome // simplify when a term is constant and directly determines the outcome
@ -387,10 +444,14 @@ class ExpressionSimplifier(private val program: Program,
return noModifications return noModifications
} }
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? { private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
// NOTE: only when the terms are not function calls!!!
if(expr.left is IFunctionCall || expr.right is IFunctionCall)
return null
val rightB = expr.right as? BinaryExpression val rightB = expr.right as? BinaryExpression
if(rightB!=null) { if(rightB!=null) {
if(rightB.left is IFunctionCall || rightB.right is IFunctionCall)
return null
// absorption laws: a or (a and b) --> a, a and (a or b) --> a // absorption laws: a or (a and b) --> a, a and (a or b) --> a
if(expr.operator=="or" && rightB.operator=="and") { if(expr.operator=="or" && rightB.operator=="and") {
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) { if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
@ -402,12 +463,26 @@ class ExpressionSimplifier(private val program: Program,
return expr.left return expr.left
} }
} }
else if(expr.operator=="or" && rightB.operator=="or") {
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
// a or (a or b) -> a or b
return expr.right
}
}
else if(expr.operator=="and" && rightB.operator=="and") {
if(expr.left isSameAs rightB.left || expr.left isSameAs rightB.right) {
// a and (a and b) -> a and b
return expr.right
}
}
} }
val leftB = expr.left as? BinaryExpression val leftB = expr.left as? BinaryExpression
if(leftB!=null) { if(leftB!=null) {
if(leftB.left is IFunctionCall || leftB.right is IFunctionCall)
return null
// absorption laws: (a and b) or a --> a, (a or b) and a --> a // absorption laws: (a and b) or a --> a, (a or b) and a --> a
if(expr.operator=="or" && leftB.operator=="and") { if(expr.operator=="or" && leftB.operator=="and") {
if(expr.right isSameAs leftB.left || expr.left isSameAs leftB.right) { if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
return expr.right return expr.right
} }
} }
@ -416,6 +491,18 @@ class ExpressionSimplifier(private val program: Program,
return expr.right return expr.right
} }
} }
else if(expr.operator=="or" && leftB.operator=="or") {
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
// (a or b) or a -> a or b
return expr.left
}
}
else if(expr.operator=="and" && leftB.operator=="and") {
if(expr.right isSameAs leftB.left || expr.right isSameAs leftB.right) {
// (a and b) or a -> a and b
return expr.left
}
}
} }
return null return null
} }
@ -654,7 +741,7 @@ class ExpressionSimplifier(private val program: Program,
in powersOfTwo -> { in powersOfTwo -> {
if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) { if (leftDt==DataType.UBYTE || leftDt==DataType.UWORD) {
// Unsigned number divided by a power of two => shift right // Unsigned number divided by a power of two => shift right
// Signed number can't simply be bitshifted in this case (due to rounding issues for negative values), TODO is this correct??? // Signed number can't simply be bitshifted in this case (due to rounding issues for negative values),
// so we leave that as is and let the code generator deal with it. // so we leave that as is and let the code generator deal with it.
val numshifts = log2(cv).toInt() val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position) return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)

View File

@ -3,7 +3,6 @@ package prog8.optimizer
import prog8.ast.IBuiltinFunctions import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
@ -13,7 +12,7 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
if(errors.noErrors()) { if(errors.noErrors()) {
valuetypefixer.applyModifications() valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, errors, options.compTarget) val replacer = ConstantIdentifierReplacer(this, options, errors)
replacer.visit(this) replacer.visit(this)
if (errors.noErrors()) { if (errors.noErrors()) {
replacer.applyModifications() replacer.applyModifications()
@ -60,8 +59,8 @@ fun Program.inlineSubroutines(options: CompilationOptions): Int {
return inliner.applyModifications() return inliner.applyModifications()
} }
fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarget) : Int { fun Program.simplifyExpressions(errors: IErrorReporter, options: CompilationOptions) : Int {
val opti = ExpressionSimplifier(this, errors, target) val opti = ExpressionSimplifier(this, options, errors)
opti.visit(this) opti.visit(this)
return opti.applyModifications() return opti.applyModifications()
} }

View File

@ -87,6 +87,14 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} else } else
false false
targetInline || valueInline targetInline || valueInline
} else if(stmt.target.identifier!=null && stmt.isAugmentable) {
val binExpr = stmt.value as BinaryExpression
if(binExpr.operator in "+-" && binExpr.right.constValue(program)?.number==1.0) {
makeFullyScoped(stmt.target.identifier!!)
makeFullyScoped(binExpr.left as IdentifierReference)
true
} else
false
} else } else
false false
} }
@ -107,18 +115,6 @@ class Inliner(private val program: Program, private val options: CompilationOpti
inline inline
} }
is PostIncrDecr -> {
if (stmt.target.identifier != null) {
makeFullyScoped(stmt.target.identifier!!)
true
} else if (stmt.target.memoryAddress?.addressExpression is NumericLiteral || stmt.target.memoryAddress?.addressExpression is IdentifierReference) {
if (stmt.target.memoryAddress?.addressExpression is IdentifierReference)
makeFullyScoped(stmt.target.memoryAddress?.addressExpression as IdentifierReference)
true
} else
false
}
is Jump -> true is Jump -> true
else -> false else -> false
} }

View File

@ -5,9 +5,10 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.AssociativeOperators
import prog8.code.target.VMTarget import prog8.code.core.CompilationOptions
import kotlin.math.floor import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
class StatementOptimizer(private val program: Program, class StatementOptimizer(private val program: Program,
@ -82,7 +83,7 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part // empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) { if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = BinaryExpression(ifElse.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, ifElse.condition.position), ifElse.condition.position) val invertedCondition = invertCondition(ifElse.condition, program)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position) val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position) val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf( return listOf(
@ -102,25 +103,40 @@ class StatementOptimizer(private val program: Program,
} }
} }
// remove obvious dangling elses (else after a return) if(ifElse.elsepart.isNotEmpty()) {
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) { // remove obvious dangling elses (else after a return)
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) if(ifElse.truepart.statements.singleOrNull() is Return) {
return listOf( val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), return listOf(
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
) IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
} )
}
// switch if/else around if the else is just a jump or branch // switch if/else around if the else is just a jump or branch
if(ifElse.elsepart.isNotEmpty() && ifElse.elsepart.statements.size==1) { if(ifElse.elsepart.statements.size==1) {
val jump = ifElse.elsepart.statements[0] val jump = ifElse.elsepart.statements[0]
if(jump is Jump) { if(jump is Jump) {
val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position) val newTruePart = AnonymousScope(mutableListOf(jump), ifElse.elsepart.position)
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse)
)
}
}
// switch if/else around if the condition is a not
val prefixCond = ifElse.condition as? PrefixExpression
if(prefixCond?.operator=="not") {
errors.info("invert conditon and swap if/else blocks", ifElse.condition.position)
val newTruePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position) val newElsePart = AnonymousScope(ifElse.truepart.statements, ifElse.truepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse), IAstModification.ReplaceNode(ifElse.elsepart, newElsePart, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse), IAstModification.ReplaceNode(ifElse.truepart, newTruePart, ifElse),
IAstModification.ReplaceNode(ifElse.condition, invertCondition(ifElse.condition, program), ifElse) IAstModification.ReplaceNode(ifElse.condition, prefixCond.expression, ifElse)
) )
} }
} }
@ -146,7 +162,7 @@ class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@ -161,7 +177,7 @@ class StatementOptimizer(private val program: Program,
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0] val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position) val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@ -174,7 +190,7 @@ class StatementOptimizer(private val program: Program,
if(av!=null) { if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment( scope.statements.add(Assignment(
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position), AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
AssignmentOrigin.OPTIMIZER, forLoop.position)) AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@ -185,47 +201,12 @@ class StatementOptimizer(private val program: Program,
val loopvarDt = forLoop.loopVarDt(program) val loopvarDt = forLoop.loopVarDt(program)
if(loopvarDt.istype(DataType.UWORD) || loopvarDt.istype(DataType.UBYTE)) { if(loopvarDt.istype(DataType.UWORD) || loopvarDt.istype(DataType.UBYTE)) {
if (range != null && range.from.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==1.0) {
val toBinExpr = range.to as? BinaryExpression
if(toBinExpr!=null && toBinExpr.operator=="-" && toBinExpr.right.constValue(program)?.number==1.0) {
// FOR var IN 0 TO X-1 .... ---> var=0, DO {... , var++} UNTIL var==X
val pos = forLoop.position
val condition = BinaryExpression(forLoop.loopVar.copy(), "==", toBinExpr.left, pos)
val incOne = PostIncrDecr(AssignTarget(forLoop.loopVar.copy(), null, null, pos), "++", pos)
forLoop.body.statements.add(incOne)
val replacement = AnonymousScope(mutableListOf(
Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, pos),
NumericLiteral.optimalNumeric(0.0, pos),
AssignmentOrigin.OPTIMIZER, pos),
UntilLoop(forLoop.body, condition, pos)
), pos)
return listOf(IAstModification.ReplaceNode(forLoop, replacement, parent))
}
if(options.compTarget.name!=VMTarget.NAME) { fun incOrDec(inc: Boolean): Assignment {
// this optimization is not effective for the VM target. val pos = forLoop.position
val toConst = range.to.constValue(program) val loopVar = forLoop.loopVar
if (toConst == null) { val addSubOne = BinaryExpression(loopVar.copy(), if(inc) "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false)
// FOR var in 0 TO X ... ---> var=0, REPEAT { ... , IF var==X break , var++ } return Assignment(AssignTarget(loopVar.copy(), null, null, null, false, pos), addSubOne, AssignmentOrigin.USERCODE, pos)
val pos = forLoop.position
val incOne = PostIncrDecr(AssignTarget(forLoop.loopVar.copy(), null, null, pos), "++", pos)
val breakCondition = IfElse(
BinaryExpression(forLoop.loopVar, "==", range.to, pos),
AnonymousScope(mutableListOf(Break(pos)), pos),
AnonymousScope(mutableListOf(), pos),
pos
)
forLoop.body.statements.add(breakCondition)
forLoop.body.statements.add(incOne)
val replacement = AnonymousScope(mutableListOf(
Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, pos),
NumericLiteral.optimalNumeric(0.0, pos),
AssignmentOrigin.OPTIMIZER, pos),
RepeatLoop(null, forLoop.body, pos)
), pos)
return listOf(IAstModification.ReplaceNode(forLoop, replacement, parent))
}
}
} }
if (range != null && range.to.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==-1.0) { if (range != null && range.to.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==-1.0) {
@ -235,10 +216,10 @@ class StatementOptimizer(private val program: Program,
val pos = forLoop.position val pos = forLoop.position
val checkValue = NumericLiteral(loopvarDt.getOr(DataType.UNDEFINED), if(loopvarDt.istype(DataType.UBYTE)) 255.0 else 65535.0, pos) val checkValue = NumericLiteral(loopvarDt.getOr(DataType.UNDEFINED), if(loopvarDt.istype(DataType.UBYTE)) 255.0 else 65535.0, pos)
val condition = BinaryExpression(forLoop.loopVar.copy(), "==", checkValue, pos) val condition = BinaryExpression(forLoop.loopVar.copy(), "==", checkValue, pos)
val decOne = PostIncrDecr(AssignTarget(forLoop.loopVar.copy(), null, null, pos), "--", pos) val decOne = incOrDec(false)
forLoop.body.statements.add(decOne) forLoop.body.statements.add(decOne)
val replacement = AnonymousScope(mutableListOf( val replacement = AnonymousScope(mutableListOf(
Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, pos), Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, null, false, pos),
fromExpr, AssignmentOrigin.OPTIMIZER, pos), fromExpr, AssignmentOrigin.OPTIMIZER, pos),
UntilLoop(forLoop.body, condition, pos) UntilLoop(forLoop.body, condition, pos)
), pos) ), pos)
@ -390,35 +371,15 @@ class StatementOptimizer(private val program: Program,
// assignments of the form: X = X <operator> <expr> // assignments of the form: X = X <operator> <expr>
// remove assignments that have no effect (such as X=X+0) // remove assignments that have no effect (such as X=X+0)
// optimize/rewrite some other expressions // optimize/rewrite some other expressions
val targetDt = targetIDt.getOr(DataType.UNDEFINED)
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program))?.type
when (bexpr.operator) { when (bexpr.operator) {
"+" -> { "+" -> {
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
val incs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) {
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
}
return listOf(IAstModification.ReplaceNode(assignment, if(incs.statements.size==1) incs.statements[0] else incs, parent))
}
} }
} }
"-" -> { "-" -> {
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
val decs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) {
decs.statements.add(PostIncrDecr(assignment.target.copy(), "--", assignment.position))
}
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
}
} }
} }
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) "*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
@ -453,6 +414,33 @@ class StatementOptimizer(private val program: Program,
} }
} }
// xx+=2 -> xx++ xx++
// note: ideally this optimization should be done by the code generator, but doing it there
// requires doing it multiple times (because lots of different things can be incremented/decremented)
if(assignment.target.identifier!=null
|| assignment.target.arrayindexed?.isSimple==true
|| assignment.target.memoryAddress?.addressExpression?.isSimple==true) {
if(assignment.value.inferType(program).isBytes && assignment.isAugmentable) {
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null) {
if(binExpr.operator in "+-") {
val value = binExpr.right.constValue(program)?.number?.toInt()
if(value!=null && value==2) {
val stmts = mutableListOf<Statement>()
repeat(value) {
val incrdecr = Assignment(assignment.target.copy(),
BinaryExpression(assignment.target.toExpression(), binExpr.operator, NumericLiteral.optimalInteger(1, assignment.position), assignment.position),
AssignmentOrigin.OPTIMIZER, assignment.position)
stmts.add(incrdecr)
}
val incrdecrs = AnonymousScope(stmts, assignment.position)
return listOf(IAstModification.ReplaceNode(assignment, incrdecrs, parent))
}
}
}
}
}
return noModifications return noModifications
} }
@ -466,44 +454,6 @@ class StatementOptimizer(private val program: Program,
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> { override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
errors.info("for boolean condition a normal if statement is preferred", whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
}
if(whenStmt.condition.inferType(program).isBool) {
if(whenStmt.choices.all { it.values?.size==1 }) {
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
// it's a when statement on booleans that can just be replaced by an if or if-else.
if (whenStmt.choices.size == 1) {
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
} else {
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
}
} else if (whenStmt.choices.size == 2) {
var trueBlock: AnonymousScope? = null
var elseBlock: AnonymousScope? = null
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[0].statements
} else {
elseBlock = whenStmt.choices[0].statements
}
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[1].statements
} else {
elseBlock = whenStmt.choices[1].statements
}
if(trueBlock!=null && elseBlock!=null) {
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
}
}
}
}
}
val constantValue = whenStmt.condition.constValue(program)?.number val constantValue = whenStmt.condition.constValue(program)?.number
if(constantValue!=null) { if(constantValue!=null) {
// when condition is a constant // when condition is a constant

View File

@ -20,7 +20,12 @@ class UnusedCodeRemover(private val program: Program,
private val compTarget: ICompilationTarget private val compTarget: ICompilationTarget
): AstWalker() { ): AstWalker() {
private val callgraph = CallGraph(program) private lateinit var callgraph: CallGraph
override fun before(program: Program): Iterable<IAstModification> {
callgraph = CallGraph(program)
return noModifications
}
override fun before(module: Module, parent: Node): Iterable<IAstModification> { override fun before(module: Module, parent: Node): Iterable<IAstModification> {
return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module))) return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module)))
@ -64,14 +69,14 @@ class UnusedCodeRemover(private val program: Program,
override fun after(block: Block, parent: Node): Iterable<IAstModification> { override fun after(block: Block, parent: Node): Iterable<IAstModification> {
if("force_output" !in block.options()) { if("force_output" !in block.options()) {
if (block.containsNoCodeNorVars) { if (block.containsNoCodeNorVars) {
if(block.name != internedStringsModuleName) { if(block.name != internedStringsModuleName && "ignore_unused" !in block.options()) {
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position) errors.info("removing unused block '${block.name}'", block.position)
} }
return listOf(IAstModification.Remove(block, parent as IStatementContainer)) return listOf(IAstModification.Remove(block, parent as IStatementContainer))
} }
if(callgraph.unused(block)) { if(callgraph.unused(block)) {
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR}) { if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR} && "ignore_unused" !in block.options()) {
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position) errors.info("removing unused block '${block.name}'", block.position)
} }

View File

@ -2,8 +2,7 @@ plugins {
id 'java' id 'java'
id 'application' id 'application'
id 'org.jetbrains.kotlin.jvm' id 'org.jetbrains.kotlin.jvm'
id 'com.github.johnrengelman.shadow' version '7.1.2' id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'io.kotest' version '0.3.9'
id 'com.peterabeles.gversion' version '1.10.2' id 'com.peterabeles.gversion' version '1.10.2'
} }
@ -37,10 +36,14 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.20"
testImplementation project(':codeCore')
testImplementation project(':intermediate') testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.8.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }
configurations.all { configurations.all {
@ -102,7 +105,7 @@ test {
gversion { gversion {
srcDir = "src/" // path is relative to the sub-project by default srcDir = "src/" // path is relative to the sub-project by default
classPackage = "prog8.buildversion" classPackage = "prog8.buildversion"
className = "BuildVersion" className = "Version"
language = "kotlin" language = "kotlin"
} }

View File

@ -99,20 +99,14 @@ sys {
rts ; nothing to copy rts ; nothing to copy
_copyshort _copyshort
; decrease source and target pointers so we can simply index by Y dey
lda P8ZP_SCRATCH_W1 beq +
bne +
dec P8ZP_SCRATCH_W1+1
+ dec P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W2
bne +
dec P8ZP_SCRATCH_W2+1
+ dec P8ZP_SCRATCH_W2
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y sta (P8ZP_SCRATCH_W2),y
dey dey
bne - bne -
+ lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
rts rts
_longcopy _longcopy
@ -262,6 +256,22 @@ save_SCRATCH_ZPWORD2 .word 0
}} }}
} }
asmsub exit2(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) {
; -- immediately exit the program with result values in the A, X and Y registers.
; TODO where to store A,X,Y as exit code?
%asm {{
jmp exit
}}
}
asmsub exit3(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) {
; -- immediately exit the program with result values in the A, X and Y registers, and the Carry flag in the status register.
; TODO where to store A,X,Y,Carry as exit code?
%asm {{
jmp exit
}}
}
inline asmsub progend() -> uword @AY { inline asmsub progend() -> uword @AY {
%asm {{ %asm {{
lda #<prog8_program_end lda #<prog8_program_end

View File

@ -4,7 +4,6 @@
%import syslib %import syslib
%import conv %import conv
txt { txt {
%option no_symbol_prefixing, ignore_unused %option no_symbol_prefixing, ignore_unused
@ -14,15 +13,23 @@ const ubyte DEFAULT_HEIGHT = 24
sub clear_screen() { sub clear_screen() {
txt.chrout(125) chrout(125)
}
sub cls() {
chrout(125)
} }
sub nl() { sub nl() {
txt.chrout('\n') chrout('\n')
} }
sub spc() { sub spc() {
txt.chrout(' ') chrout(' ')
}
sub bell() {
chrout($fd)
} }
sub column(ubyte col) { sub column(ubyte col) {
@ -144,15 +151,15 @@ _tmp_outchar
} }
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print zero terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to CHROUT of that single char. ; by just one call to CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_W2+1
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_W2),y
beq + beq +
jsr chrout jsr chrout
iny iny
@ -164,13 +171,13 @@ asmsub print (str text @ AY) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) { asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total) ; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{ %asm {{
jsr conv.ubyte2decimal jsr conv.internal_ubyte2decimal
pha pha
tya tya
jsr chrout jsr chrout
pla
jsr chrout
txa txa
jsr chrout
pla
jmp chrout jmp chrout
}} }}
} }
@ -178,21 +185,21 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) { asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s ; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{ %asm {{
jsr conv.ubyte2decimal jsr conv.internal_ubyte2decimal
_print_byte_digits _print_byte_digits
pha pha
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr chrout jsr chrout
pla txa
jsr chrout jsr chrout
jmp _ones jmp _ones
+ pla + cpx #'0'
cmp #'0'
beq _ones beq _ones
txa
jsr chrout jsr chrout
_ones txa _ones pla
jmp chrout jmp chrout
}} }}
} }
@ -206,7 +213,7 @@ asmsub print_b (byte value @ A) clobbers(A,X,Y) {
lda #'-' lda #'-'
jsr chrout jsr chrout
+ pla + pla
jsr conv.byte2decimal jsr conv.internal_byte2decimal
jmp print_ub._print_byte_digits jmp print_ub._print_byte_digits
}} }}
} }
@ -219,7 +226,7 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
lda #'$' lda #'$'
jsr chrout jsr chrout
pla pla
+ jsr conv.ubyte2hex + jsr conv.internal_ubyte2hex
jsr chrout jsr chrout
tya tya
jmp chrout jmp chrout
@ -273,9 +280,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) { asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total) ; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{ %asm {{
jsr conv.uword2decimal jsr conv.internal_uword2decimal
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.internal_uword2decimal.decTenThousands,y
beq + beq +
jsr chrout jsr chrout
iny iny
@ -287,9 +294,9 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) { asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s ; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{ %asm {{
jsr conv.uword2decimal jsr conv.internal_uword2decimal
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.internal_uword2decimal.decTenThousands,y
beq _allzero beq _allzero
cmp #'0' cmp #'0'
bne _gotdigit bne _gotdigit
@ -299,7 +306,7 @@ asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
_gotdigit _gotdigit
jsr chrout jsr chrout
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.internal_uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero

View File

@ -93,27 +93,33 @@ romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high) romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) also see STOP2
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character also see GETIN2
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; get current window dimensions into X (columns) and Y (rows) NOTE: changed behavior compared to VIC/C64/PET SCREEN() routine!
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X. romsub $FFED = SCRORG() -> ubyte @ X, ubyte @ Y ; get current window dimensions into X (columns) and Y (rows) NOTE: changed behavior compared to VIC/C64/PET SCREEN() routine!
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- end of C64 compatible ROM kernal routines ---- ; ---- end of C64 compatible ROM kernal routines ----
; ---- utilities ----- ; ---- utilities -----
asmsub STOP2() clobbers(X) -> ubyte @A { inline asmsub STOP2() clobbers(X,A) -> bool @Pz {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag. ; -- just like STOP, but omits the special keys result value in A.
; just for convenience because most of the times you're only interested in the stop pressed or not status.
%asm {{ %asm {{
jsr cbm.STOP jsr cbm.STOP
beq + }}
lda #0 }
rts
+ lda #1 inline asmsub GETIN2() clobbers(X,Y) -> ubyte @A {
rts ; -- just like GETIN, but omits the carry flag result value.
; just for convenience because GETIN is so often used to just read keyboard input,
; where you don't have to deal with a potential error status
%asm {{
jsr cbm.GETIN
}} }}
} }
@ -138,6 +144,16 @@ sub CLEARST() {
CLOSE(15) CLOSE(15)
} }
asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer
%asm {{
- jsr GETIN
cmp #0
bne -
rts
}}
}
} }
c64 { c64 {
@ -377,8 +393,15 @@ asmsub cleanup_at_exit() {
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch jsr enable_runstop_and_charsetswitch
_exitcodeCarry = *+1
lda #0
lsr a
_exitcode = *+1 _exitcode = *+1
lda #0 ; exit code possibly modified in exit() lda #0 ; exit code possibly modified in exit()
_exitcodeX = *+1
ldx #0
_exitcodeY = *+1
ldy #0
rts rts
}} }}
} }
@ -641,20 +664,14 @@ _loop lda P8ZP_SCRATCH_W1
rts ; nothing to copy rts ; nothing to copy
_copyshort _copyshort
; decrease source and target pointers so we can simply index by Y dey
lda P8ZP_SCRATCH_W1 beq +
bne +
dec P8ZP_SCRATCH_W1+1
+ dec P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W2
bne +
dec P8ZP_SCRATCH_W2+1
+ dec P8ZP_SCRATCH_W2
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y sta (P8ZP_SCRATCH_W2),y
dey dey
bne - bne -
+ lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
rts rts
_longcopy _longcopy
@ -770,6 +787,33 @@ _longcopy
}} }}
} }
asmsub exit2(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) {
; -- immediately exit the program with result values in the A, X and Y registers.
%asm {{
sta cleanup_at_exit._exitcode
stx cleanup_at_exit._exitcodeX
sty cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp cleanup_at_exit
}}
}
asmsub exit3(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) {
; -- immediately exit the program with result values in the A, X and Y registers, and the Carry flag in the status register.
%asm {{
sta cleanup_at_exit._exitcode
lda #0
rol a
sta cleanup_at_exit._exitcodeCarry
stx cleanup_at_exit._exitcodeX
sty cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp cleanup_at_exit
}}
}
inline asmsub progend() -> uword @AY { inline asmsub progend() -> uword @AY {
%asm {{ %asm {{
lda #<prog8_program_end lda #<prog8_program_end

View File

@ -4,6 +4,7 @@
%import syslib %import syslib
%import conv %import conv
%import shared_cbm_textio_functions
txt { txt {
@ -12,28 +13,40 @@ txt {
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() { sub clear_screen() {
txt.chrout(147) chrout(147)
}
sub cls() {
chrout(147)
} }
sub home() { sub home() {
txt.chrout(19) chrout(19)
} }
sub nl() { sub nl() {
txt.chrout('\n') chrout('\n')
} }
sub spc() { sub spc() {
txt.chrout(' ') chrout(' ')
}
sub bell() {
chrout(7)
} }
asmsub column(ubyte col @A) clobbers(A, X, Y) { asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
pha
sec sec
jsr cbm.PLOT jsr cbm.PLOT
pla
tay tay
clc clc
jmp cbm.PLOT jmp cbm.PLOT
@ -51,8 +64,10 @@ asmsub get_column() -> ubyte @Y {
asmsub row(ubyte rownum @A) clobbers(A, X, Y) { asmsub row(ubyte rownum @A) clobbers(A, X, Y) {
; ---- set the cursor on the given row (starting with 0) on the current line ; ---- set the cursor on the given row (starting with 0) on the current line
%asm {{ %asm {{
pha
sec sec
jsr cbm.PLOT jsr cbm.PLOT
pla
tax tax
clc clc
jmp cbm.PLOT jmp cbm.PLOT
@ -279,216 +294,6 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string, in PETSCII encoding, from A/Y
; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
; by just one call to cbm.CHROUT of that single char.
%asm {{
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr cbm.CHROUT
iny
bne -
+ rts
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
jsr conv.ubyte2decimal
pha
tya
jsr cbm.CHROUT
pla
jsr cbm.CHROUT
txa
jmp cbm.CHROUT
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
jsr conv.ubyte2decimal
_print_byte_digits
pha
cpy #'0'
beq +
tya
jsr cbm.CHROUT
pla
jsr cbm.CHROUT
jmp _ones
+ pla
cmp #'0'
beq _ones
jsr cbm.CHROUT
_ones txa
jmp cbm.CHROUT
}}
}
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
pha
cmp #0
bpl +
lda #'-'
jsr cbm.CHROUT
+ pla
jsr conv.byte2decimal
jmp print_ub._print_byte_digits
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
bcc +
pha
lda #'$'
jsr cbm.CHROUT
pla
+ jsr conv.ubyte2hex
jsr cbm.CHROUT
tya
jmp cbm.CHROUT
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
jsr cbm.CHROUT
+ ldy #8
- lda #'0'
asl P8ZP_SCRATCH_B1
bcc +
lda #'1'
+ jsr cbm.CHROUT
dey
bne -
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
tya
jsr print_ubbin
pla
clc
jmp print_ubbin
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
pha
tya
jsr print_ubhex
pla
clc
jmp print_ubhex
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq +
jsr cbm.CHROUT
iny
bne -
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit
jsr cbm.CHROUT
iny
lda conv.uword2decimal.decTenThousands,y
bne _gotdigit
rts
_allzero
lda #'0'
jmp cbm.CHROUT
}}
}
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
bpl +
pha
lda #'-'
jsr cbm.CHROUT
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jmp print_uw
}}
}
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
; Returns length in Y. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0
- jsr cbm.CHRIN
cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
iny
bne -
+ lda #0
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
rts
}}
}
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) { asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
; ---- sets the character in the screen matrix at the given position ; ---- sets the character in the screen matrix at the given position
%asm {{ %asm {{

View File

@ -98,6 +98,21 @@ cast_as_w_into_ay .proc ; also used for float 2 b
jmp cast_FAC1_as_w_into_ay jmp cast_FAC1_as_w_into_ay
.pend .pend
cast_as_bool_into_a .proc
; -- cast float at A/Y to bool into A
; clobbers X
jsr MOVFM
jmp cast_FAC1_as_bool_into_a
.pend
cast_FAC1_as_bool_into_a .proc
; -- cast fac1 to bool into A
; clobbers X
jsr SIGN
and #1
rts
.pend
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast fac1 to uword into Y/A ; -- cast fac1 to uword into Y/A
; clobbers X ; clobbers X
@ -221,7 +236,7 @@ var_fac1_greatereq_f .proc
.pend .pend
var_fac1_equal_f .proc var_fac1_equal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X. ; -- are the floats numbers in FAC1 and the variable AY identical? Result in A. Clobbers X.
jsr FCOMP jsr FCOMP
and #1 and #1
eor #1 eor #1

View File

@ -1,14 +1,10 @@
; Prog8 definitions for floating point handling on the Commodore-64 ; Prog8 definitions for floating point handling on the Commodore-64
%option enable_floats, no_symbol_prefixing, ignore_unused %option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions %import shared_floats_functions
floats { floats {
; ---- this block contains C-64 floating point related functions ---- ; ---- this block contains C-64 floating point related functions ----
const float π = 3.141592653589793
const float PI = π
const float TWOPI = 2*π
; ---- C64 basic and kernal ROM float constants and functions ---- ; ---- C64 basic and kernal ROM float constants and functions ----
@ -24,7 +20,7 @@ romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value f
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2 romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
romsub $ba90 = FAREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac2 romsub $ba90 = FAREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac2
romsub $bbfc = MOVFA() clobbers(A,X) ; copy fac2 to fac1 romsub $bbfc = MOVFA() clobbers(A,X) ; copy fac2 to fac1
romsub $bc0c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded) romsub $bc0c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded the least significant bit)
romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2 romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
@ -49,7 +45,7 @@ romsub $b7b5 = FREADSTR(ubyte length @ A) clobbers(A,X,Y) ; str -> fac1, $22/2
romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well) romsub $aabc = FPRINTLN() clobbers(A,X,Y) ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100) romsub $bddd = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY ($0100)
romsub $b849 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT romsub $b849 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for integer rounding- call this before INT
romsub $bae2 = MUL10() clobbers(A,X,Y) ; fac1 *= 10 romsub $bae2 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
romsub $bafe = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive! You have to fix sign manually! romsub $bafe = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive! You have to fix sign manually!
romsub $bc5b = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than romsub $bc5b = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
@ -67,7 +63,7 @@ romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mfl
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1) romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to integer round instead of trunc
romsub $b9ea = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log) romsub $b9ea = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
romsub $bc39 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1) romsub $bc39 = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
romsub $bc2b = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive romsub $bc2b = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive

View File

@ -73,7 +73,7 @@ graphics {
} }
word @zp d = 0 word @zp d = 0
ubyte positive_ix = true bool positive_ix = true
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
positive_ix = false positive_ix = false
@ -173,7 +173,7 @@ graphics {
ubyte separate_pixels = lsb(xx) & 7 ubyte separate_pixels = lsb(xx) & 7
uword pixaddr = get_y_lookup(yy) + (xx&$fff8) uword pixaddr = get_y_lookup(yy) + (xx&$fff8)
if separate_pixels { if separate_pixels!=0 {
%asm {{ %asm {{
lda p8v_pixaddr lda p8v_pixaddr
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -191,7 +191,7 @@ graphics {
length -= 8 length -= 8
} }
if length { if length!=0 {
%asm {{ %asm {{
lda p8v_length lda p8v_length
and #7 and #7

View File

@ -94,23 +94,29 @@ romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high) romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) also see STOP2
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character also see GETIN2
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; get size of text screen into X (columns) and Y (rows)
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X. romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) clobbers(A) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
asmsub STOP2() clobbers(X) -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag. inline asmsub STOP2() clobbers(X,A) -> bool @Pz {
; -- just like STOP, but omits the special keys result value in A.
; just for convenience because most of the times you're only interested in the stop pressed or not status.
%asm {{ %asm {{
jsr cbm.STOP jsr cbm.STOP
beq + }}
lda #0 }
rts
+ lda #1 inline asmsub GETIN2() clobbers(X,Y) -> ubyte @A {
rts ; -- just like GETIN, but omits the carry flag result value.
; just for convenience because GETIN is so often used to just read keyboard input,
; where you don't have to deal with a potential error status
%asm {{
jsr cbm.GETIN
}} }}
} }
@ -135,6 +141,16 @@ sub CLEARST() {
CLOSE(15) CLOSE(15)
} }
asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer
%asm {{
- jsr GETIN
cmp #0
bne -
rts
}}
}
} }
c64 { c64 {
@ -376,8 +392,15 @@ asmsub cleanup_at_exit() {
sta $01 ; bank the kernal in sta $01 ; bank the kernal in
jsr cbm.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch jsr enable_runstop_and_charsetswitch
_exitcodeCarry = *+1
lda #0
lsr a
_exitcode = *+1 _exitcode = *+1
lda #0 ; exit code possibly modified in exit() lda #0 ; exit code possibly modified in exit()
_exitcodeX = *+1
ldx #0
_exitcodeY = *+1
ldy #0
rts rts
}} }}
} }
@ -639,20 +662,14 @@ _loop lda P8ZP_SCRATCH_W1
rts ; nothing to copy rts ; nothing to copy
_copyshort _copyshort
; decrease source and target pointers so we can simply index by Y dey
lda P8ZP_SCRATCH_W1 beq +
bne +
dec P8ZP_SCRATCH_W1+1
+ dec P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W2
bne +
dec P8ZP_SCRATCH_W2+1
+ dec P8ZP_SCRATCH_W2
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y sta (P8ZP_SCRATCH_W2),y
dey dey
bne - bne -
+ lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
rts rts
_longcopy _longcopy
@ -768,6 +785,33 @@ _longcopy
}} }}
} }
asmsub exit2(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y) {
; -- immediately exit the program with result values in the A, X and Y registers.
%asm {{
sta cleanup_at_exit._exitcode
stx cleanup_at_exit._exitcodeX
sty cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp cleanup_at_exit
}}
}
asmsub exit3(ubyte resulta @A, ubyte resultx @X, ubyte resulty @Y, bool carry @Pc) {
; -- immediately exit the program with result values in the A, X and Y registers, and the Carry flag in the status register.
%asm {{
sta cleanup_at_exit._exitcode
lda #0
rol a
sta cleanup_at_exit._exitcodeCarry
stx cleanup_at_exit._exitcodeX
sty cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer
txs
jmp cleanup_at_exit
}}
}
inline asmsub progend() -> uword @AY { inline asmsub progend() -> uword @AY {
%asm {{ %asm {{
lda #<prog8_program_end lda #<prog8_program_end

View File

@ -4,7 +4,7 @@
%import syslib %import syslib
%import conv %import conv
%import shared_cbm_textio_functions
txt { txt {
@ -13,28 +13,45 @@ txt {
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() { sub clear_screen() {
txt.chrout(147) chrout(147)
}
sub cls() {
chrout(147)
} }
sub home() { sub home() {
txt.chrout(19) chrout(19)
} }
sub nl() { sub nl() {
txt.chrout('\n') chrout('\n')
} }
sub spc() { sub spc() {
txt.chrout(' ') chrout(' ')
}
sub bell() {
; beep
c64.MVOL = 11
c64.AD1 = %00110111
c64.SR1 = %00000000
c64.FREQ1 = 8500
c64.CR1 = %00010000
c64.CR1 = %00010001
} }
asmsub column(ubyte col @A) clobbers(A, X, Y) { asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
pha
sec sec
jsr cbm.PLOT jsr cbm.PLOT
pla
tay tay
clc clc
jmp cbm.PLOT jmp cbm.PLOT
@ -52,8 +69,10 @@ asmsub get_column() -> ubyte @Y {
asmsub row(ubyte rownum @A) clobbers(A, X, Y) { asmsub row(ubyte rownum @A) clobbers(A, X, Y) {
; ---- set the cursor on the given row (starting with 0) on the current line ; ---- set the cursor on the given row (starting with 0) on the current line
%asm {{ %asm {{
pha
sec sec
jsr cbm.PLOT jsr cbm.PLOT
pla
tax tax
clc clc
jmp cbm.PLOT jmp cbm.PLOT
@ -278,216 +297,6 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string, in PESCII encoding, from A/Y
; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
; by just one call to cbm.CHROUT of that single char.
%asm {{
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy #0
- lda (P8ZP_SCRATCH_B1),y
beq +
jsr cbm.CHROUT
iny
bne -
+ rts
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
jsr conv.ubyte2decimal
pha
tya
jsr cbm.CHROUT
pla
jsr cbm.CHROUT
txa
jmp cbm.CHROUT
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
jsr conv.ubyte2decimal
_print_byte_digits
pha
cpy #'0'
beq +
tya
jsr cbm.CHROUT
pla
jsr cbm.CHROUT
jmp _ones
+ pla
cmp #'0'
beq _ones
jsr cbm.CHROUT
_ones txa
jmp cbm.CHROUT
}}
}
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
pha
cmp #0
bpl +
lda #'-'
jsr cbm.CHROUT
+ pla
jsr conv.byte2decimal
jmp print_ub._print_byte_digits
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
bcc +
pha
lda #'$'
jsr cbm.CHROUT
pla
+ jsr conv.ubyte2hex
jsr cbm.CHROUT
tya
jmp cbm.CHROUT
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
jsr cbm.CHROUT
+ ldy #8
- lda #'0'
asl P8ZP_SCRATCH_B1
bcc +
lda #'1'
+ jsr cbm.CHROUT
dey
bne -
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
tya
jsr print_ubbin
pla
clc
jmp print_ubbin
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
pha
tya
jsr print_ubhex
pla
clc
jmp print_ubhex
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq +
jsr cbm.CHROUT
iny
bne -
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit
jsr cbm.CHROUT
iny
lda conv.uword2decimal.decTenThousands,y
bne _gotdigit
rts
_allzero
lda #'0'
jmp cbm.CHROUT
}}
}
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
bpl +
pha
lda #'-'
jsr cbm.CHROUT
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jmp print_uw
}}
}
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
; Returns length in Y. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0
- jsr cbm.CHRIN
cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
iny
bne -
+ lda #0
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
rts
}}
}
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) { asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
; ---- sets the character in the screen matrix at the given position ; ---- sets the character in the screen matrix at the given position
%asm {{ %asm {{

View File

@ -8,81 +8,90 @@ conv {
str @shared string_out = "????????????????" ; result buffer for the string conversion routines str @shared string_out = "????????????????" ; result buffer for the string conversion routines
asmsub str_ub0 (ubyte value @ A) clobbers(A,X,Y) { asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total) ; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
%asm {{ %asm {{
jsr conv.ubyte2decimal jsr internal_ubyte2decimal
sty string_out sty conv.string_out
sta string_out+1 stx conv.string_out+1
stx string_out+2 sta conv.string_out+2
lda #0 lda #0
sta string_out+3 sta conv.string_out+3
lda #<conv.string_out
ldy #>conv.string_out
rts rts
}} }}
} }
asmsub str_ub (ubyte value @ A) clobbers(A,X,Y) { asmsub str_ub(ubyte value @A) clobbers(X) -> str @AY {
; ---- convert the ubyte in A in decimal string form, without left padding 0s ; ---- convert the ubyte in A in decimal string form, without left padding 0s
%asm {{ %asm {{
ldy #0 jsr internal_ubyte2decimal
sty P8ZP_SCRATCH_B1 cpy #'0'
jsr conv.ubyte2decimal beq +
_output_byte_digits sty conv.string_out
; hundreds? stx conv.string_out+1
cpy #'0' sta conv.string_out+2
beq + lda #0
pha sta conv.string_out+3
tya jmp _done
ldy P8ZP_SCRATCH_B1 + cpx #'0'
sta string_out,y beq +
pla stx conv.string_out
inc P8ZP_SCRATCH_B1 sta conv.string_out+1
; tens? lda #0
+ ldy P8ZP_SCRATCH_B1 sta conv.string_out+2
cmp #'0' jmp _done
beq + + sta conv.string_out
sta string_out,y lda #0
iny sta conv.string_out+1
+ ; ones. _done lda #<conv.string_out
txa ldy #>conv.string_out
sta string_out,y rts
iny }}
lda #0 }
sta string_out,y
rts
}}
}
asmsub str_b (byte value @ A) clobbers(A,X,Y) { asmsub str_b(byte value @A) clobbers(X) -> str @AY {
; ---- convert the byte in A in decimal string form, without left padding 0s ; ---- convert the byte in A in decimal string form, without left padding 0s
%asm {{ %asm {{
ldy #0
sty P8ZP_SCRATCH_B1
cmp #0 cmp #0
bpl + bpl str_ub
pha eor #255
clc
adc #1
jsr str_ub
; insert a minus sign at the start
lda #0
sta conv.string_out+4
lda conv.string_out+2
sta conv.string_out+3
lda conv.string_out+1
sta conv.string_out+2
lda conv.string_out
sta conv.string_out+1
lda #'-' lda #'-'
sta string_out sta conv.string_out
inc P8ZP_SCRATCH_B1 lda #<conv.string_out
pla ldy #>conv.string_out
+ jsr conv.byte2decimal rts
bra str_ub._output_byte_digits }}
}} }
}
asmsub str_ubhex (ubyte value @ A) clobbers(A,X,Y) { asmsub str_ubhex (ubyte value @ A) clobbers(X) -> str @AY {
; ---- convert the ubyte in A in hex string form ; ---- convert the ubyte in A in hex string form
%asm {{ %asm {{
jsr conv.ubyte2hex jsr internal_ubyte2hex
sta string_out sta string_out
sty string_out+1 sty string_out+1
lda #0 lda #0
sta string_out+2 sta string_out+2
rts lda #<string_out
ldy #>string_out
rts
}} }}
} }
asmsub str_ubbin (ubyte value @ A) clobbers(A,X,Y) { asmsub str_ubbin (ubyte value @ A) clobbers(X) -> str @AY {
; ---- convert the ubyte in A in binary string form ; ---- convert the ubyte in A in binary string form
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -90,18 +99,20 @@ asmsub str_ubbin (ubyte value @ A) clobbers(A,X,Y) {
sty string_out+8 sty string_out+8
ldy #7 ldy #7
- lsr P8ZP_SCRATCH_B1 - lsr P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
bne _digit bne _digit
+ lda #'0' + lda #'0'
_digit sta string_out,y _digit sta string_out,y
dey dey
bpl - bpl -
lda #<string_out
ldy #>string_out
rts rts
}} }}
} }
asmsub str_uwbin (uword value @ AY) clobbers(A,X,Y) { asmsub str_uwbin (uword value @ AY) clobbers(X) -> str @AY {
; ---- convert the uword in A/Y in binary string form ; ---- convert the uword in A/Y in binary string form
%asm {{ %asm {{
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
@ -111,79 +122,88 @@ asmsub str_uwbin (uword value @ AY) clobbers(A,X,Y) {
sty string_out+16 sty string_out+16
ldy #7 ldy #7
- lsr P8ZP_SCRATCH_REG - lsr P8ZP_SCRATCH_REG
bcc + bcc +
lda #'1' lda #'1'
bne _digit bne _digit
+ lda #'0' + lda #'0'
_digit sta string_out+8,y _digit sta string_out+8,y
dey dey
bpl - bpl -
lda #<string_out
ldy #>string_out
rts rts
}} }}
} }
asmsub str_uwhex (uword value @ AY) clobbers(A,Y) { asmsub str_uwhex (uword value @ AY) -> str @AY {
; ---- convert the uword in A/Y in hexadecimal string form (4 digits) ; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
%asm {{ %asm {{
pha pha
tya tya
jsr conv.ubyte2hex jsr internal_ubyte2hex
sta string_out sta string_out
sty string_out+1 sty string_out+1
pla pla
jsr conv.ubyte2hex jsr internal_ubyte2hex
sta string_out+2 sta string_out+2
sty string_out+3 sty string_out+3
lda #0 lda #0
sta string_out+4 sta string_out+4
rts lda #<string_out
ldy #>string_out
rts
}} }}
} }
asmsub str_uw0 (uword value @ AY) clobbers(A,X,Y) { asmsub str_uw0 (uword value @ AY) clobbers(X) -> str @AY {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total) ; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
%asm {{ %asm {{
jsr conv.uword2decimal jsr conv.internal_uword2decimal
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.internal_uword2decimal.decTenThousands,y
sta string_out,y sta string_out,y
beq + beq +
iny iny
bne - bne -
+ rts +
lda #<string_out
ldy #>string_out
rts
}} }}
} }
asmsub str_uw (uword value @ AY) clobbers(A,X,Y) { asmsub str_uw (uword value @ AY) clobbers(X) -> str @AY {
; ---- convert the uword in A/Y in decimal string form, without left padding 0s ; ---- convert the uword in A/Y in decimal string form, without left padding 0s
%asm {{ %asm {{
jsr conv.uword2decimal jsr conv.internal_uword2decimal
ldx #0 ldx #0
_output_digits _output_digits
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda internal_uword2decimal.decTenThousands,y
beq _allzero beq _allzero
cmp #'0' cmp #'0'
bne _gotdigit bne _gotdigit
iny iny
bne - bne -
_gotdigit sta string_out,x _gotdigit sta string_out,x
inx inx
iny iny
lda conv.uword2decimal.decTenThousands,y lda internal_uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
_end lda #0 _end lda #0
sta string_out,x sta string_out,x
rts lda #<string_out
ldy #>string_out
rts
_allzero lda #'0' _allzero lda #'0'
sta string_out,x sta string_out,x
inx inx
bne _end bne _end
}} }}
} }
asmsub str_w (word value @ AY) clobbers(A,X,Y) { asmsub str_w (word value @ AY) clobbers(X) -> str @AY {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's ; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
%asm {{ %asm {{
cpy #0 cpy #0
@ -191,18 +211,19 @@ asmsub str_w (word value @ AY) clobbers(A,X,Y) {
pha pha
lda #'-' lda #'-'
sta string_out sta string_out
tya tya
eor #255 eor #255
tay tay
pla pla
eor #255 eor #255
clc clc
adc #1 adc #1
bcc + bcc +
iny iny
+ jsr conv.uword2decimal + jsr conv.internal_uword2decimal
ldx #1 ldx #1
bne str_uw._output_digits bne str_uw._output_digits
rts
}} }}
} }
@ -494,17 +515,25 @@ _stop
; ----- low level number conversions to decimal strings ---- ; ----- low level number conversions to decimal strings ----
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X { asmsub internal_ubyte2decimal(ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X) %asm {{
%asm {{ ldy #'0'-1
ldy #uword2decimal.ASCII_0_OFFSET ldx #'9'+1
jmp uword2decimal.hex_try200 sec
}} - iny
sbc #100
bcs -
- dex
adc #10
bmi -
adc #'0'-1
rts
}}
} }
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X { asmsub internal_uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal ; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes ; output in internal_uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed) ; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's ; also returns Y = 100's, A = 10's, X = 1's
@ -677,7 +706,7 @@ decOnes .byte 0
}} }}
} }
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X { asmsub internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X) ; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself! ; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{ %asm {{
@ -686,11 +715,11 @@ asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
eor #255 eor #255
clc clc
adc #1 adc #1
+ jmp ubyte2decimal + jmp internal_ubyte2decimal
}} }}
} }
asmsub ubyte2hex (ubyte value @A) clobbers(X) -> ubyte @A, ubyte @Y { asmsub internal_ubyte2hex (ubyte value @A) clobbers(X) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y) ; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{ %asm {{
pha pha
@ -710,7 +739,7 @@ _hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as
}} }}
} }
asmsub uword2hex (uword value @AY) clobbers(A,Y) { asmsub internal_uword2hex (uword value @AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated) ; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
%asm {{ %asm {{
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG

View File

@ -126,7 +126,7 @@ bmx {
; If you're saving the whole screen width, you can leave screenwidth at 0. ; If you're saving the whole screen width, you can leave screenwidth at 0.
; Returns: success status. If false, error_message points to the error message string. ; Returns: success status. If false, error_message points to the error message string.
error_message = 0 error_message = 0
if compression { if compression!=0 {
error_message = "compression not supported" error_message = "compression not supported"
return false return false
} }
@ -170,6 +170,7 @@ save_end:
sub set_bpp(ubyte bpp) { sub set_bpp(ubyte bpp) {
ubyte[8] depths = [0,1,1,2,2,2,2,3] ubyte[8] depths = [0,1,1,2,2,2,2,3]
vera_colordepth = depths[bpp-1] vera_colordepth = depths[bpp-1]
bitsperpixel = bpp
} }
sub set_vera_colordepth(ubyte depth) { sub set_vera_colordepth(ubyte depth) {
@ -184,7 +185,7 @@ save_end:
for cx16.r0L in 0 to sizeof(header)-1 { for cx16.r0L in 0 to sizeof(header)-1 {
header[cx16.r0L] = cbm.CHRIN() header[cx16.r0L] = cbm.CHRIN()
} }
return not cbm.READST() return cbm.READST()==0
} }
sub read_palette() -> bool { sub read_palette() -> bool {
@ -197,7 +198,7 @@ save_end:
do { do {
cx16.r4L = cbm.CHRIN() cx16.r4L = cbm.CHRIN()
cx16.r4H = cbm.CHRIN() cx16.r4H = cbm.CHRIN()
if cx16.r3 { if cx16.r3!=0 {
pokew(cx16.r3, cx16.r4) ; into memory pokew(cx16.r3, cx16.r4) ; into memory
cx16.r3+=2 cx16.r3+=2
} else { } else {
@ -206,7 +207,7 @@ save_end:
} }
cx16.r2L-- cx16.r2L--
} until cx16.r2L==0 } until cx16.r2L==0
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof? return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
} }
sub read_bitmap_padded(ubyte vbank, uword vaddr, uword screenwidth) -> bool { sub read_bitmap_padded(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
@ -220,7 +221,7 @@ save_end:
if_cs if_cs
vbank++ vbank++
} }
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof? return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
} }
sub read_bitmap(ubyte vbank, uword vaddr) -> bool { sub read_bitmap(ubyte vbank, uword vaddr) -> bool {
@ -229,12 +230,12 @@ save_end:
cx16.vaddr(vbank, vaddr, 0, 1) cx16.vaddr(vbank, vaddr, 0, 1)
repeat height repeat height
read_scanline(cx16.r3) read_scanline(cx16.r3)
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof? return cbm.READST()==0 or cbm.READST()&$40!=0 ; no error or eof?
} }
sub read_scanline(uword size) { sub read_scanline(uword size) {
while size { while size!=0 {
cx16.r0 = cx16.MACPTR(min(255, size) as ubyte, &cx16.VERA_DATA0, true) void, cx16.r0 = cx16.MACPTR(min(255, size) as ubyte, &cx16.VERA_DATA0, true)
if_cs { if_cs {
; no MACPTR support ; no MACPTR support
repeat size repeat size
@ -251,7 +252,7 @@ save_end:
for cx16.r0L in 0 to sizeof(header)-1 { for cx16.r0L in 0 to sizeof(header)-1 {
cbm.CHROUT(header[cx16.r0L]) cbm.CHROUT(header[cx16.r0L])
} }
return not cbm.READST() return cbm.READST()==0
} }
sub write_palette() -> bool { sub write_palette() -> bool {
@ -262,7 +263,7 @@ save_end:
cx16.r2L = lsb(palette_entries) cx16.r2L = lsb(palette_entries)
cx16.vaddr(1, $fa00+palette_start*2, 0, 1) cx16.vaddr(1, $fa00+palette_start*2, 0, 1)
do { do {
if cx16.r3 { if cx16.r3!=0 {
cbm.CHROUT(@(cx16.r3)) ; from memory cbm.CHROUT(@(cx16.r3)) ; from memory
cx16.r3++ cx16.r3++
cbm.CHROUT(@(cx16.r3)) cbm.CHROUT(@(cx16.r3))
@ -273,7 +274,7 @@ save_end:
} }
cx16.r2L-- cx16.r2L--
} until cx16.r2L==0 } until cx16.r2L==0
return not cbm.READST() return cbm.READST()==0
} }
sub write_bitmap(ubyte vbank, uword vaddr, uword screenwidth) -> bool { sub write_bitmap(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
@ -282,21 +283,21 @@ save_end:
cx16.vaddr(vbank, vaddr, 0, 1) cx16.vaddr(vbank, vaddr, 0, 1)
cx16.r3 = bytes_per_scanline(width) ; num bytes per image scanline cx16.r3 = bytes_per_scanline(width) ; num bytes per image scanline
cx16.r2 = 0 cx16.r2 = 0
if screenwidth if screenwidth!=0
cx16.r2 = bytes_per_scanline(screenwidth-width) ; num bytes padding per screen scanline cx16.r2 = bytes_per_scanline(screenwidth-width) ; num bytes padding per screen scanline
repeat height { repeat height {
write_scanline(cx16.r3) write_scanline(cx16.r3)
repeat cx16.r2 repeat cx16.r2
cx16.r0L = cx16.VERA_DATA0 cx16.r0L = cx16.VERA_DATA0
} }
return not cbm.READST() return cbm.READST()==0
sub write_scanline(uword size) { sub write_scanline(uword size) {
while size { while size!=0 {
cx16.r0L = lsb(size) cx16.r0L = lsb(size)
if msb(size) if msb(size)!=0
cx16.r0L = 0 ; 256 bytes cx16.r0L = 0 ; 256 bytes
cx16.r0 = cx16.MCIOUT(cx16.r0L, &cx16.VERA_DATA0, true) void, cx16.r0 = cx16.MCIOUT(cx16.r0L, &cx16.VERA_DATA0, true)
if_cs { if_cs {
; no MCIOUT support ; no MCIOUT support
repeat size repeat size

View File

@ -32,6 +32,19 @@ diskio {
cbm.CHKOUT(WRITE_IO_CHANNEL) cbm.CHKOUT(WRITE_IO_CHANNEL)
} }
sub fastmode(ubyte mode) -> bool {
; -- set fast serial mode (0=none, 1=auto_tx, 2=fast writes, 3=both) for the SD card.
; Returns success status (fails on emulator host fs for example)
list_filename[0] = 'u'
list_filename[1] = '0'
list_filename[2] = '>'
list_filename[3] = 'b'
list_filename[4] = mode | $30
list_filename[5] = 0
send_command(list_filename)
return status_code()==0
}
sub directory() -> bool { sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success. ; -- Prints the directory contents to the screen. Returns success.
@ -70,7 +83,8 @@ diskio {
void cbm.CHRIN() ; skip 2 bytes void cbm.CHRIN() ; skip 2 bytes
void cbm.CHRIN() void cbm.CHRIN()
status = cbm.READST() status = cbm.READST()
if cbm.STOP2() void cbm.STOP()
if_z
break break
} }
status = cbm.READST() status = cbm.READST()
@ -79,7 +93,7 @@ io_error:
cbm.CLRCHN() ; restore default i/o devices cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(READ_IO_CHANNEL) cbm.CLOSE(READ_IO_CHANNEL)
if status and status & $40 == 0 { ; bit 6=end of file if status!=0 and status & $40 == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ") txt.print("\ni/o error, status: ")
txt.print_ub(status) txt.print_ub(status)
txt.nl() txt.nl()
@ -121,7 +135,7 @@ io_error:
io_error: io_error:
cbm.CLRCHN() cbm.CLRCHN()
cbm.CLOSE(READ_IO_CHANNEL) cbm.CLOSE(READ_IO_CHANNEL)
if status and status & $40 == 0 if status!=0 and status & $40 == 0
return 0 return 0
return list_filename return list_filename
} }
@ -210,14 +224,14 @@ io_error:
ubyte blocks_lsb = cbm.CHRIN() ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = cbm.CHRIN() ubyte blocks_msb = cbm.CHRIN()
if cbm.READST() if cbm.READST()!=0
goto close_end goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb) list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first " ; read until the filename starts after the first "
while cbm.CHRIN()!='\"' { while cbm.CHRIN()!='\"' {
if cbm.READST() if cbm.READST()!=0
goto close_end goto close_end
} }
@ -240,7 +254,7 @@ io_error:
list_filetype[0] = cx16.r15L list_filetype[0] = cx16.r15L
list_filetype[1] = cbm.CHRIN() list_filetype[1] = cbm.CHRIN()
list_filetype[2] = cbm.CHRIN() list_filetype[2] = cbm.CHRIN()
while cbm.CHRIN() { while cbm.CHRIN()!=0 {
; read the rest of the entry until the end ; read the rest of the entry until the end
} }
@ -248,7 +262,7 @@ io_error:
void cbm.CHRIN() void cbm.CHRIN()
if not list_skip_disk_name { if not list_skip_disk_name {
if not list_pattern if list_pattern==0
return true return true
if string.pattern_match(list_filename, list_pattern) if string.pattern_match(list_filename, list_pattern)
return true return true
@ -308,18 +322,18 @@ close_end:
; -- read from the currently open file, up to the given number of bytes. ; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions) ; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address. ; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
if not iteration_in_progress or not num_bytes if not iteration_in_progress or num_bytes==0
return 0 return 0
reset_read_channel() reset_read_channel()
list_blocks = 0 ; we reuse this variable for the total number of bytes read list_blocks = 0 ; we reuse this variable for the total number of bytes read
uword readsize uword readsize
while num_bytes { while num_bytes!=0 {
readsize = 255 readsize = 255
if num_bytes<readsize if num_bytes<readsize
readsize = num_bytes readsize = num_bytes
readsize = cx16.MACPTR(lsb(readsize), bufferpointer, false) ; fast block reads void, readsize = cx16.MACPTR(lsb(readsize), bufferpointer, false) ; fast block reads
if_cs if_cs
goto byte_read_loop ; MACPTR block read not supported, do fallback loop goto byte_read_loop ; MACPTR block read not supported, do fallback loop
list_blocks += readsize list_blocks += readsize
@ -327,7 +341,7 @@ close_end:
if msb(bufferpointer) == $c0 if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= readsize num_bytes -= readsize
if cbm.READST() & $40 { if cbm.READST() & $40 !=0 {
f_close() ; end of file, close it f_close() ; end of file, close it
break break
} }
@ -341,10 +355,10 @@ byte_read_loop: ; fallback if MACPTR isn't supported on the device
lda bufferpointer+1 lda bufferpointer+1
sta m_in_buffer+2 sta m_in_buffer+2
}} }}
while num_bytes { while num_bytes!=0 {
if cbm.READST() { if cbm.READST()!=0 {
f_close() f_close()
if cbm.READST() & $40 ; eof? if cbm.READST() & $40 !=0 ; eof?
return list_blocks ; number of bytes read return list_blocks ; number of bytes read
return 0 ; error. return 0 ; error.
} }
@ -371,7 +385,7 @@ m_in_buffer sta $ffff
reset_read_channel() reset_read_channel()
uword total_read = 0 uword total_read = 0
while not cbm.READST() { while cbm.READST()==0 {
cx16.r0 = f_read(bufferpointer, 256) cx16.r0 = f_read(bufferpointer, 256)
total_read += cx16.r0 total_read += cx16.r0
bufferpointer += cx16.r0 bufferpointer += cx16.r0
@ -444,7 +458,7 @@ _end rts
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, WRITE_IO_CHANNEL) cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, WRITE_IO_CHANNEL)
void cbm.OPEN() ; open 13,8,13,"filename" void cbm.OPEN() ; open 13,8,13,"filename"
if_cc { if_cc {
return not cbm.READST() return cbm.READST()==0
} }
cbm.CLOSE(WRITE_IO_CHANNEL) cbm.CLOSE(WRITE_IO_CHANNEL)
f_close_w() f_close_w()
@ -456,7 +470,7 @@ _end rts
if num_bytes!=0 { if num_bytes!=0 {
reset_write_channel() reset_write_channel()
do { do {
cx16.r0 = cx16.MCIOUT(lsb(num_bytes), bufferpointer, false) ; fast block writes void, cx16.r0 = cx16.MCIOUT(lsb(num_bytes), bufferpointer, false) ; fast block writes
if_cs if_cs
goto no_mciout goto no_mciout
num_bytes -= cx16.r0 num_bytes -= cx16.r0
@ -466,7 +480,7 @@ _end rts
if cbm.READST()!=0 if cbm.READST()!=0
return false return false
} until num_bytes==0 } until num_bytes==0
return not cbm.READST() return cbm.READST()==0
no_mciout: no_mciout:
; the device doesn't support MCIOUT, use a normal per-byte write loop ; the device doesn't support MCIOUT, use a normal per-byte write loop
@ -474,7 +488,7 @@ no_mciout:
cbm.CHROUT(@(bufferpointer)) cbm.CHROUT(@(bufferpointer))
bufferpointer++ bufferpointer++
} }
return not cbm.READST() return cbm.READST()==0
} }
return true return true
} }
@ -493,8 +507,7 @@ no_mciout:
str device_not_present_error = "device not present #xx" str device_not_present_error = "device not present #xx"
if cbm.READST()==128 { if cbm.READST()==128 {
device_not_present_error[len(device_not_present_error)-2] = 0 device_not_present_error[len(device_not_present_error)-2] = 0
conv.str_ub(drivenumber) void string.copy(conv.str_ub(drivenumber), &device_not_present_error+len(device_not_present_error)-2)
void string.copy(conv.string_out, &device_not_present_error+len(device_not_present_error)-2)
return device_not_present_error return device_not_present_error
} }
@ -506,7 +519,7 @@ no_mciout:
goto io_error goto io_error
void cbm.CHKIN(15) ; use #15 as input channel void cbm.CHKIN(15) ; use #15 as input channel
while not cbm.READST() { while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN() cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n' if cx16.r5L=='\r' or cx16.r5L=='\n'
break break
@ -526,6 +539,39 @@ io_error:
goto done goto done
} }
; similar to above, but instead of fetching the entire string, it only fetches the status code and returns it as ubyte
; in case of IO error, returns 255 (CMDR-DOS itself is physically unable to return such a value)
sub status_code() -> ubyte {
if cbm.READST()==128 {
return 255
}
cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15
if_cs
goto io_error
void cbm.CHKIN(15) ; use #15 as input channel
list_filename[0] = cbm.CHRIN()
list_filename[1] = cbm.CHRIN()
list_filename[2] = 0
while cbm.READST()==0 {
void cbm.CHRIN()
}
cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(15)
return conv.str2ubyte(list_filename)
io_error:
cbm.CLRCHN()
cbm.CLOSE(15)
return 255
}
; saves a block of memory to disk, including the default 2 byte prg header. ; saves a block of memory to disk, including the default 2 byte prg header.
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool { sub save(uword filenameptr, uword startaddress, uword savesize) -> bool {
@ -562,9 +608,9 @@ io_error:
}} }}
if_cc if_cc
cx16.r0L = cbm.READST()==0 cx16.r0L = cbm.READST()==0 as ubyte
return cx16.r0L return cx16.r0L as bool
} }
; Use kernal LOAD routine to load the given program file in memory. ; Use kernal LOAD routine to load the given program file in memory.
@ -596,7 +642,7 @@ io_error:
cbm.SETNAM(string.length(filenameptr), filenameptr) cbm.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1 ubyte secondary = 1
cx16.r1 = 0 cx16.r1 = 0
if address_override if address_override!=0
secondary = 0 secondary = 0
if headerless if headerless
secondary |= %00000010 ; activate cx16 kernal headerless load support secondary |= %00000010 ; activate cx16 kernal headerless load support
@ -659,7 +705,7 @@ io_error:
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
} }
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A { asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
; -- like the basic command VLOAD "filename",drivenumber,bank,address ; -- like the basic command VLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A ; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file has to have the usual 2 byte header (which will be skipped) ; the file has to have the usual 2 byte header (which will be skipped)
@ -698,7 +744,7 @@ internal_vload:
}} }}
} }
asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> ubyte @A { asmsub vload_raw(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
; -- like the basic command BVLOAD "filename",drivenumber,bank,address ; -- like the basic command BVLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A ; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file is read fully including the first two bytes. ; the file is read fully including the first two bytes.
@ -755,7 +801,7 @@ internal_vload:
repeat 6 { repeat 6 {
void cbm.CHRIN() void cbm.CHRIN()
} }
while cbm.CHRIN() { while cbm.CHRIN()!=0 {
; skip first line (drive label) ; skip first line (drive label)
} }
while cbm.CHRIN()!='"' { while cbm.CHRIN()!='"' {
@ -786,7 +832,7 @@ internal_vload:
io_error: io_error:
cbm.CLRCHN() cbm.CLRCHN()
cbm.CLOSE(READ_IO_CHANNEL) cbm.CLOSE(READ_IO_CHANNEL)
if status and status & $40 == 0 if status!=0 and status & $40 == 0
return 0 return 0
if @(cx16.r12)==0 { if @(cx16.r12)==0 {
cx16.r12-- cx16.r12--

View File

@ -1,5 +1,5 @@
; Emulator debug interface. ; Emulator debug interface.
; Docs: https://github.com/X16Community/x16-emulator#emulator-io-registers ; Docs: https://github.com/X16Community/x16-emulator/tree/d52f118ce893fa24c4ca021a0b8de46cb80e5ccf#emulator-io-registers
emudbg { emudbg {
%option ignore_unused %option ignore_unused
@ -41,7 +41,7 @@ emudbg {
chr = @(isoString) chr = @(isoString)
if_z if_z
break break
EMU_CPUCLK_U = chr EMU_CHROUT = chr
isoString++ isoString++
} }
} }

View File

@ -1,15 +1,10 @@
; Prog8 definitions for floating point handling on the CommanderX16 ; Prog8 definitions for floating point handling on the CommanderX16
%option enable_floats, no_symbol_prefixing, ignore_unused %option enable_floats, no_symbol_prefixing, ignore_unused
%import floats_functions %import shared_floats_functions
floats { floats {
; ---- this block contains C-64 compatible floating point related functions ---- ; ---- this block contains C-64 compatible floating point related functions ----
const float π = 3.141592653589793
const float PI = π
const float TWOPI = 2*π
; ---- ROM float functions (same as on C128 except base page) ---- ; ---- ROM float functions (same as on C128 except base page) ----
@ -19,7 +14,7 @@ floats {
; note: fac1/2 might get clobbered even if not mentioned in the function's name. ; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1. ; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY) romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. (might throw ILLEGAL QUANTITY) See "basic.sym" kernal symbol file for their memory locations.
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1 ; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1 ; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
@ -43,7 +38,7 @@ romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log) romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use ROUND or FADDH first to round instead of trunc romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to integer round instead of trunc
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1) romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
romsub $fe33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) romsub $fe33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
romsub $fe36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y romsub $fe36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y
@ -53,7 +48,7 @@ romsub $fe3f = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
romsub $fe42 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1) romsub $fe42 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
romsub $fe45 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1) romsub $fe45 = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
romsub $fe48 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1) romsub $fe48 = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
romsub $fe4b = ROUND() clobbers(A,X,Y) ; round fac1 romsub $fe4b = ROUND() clobbers(A,X,Y) ; round least significant bit of fac1
romsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1) romsub $fe4e = ABS() clobbers(A,X,Y) ; fac1 = ABS(fac1)
romsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive romsub $fe51 = SIGN() clobbers(X,Y) -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
romsub $fe54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than romsub $fe54 = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
@ -65,10 +60,10 @@ romsub $fe60 = MOVFRM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value f
romsub $fe63 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1 romsub $fe63 = MOVFM(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac1
romsub $fe66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt romsub $fe66 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
romsub $fe69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1 romsub $fe69 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
romsub $fe6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded) romsub $fe6c = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded the least significant bit)
; X16 additions ; X16 additions
romsub $fe6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT romsub $fe6f = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for integer rounding- call this before INT
romsub $fe72 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0 romsub $fe72 = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
romsub $fe75 = NORMAL() clobbers(A,X,Y) ; normalize fac1 romsub $fe75 = NORMAL() clobbers(A,X,Y) ; normalize fac1
romsub $fe78 = NEGFAC() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) (doesn't work, juse use NEGOP() instead!) romsub $fe78 = NEGFAC() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1) (doesn't work, juse use NEGOP() instead!)
@ -98,8 +93,8 @@ asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) { asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1 ; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
%asm {{ %asm {{
sty $c4 ; facmo ($64 on c128) sty $c4 ; facmo
sta $c5 ; facmo+1 ($65 on c128) sta $c5 ; facmo+1
ldx #$90 ldx #$90
sec sec
jmp FLOATC jmp FLOATC
@ -136,14 +131,12 @@ asmsub FREADUY (ubyte value @Y) {
} }
asmsub parse(str value @AY) -> float @FAC1 { asmsub parse(str value @AY) -> float @FAC1 {
; -- parse a string value of a number to float in FAC1 ; -- Parse a string value of a number to float in FAC1.
; warning: on older <R47 kernals it uses an internal BASIC routine that is ROM version dependent, ; Requires kernal R47 or newer! (depends on val_1 working)
; ($deb6 is inside the routine for VAL at $deb3) See basic.sym from x16-rom
; TODO once ROM v47 is released, all the workarounds here can be removed. But probably keep the kernal VAL_1 existance check
%asm {{ %asm {{
ldx VAL_1 ldx VAL_1
cpx #$4c ; is there an implementation in VAL_1? (test for JMP) cpx #$4c ; is there an implementation in VAL_1? (test for JMP)
bne + ; no, do it ourselves bne _borked ; no, print error message
pha ; yes, count the length and call rom VAL_1. pha ; yes, count the length and call rom VAL_1.
phy phy
jsr prog8_lib.strlen jsr prog8_lib.strlen
@ -151,15 +144,7 @@ asmsub parse(str value @AY) -> float @FAC1 {
ply ply
plx plx
jmp VAL_1 jmp VAL_1
+ sta $a9 ; 'index' variable _borked
sty $aa
jsr prog8_lib.strlen
lda $deb6
cmp #$d0 ; sanity check for kernal routine correct
bne +
tya
jmp $deb6 ; kernal version dependent...
+ ; print error message if routine is borked in kernal, and exit program
ldy #0 ldy #0
- lda _msg,y - lda _msg,y
beq + beq +
@ -168,12 +153,12 @@ asmsub parse(str value @AY) -> float @FAC1 {
bne - bne -
+ jmp sys.exit + jmp sys.exit
_msg .text 13,"?val kaputt",13,0 _msg .text 13,"?rom 47+ required for val1",13,0
}} }}
} }
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT &uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT (See "basic.sym" kernal symbol file)
sub rnd() -> float { sub rnd() -> float {
%asm {{ %asm {{

View File

@ -55,14 +55,16 @@ gfx2 {
} }
else -> { else -> {
; back to default text mode ; back to default text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode if active_mode!=0 {
cbm.CINT() cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L cbm.CINT()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
}
} }
} }
init_mode(mode) init_mode(mode)
if active_mode if active_mode!=0
clear_screen(0) clear_screen(0)
} }
@ -305,17 +307,17 @@ gfx2 {
} }
word @zp d = 0 word @zp d = 0
cx16.r1L = true ; 'positive_ix' cx16.r1L = 1 ;; true ; 'positive_ix'
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
cx16.r1L = false cx16.r1L = 0 ;; false
} }
word @zp dx2 = dx*2 word @zp dx2 = dx*2
word @zp dy2 = dy*2 word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X cx16.r14 = x1 ; internal plot X
if dx >= dy { if dx >= dy {
if cx16.r1L { if cx16.r1L!=0 {
repeat { repeat {
plot(cx16.r14, y1, color) plot(cx16.r14, y1, color)
if cx16.r14==x2 if cx16.r14==x2
@ -342,7 +344,7 @@ gfx2 {
} }
} }
else { else {
if cx16.r1L { if cx16.r1L!=0 {
repeat { repeat {
plot(cx16.r14, y1, color) plot(cx16.r14, y1, color)
if y1 == y2 if y1 == y2
@ -705,7 +707,7 @@ gfx2 {
push_stack(xx, xx, yy, 1) push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1) push_stack(xx, xx, yy + 1, -1)
word left = 0 word left = 0
while cx16.r12L { while cx16.r12L!=0 {
pop_stack() pop_stack()
xx = x1 xx = x1
; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it? ; possible speed optimization: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
@ -725,15 +727,15 @@ gfx2 {
xx = x1 + 1 xx = x1 + 1
do { do {
cx16.r9 = xx cx16.r9s = xx
; possible speed optimization: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it? ; possible speed optimization: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
while xx <= width-1 { while xx <= width-1 {
if pget(xx as uword, yy as uword) != cx16.r11L if pget(xx as uword, yy as uword) != cx16.r11L
break break
xx++ xx++
} }
if cx16.r9!=xx if cx16.r9s!=xx
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L) horizontal_line(cx16.r9, yy as uword, xx-cx16.r9s as uword, cx16.r10L)
push_stack(left, xx - 1, yy, dy) push_stack(left, xx - 1, yy, dy)
if xx > x2 + 1 if xx > x2 + 1
@ -845,7 +847,7 @@ skip:
when active_mode { when active_mode {
1 -> { 1 -> {
; lores 256c ; lores 256c
while @(sctextptr) { while @(sctextptr)!=0 {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1) cx16.vaddr(charset_bank, chardataptr, 1, 1)
repeat 8 { repeat 8 {
@ -874,9 +876,9 @@ skip:
; hires 4c ; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop. ; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color cx16.r11L = color
while @(sctextptr) { while @(sctextptr)!=0 {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1 cx16.vaddr(charset_bank, chardataptr, 1, 1) ; for reading the chardata from Vera data channel 1
position(xx, yy) ; only calculated once, we update vera address in the loop instead position(xx, yy) ; only calculated once, we update vera address in the loop instead
cx16.VERA_ADDR_H &= $0f ; no auto increment cx16.VERA_ADDR_H &= $0f ; no auto increment
repeat 8 { repeat 8 {

View File

@ -57,20 +57,20 @@ graphics {
} }
sub fillrect(uword xx, uword yy, uword width, uword height) { sub fillrect(uword xx, uword yy, uword width, uword height) {
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 1) cx16.GRAPH_draw_rect(xx, yy, width, height, 0, true)
} }
sub rect(uword xx, uword yy, uword width, uword height) { sub rect(uword xx, uword yy, uword width, uword height) {
cx16.GRAPH_draw_rect(xx, yy, width, height, 0, 0) cx16.GRAPH_draw_rect(xx, yy, width, height, 0, false)
} }
sub horizontal_line(uword xx, uword yy, uword length) { sub horizontal_line(uword xx, uword yy, uword length) {
if length if length!=0
cx16.GRAPH_draw_line(xx, yy, xx+length-1, yy) cx16.GRAPH_draw_line(xx, yy, xx+length-1, yy)
} }
sub vertical_line(uword xx, uword yy, uword height) { sub vertical_line(uword xx, uword yy, uword height) {
if height if height!=0
cx16.GRAPH_draw_line(xx, yy, xx, yy+height-1) cx16.GRAPH_draw_line(xx, yy, xx, yy+height-1)
} }

View File

@ -7,7 +7,6 @@
; NOTE: a lot of the code here is similar or the same to that in gfx2 ; NOTE: a lot of the code here is similar or the same to that in gfx2
; NOTE: For sake of speed, NO BOUNDS CHECKING is performed in most routines! ; NOTE: For sake of speed, NO BOUNDS CHECKING is performed in most routines!
; You'll have to make sure yourself that you're not writing outside of bitmap boundaries! ; You'll have to make sure yourself that you're not writing outside of bitmap boundaries!
;
monogfx { monogfx {
@ -16,7 +15,10 @@ monogfx {
; read-only control variables: ; read-only control variables:
uword width = 0 uword width = 0
uword height = 0 uword height = 0
bool dont_stipple_flag = true ; set to false to enable stippling mode ubyte mode
const ubyte MODE_NORMAL = %00000000
const ubyte MODE_STIPPLE = %00000001
const ubyte MODE_INVERT = %00000010
sub lores() { sub lores() {
; enable 320*240 bitmap mode ; enable 320*240 bitmap mode
@ -29,6 +31,7 @@ monogfx {
cx16.VERA_L1_TILEBASE = 0 cx16.VERA_L1_TILEBASE = 0
width = 320 width = 320
height = 240 height = 240
mode = MODE_NORMAL
clear_screen(0) clear_screen(0)
} }
@ -43,6 +46,7 @@ monogfx {
cx16.VERA_L1_TILEBASE = %00000001 cx16.VERA_L1_TILEBASE = %00000001
width = 640 width = 640
height = 480 height = 480
mode = MODE_NORMAL
clear_screen(0) clear_screen(0)
} }
@ -53,8 +57,11 @@ monogfx {
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
} }
sub drawmode(ubyte dm) {
mode = dm
}
sub clear_screen(ubyte color) { sub clear_screen(ubyte color) {
stipple(false)
position(0, 0) position(0, 0)
when width { when width {
320 -> { 320 -> {
@ -69,10 +76,6 @@ monogfx {
position(0, 0) position(0, 0)
} }
sub stipple(bool enable) {
dont_stipple_flag = not enable
}
sub rect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) { sub rect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) {
if rwidth==0 or rheight==0 if rwidth==0 or rheight==0
return return
@ -122,8 +125,9 @@ monogfx {
bne - bne -
sta cx16.r0L ; new left byte sta cx16.r0L ; new left byte
+ +
lda p8v_dont_stipple_flag lda p8v_mode
bne _dontstipple lsr a
bcc _dontstipple
; determine stipple pattern ; determine stipple pattern
lda p8v_yy lda p8v_yy
and #1 and #1
@ -162,24 +166,36 @@ _clear
} }
ubyte separate_pixels = (8-lsb(xx)) & 7 ubyte separate_pixels = (8-lsb(xx)) & 7
if separate_pixels { if separate_pixels!=0 {
if dont_stipple_flag { when mode {
position(xx,yy) MODE_NORMAL -> {
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off position(xx,yy)
if draw cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
cx16.VERA_DATA0 |= masked_starts[separate_pixels] if draw
else cx16.VERA_DATA0 |= masked_starts[separate_pixels]
cx16.VERA_DATA0 &= ~masked_starts[separate_pixels] else
xx += separate_pixels cx16.VERA_DATA0 &= ~masked_starts[separate_pixels]
} else { xx += separate_pixels
repeat separate_pixels { }
plot(xx, yy, draw) MODE_STIPPLE -> {
xx++ repeat separate_pixels {
plot(xx, yy, draw)
xx++
}
}
MODE_INVERT -> {
position(xx,yy)
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
if draw
cx16.VERA_DATA0 ^= masked_starts[separate_pixels]
else
cx16.VERA_DATA0 &= masked_starts[separate_pixels]
xx += separate_pixels
} }
} }
length -= separate_pixels length -= separate_pixels
} }
if length { if length!=0 {
position(xx, yy) position(xx, yy)
separate_pixels = lsb(length) & 7 separate_pixels = lsb(length) & 7
xx += length & $fff8 xx += length & $fff8
@ -194,10 +210,28 @@ _clear
bne + bne +
ldy #0 ; black ldy #0 ; black
bra _loop bra _loop
+ lda p8v_dont_stipple_flag + lda p8v_mode
beq _stipple lsr a
ldy #255 ; don't stipple bcs _stipple
lsr a
bcs _inverted
ldy #255 ; normal drawing mode
bra _loop bra _loop
_inverted lda #0
jsr cx16.vaddr_clone
_invertedloop lda p8v_length
ora p8v_length+1
beq _done
lda cx16.VERA_DATA1
eor #255
sta cx16.VERA_DATA0
lda p8v_length
bne +
dec p8v_length+1
+ dec p8v_length
bra _invertedloop
_stipple lda p8v_yy _stipple lda p8v_yy
and #1 ; determine stipple pattern to use and #1 ; determine stipple pattern to use
bne + bne +
@ -216,16 +250,26 @@ _loop lda p8v_length
_done _done
}} }}
if dont_stipple_flag { when mode {
MODE_NORMAL -> {
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
if draw
cx16.VERA_DATA0 |= masked_ends[separate_pixels]
else
cx16.VERA_DATA0 &= ~masked_ends[separate_pixels]
}
MODE_STIPPLE -> {
repeat separate_pixels {
plot(xx, yy, draw)
xx++
}
}
MODE_INVERT -> {
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
if draw if draw
cx16.VERA_DATA0 |= masked_ends[separate_pixels] cx16.VERA_DATA0 ^= masked_ends[separate_pixels]
else else
cx16.VERA_DATA0 &= ~masked_ends[separate_pixels] cx16.VERA_DATA0 &= masked_ends[separate_pixels]
} else {
repeat separate_pixels {
plot(xx, yy, draw)
xx++
} }
} }
} }
@ -253,7 +297,16 @@ _done
sub vertical_line(uword xx, uword yy, uword lheight, bool draw) { sub vertical_line(uword xx, uword yy, uword lheight, bool draw) {
cx16.r15L = monogfx.plot.maskbits[xx as ubyte & 7] ; bitmask cx16.r15L = monogfx.plot.maskbits[xx as ubyte & 7] ; bitmask
if draw { if draw {
if dont_stipple_flag { %asm {{
lda p8v_mode
and #p8c_MODE_INVERT
beq +
lda #$45 ; eor ZP modifying code
bne ++
+ lda #$05 ; ora ZP modifying code
+ sta drawmode
}}
if mode!=MODE_STIPPLE {
; draw continuous line. ; draw continuous line.
position2(xx,yy,true) position2(xx,yy,true)
if width==320 if width==320
@ -263,7 +316,7 @@ _done
repeat lheight { repeat lheight {
%asm {{ %asm {{
lda cx16.VERA_DATA0 lda cx16.VERA_DATA0
ora cx16.r15L drawmode: ora cx16.r15L
sta cx16.VERA_DATA1 sta cx16.VERA_DATA1
}} }}
} }
@ -340,17 +393,17 @@ _done
} }
word @zp d = 0 word @zp d = 0
cx16.r1L = true ; 'positive_ix' cx16.r1L = 1 ;; true ; 'positive_ix'
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
cx16.r1L = false cx16.r1L = 0 ;; false
} }
word @zp dx2 = dx*2 word @zp dx2 = dx*2
word @zp dy2 = dy*2 word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X cx16.r14 = x1 ; internal plot X
if dx >= dy { if dx >= dy {
if cx16.r1L { if cx16.r1L!=0 {
repeat { repeat {
plot(cx16.r14, y1, draw) plot(cx16.r14, y1, draw)
if cx16.r14==x2 if cx16.r14==x2
@ -377,7 +430,7 @@ _done
} }
} }
else { else {
if cx16.r1L { if cx16.r1L!=0 {
repeat { repeat {
plot(cx16.r14, y1, draw) plot(cx16.r14, y1, draw)
if y1 == y2 if y1 == y2
@ -554,12 +607,19 @@ _done
if draw { if draw {
; solid color or perhaps stipple ; solid color or perhaps stipple
%asm {{ %asm {{
lda p8v_mode
lsr a
bcs +
lsr a
bcs p8l_invert
bra p8l_nostipple
+ ; stipple mode
lda p8v_xx lda p8v_xx
eor p8v_yy eor p8v_yy
ora p8v_dont_stipple_flag
and #1 and #1
}} }}
if_nz { if_nz {
nostipple:
prepare() prepare()
%asm {{ %asm {{
tsb cx16.VERA_DATA0 tsb cx16.VERA_DATA0
@ -572,6 +632,16 @@ _done
trb cx16.VERA_DATA0 trb cx16.VERA_DATA0
}} }}
} }
return
invert:
prepare()
%asm {{
lda cx16.VERA_DATA0
eor p8v_maskbits,y
sta cx16.VERA_DATA0
}}
return
sub prepare() { sub prepare() {
%asm {{ %asm {{
@ -606,7 +676,7 @@ _done
plot(xx, yy, draw) plot(xx, yy, draw)
} }
sub pget(uword @zp xx, uword yy) -> ubyte { sub pget(uword @zp xx, uword yy) -> bool {
%asm {{ %asm {{
lda p8v_xx lda p8v_xx
and #7 and #7
@ -708,7 +778,7 @@ _done
}} }}
yy+=dy yy+=dy
} }
cx16.r11L = pget(xx as uword, yy as uword) ; old_color cx16.r11L = pget(xx as uword, yy as uword) as ubyte ; old_color
if cx16.r11L == cx16.r10L if cx16.r11L == cx16.r10L
return return
if xx<0 or xx>width-1 or yy<0 or yy>height-1 if xx<0 or xx>width-1 or yy<0 or yy>height-1
@ -716,16 +786,16 @@ _done
push_stack(xx, xx, yy, 1) push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1) push_stack(xx, xx, yy + 1, -1)
word left = 0 word left = 0
while cx16.r12L { while cx16.r12L!=0 {
pop_stack() pop_stack()
xx = x1 xx = x1
while xx >= 0 { while xx >= 0 {
if pget(xx as uword, yy as uword) != cx16.r11L if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
break break
xx-- xx--
} }
if x1!=xx if x1!=xx
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L) horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L as bool)
else else
goto skip goto skip
@ -735,14 +805,14 @@ _done
xx = x1 + 1 xx = x1 + 1
do { do {
cx16.r9 = xx cx16.r9s = xx
while xx <= width-1 { while xx <= width-1 {
if pget(xx as uword, yy as uword) != cx16.r11L if pget(xx as uword, yy as uword) as ubyte != cx16.r11L
break break
xx++ xx++
} }
if cx16.r9!=xx if cx16.r9s!=xx
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L) horizontal_line(cx16.r9, yy as uword, xx-cx16.r9s as uword, cx16.r10L as bool)
push_stack(left, xx - 1, yy, dy) push_stack(left, xx - 1, yy, dy)
if xx > x2 + 1 if xx > x2 + 1
@ -750,7 +820,7 @@ _done
skip: skip:
xx++ xx++
while xx <= x2 { while xx <= x2 {
if pget(xx as uword, yy as uword) == cx16.r11L if pget(xx as uword, yy as uword) as ubyte == cx16.r11L
break break
xx++ xx++
} }
@ -783,7 +853,7 @@ skip:
cx16.screen_set_charset(charset, 0) cx16.screen_set_charset(charset, 0)
} }
sub text(uword @zp xx, uword yy, bool draw, uword sctextptr) { sub text(uword @zp xx, uword yy, bool draw, str sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use. ; You must also have called text_charset() first to select and prepare the character set to use.
uword chardataptr uword chardataptr
@ -791,7 +861,18 @@ skip:
ubyte[8] @shared char_bitmap_bytes_right ubyte[8] @shared char_bitmap_bytes_right
cx16.r3 = sctextptr cx16.r3 = sctextptr
while @(cx16.r3) { %asm {{
lda p8v_mode
cmp #p8c_MODE_INVERT
beq +
lda #$0d ; ORA abs modifying code
bne ++
+ lda #$4d ; EOR abs modifying code
+ sta cdraw_mod1
sta cdraw_mod2
}}
while @(cx16.r3)!=0 {
chardataptr = charset_addr + @(cx16.r3) * $0008 chardataptr = charset_addr + @(cx16.r3) * $0008
; copy the character bitmap into RAM ; copy the character bitmap into RAM
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1) cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
@ -824,7 +905,7 @@ skip:
%asm {{ %asm {{
ldy #0 ldy #0
- lda p8v_char_bitmap_bytes_left,y - lda p8v_char_bitmap_bytes_left,y
ora cx16.VERA_DATA1 cdraw_mod1 ora cx16.VERA_DATA1
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
iny iny
cpy #8 cpy #8
@ -843,14 +924,14 @@ skip:
}} }}
} }
; right part of shifted char ; right part of shifted char
if lsb(xx) & 7 { if lsb(xx) & 7 !=0 {
position2(xx+8, yy, true) position2(xx+8, yy, true)
set_autoincrs() set_autoincrs()
if draw { if draw {
%asm {{ %asm {{
ldy #0 ldy #0
- lda p8v_char_bitmap_bytes_right,y - lda p8v_char_bitmap_bytes_right,y
ora cx16.VERA_DATA1 cdraw_mod2 ora cx16.VERA_DATA1
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
iny iny
cpy #8 cpy #8

View File

@ -48,7 +48,7 @@ psg {
sub freq(ubyte voice_num, uword vera_freq) { sub freq(ubyte voice_num, uword vera_freq) {
; -- Changes the frequency of the voice's sound. ; -- Changes the frequency of the voice's sound.
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation. ; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md) ; (https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2009%20-%20VERA%20Programmer's%20Reference.md)
; Write freq MSB first and then LSB to reduce the chance on clicks ; Write freq MSB first and then LSB to reduce the chance on clicks
sys.irqsafe_set_irqd() sys.irqsafe_set_irqd()
cx16.r0 = $f9c1 + voice_num * 4 cx16.r0 = $f9c1 + voice_num * 4
@ -134,7 +134,7 @@ psg {
} }
1 -> { 1 -> {
; sustain ; sustain
if envelope_sustains[cx16.r1L] { if envelope_sustains[cx16.r1L]!=0 {
envelope_sustains[cx16.r1L]-- envelope_sustains[cx16.r1L]--
} else { } else {
envelope_states[cx16.r1L] = 2 ; start release envelope_states[cx16.r1L] = 2 ; start release
@ -143,7 +143,7 @@ psg {
2 -> { 2 -> {
; release ; release
cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040 cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040
if msb(cx16.r0) & %11000000 { if msb(cx16.r0) & %11000000 !=0 {
cx16.r0 = 0 cx16.r0 = 0
envelope_releases[cx16.r1L] = 0 envelope_releases[cx16.r1L] = 0
} }

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