Compare commits

...
144 Commits
Author SHA1 Message Date
Irmen de Jong 4030a1b9e5 cleanup, animals example removed because it's superseded by the pointers/animalgame example now 2025-12-16 16:48:34 +01:00
Irmen de Jong 0217d05c68 IR: loadfield/storefield now accept offsets up to 65535 2025-12-16 14:56:11 +01:00
Irmen de Jong 8553f3c9f1 IR: loadfield is a better name 2025-12-16 12:57:45 +01:00
Irmen de Jong d157d03ea0 IR: optimize load+add+load into single loadm with offset (also store)
update gradle wrapper to 9.2.0
2025-12-16 12:17:52 +01:00
Irmen de Jong 1799b5f00c updated bundled zsmkit version to 2.8 2025-12-15 18:41:28 +01:00
Irmen de Jong 2ddb5e11b7 updated symboldumps 2025-12-13 02:12:00 +01:00
Irmen de Jong 3aa8b9fd12 turn prefix - numbers into negative numbers asap to avoid type problems later 2025-12-13 01:13:52 +01:00
Irmen de Jong 51300196e8 fix more long comparisons with arrays, fixes #196 2025-12-12 21:01:51 +01:00
Irmen de Jong f7b2f19cba remove restriction about long comparison with expression 2025-12-11 21:43:53 +01:00
Irmen de Jong 014a82a1ee always put all struct types as .struct in asm code to make them all accessible for size and offsets 2025-12-10 21:39:44 +01:00
Irmen de Jong 745cf3d958 IR: removed loadix/storeix instructions 2025-12-09 22:55:53 +01:00
Irmen de Jong c4aa681d58 IR: better name for loadfield/storefield instructions 2025-12-09 00:40:26 +01:00
Irmen de Jong feb0774bbd in benchmark-c routines, use same CIA timer as the original C code did so the program size is better comparable 2025-12-08 01:08:33 +01:00
Irmen de Jong 2ccfb2ac90 fix double evaluation (sign extension) of word to long assignment. Fixes #201 2025-12-07 22:23:59 +01:00
Irmen de Jong 2191c4b7d8 remove redundant expression assignment before multiplying certain words 2025-12-07 14:06:59 +01:00
Irmen de Jong 39c1cf0c3c fix diskio lf_start_list_dirs() and lf_start_list_files() not processing the pattern argument 2025-12-07 05:11:08 +01:00
Irmen de Jong c693be0dd6 fix byte subtract from long
fix IR register check error for %ir segments
2025-12-06 20:40:42 +01:00
Irmen de Jong 0f433055a7 fix pointer dereference division and modulus , fixes #199 2025-12-05 22:19:54 +01:00
Irmen de Jong 76c95ba6fa some more obscure inplace modifications implemented 2025-12-05 21:00:14 +01:00
Irmen de Jong bd272a8567 optimize some specialized cases of long << 2025-12-05 01:08:19 +01:00
Irmen de Jong b38fff76e9 hack to work around struct name prefix inconsistency
see github issue 198
2025-12-03 21:07:08 +01:00
Irmen de Jong 07b7639ff2 better error msg 2025-12-02 22:56:40 +01:00
Irmen de Jong 6871561c09 fix parent node error in sizeof() 2025-12-01 23:21:14 +01:00
Irmen de Jong 77a71138ad fix typo r8r0sl -> r8r9sl 2025-11-30 22:05:57 +01:00
Irmen de Jong 165f94bc93 fix long expression <0 / >0 comparison 2025-11-30 16:57:05 +01:00
Irmen de Jong b75090f928 fix some register clobbering with long operations 2025-11-30 16:44:36 +01:00
Irmen de Jong 62b0c82f93 implement long <=0 and long >=0 comparisons
make sure the adpcm benchmark doesn't use random memory contents
2025-11-30 16:25:58 +01:00
Irmen de Jong b1ef863c7f make $8000000 a valid long integer (-2147483648) 2025-11-26 23:10:59 +01:00
Irmen de Jong e78345410f fix long-- high word wrap bug 2025-11-26 20:13:17 +01:00
Irmen de Jong 8b86f97aaa Implement struct field long and signed word bitshifts. Fixes #194 2025-11-25 22:28:28 +01:00
Irmen de Jong 93135774e6 fix IR signed bitshift right 2025-11-25 22:16:28 +01:00
Irmen de Jong a64f27c6b0 add cx16.r0r1sl, cx16.r2r3sl, ... that memory-map signed longs on the virtual registers 2025-11-25 21:13:03 +01:00
Irmen de Jong 69ef63c96d tweaks 2025-11-24 22:23:40 +01:00
Irmen de Jong df1a2a1611 also optimize BRA+RTS into just BRA
release 12.0
2025-11-23 15:18:05 +01:00
Irmen de Jong d19a3af9ed change some single use float global constants to their asm proc 2025-11-21 21:35:51 +01:00
Irmen de Jong 352c11ad9f optimize float<>0 into sgn(float)<>0 2025-11-21 00:57:43 +01:00
Irmen de Jong 9504711fc7 IR/VM: SGN sets status bits 2025-11-19 21:37:04 +01:00
Irmen de Jong 590feda903 IR: fix sgn(float) register type error 2025-11-19 21:11:31 +01:00
Irmen de Jong 6e7e2922bf moved the float <> long cast routines to float.asm where the other cast routines are too 2025-11-19 18:45:32 +01:00
Irmen de Jong 01df1f0083 way more efficient implementation of internal_cast_as_long now using QINT 2025-11-19 18:32:40 +01:00
Irmen de Jong 397299bd1d implement 6502 codegen for casting float to long (super slow...) 2025-11-19 00:05:18 +01:00
Irmen de Jong c275aacd38 implement 6502 codegen for casting long to float 2025-11-18 23:26:12 +01:00
Irmen de Jong 6a6e18773e C64: key repeat is now enabled at program startup, to fall in line with the default key repeat behavior on the C128 and X16. 2025-11-18 21:09:13 +01:00
Irmen de Jong 99e037489b IR: added float<>long casts, cx16: blink_cursor extapi tested in test.p8 2025-11-17 23:13:56 +01:00
Irmen de Jong fb5290e17b cx16: added diskio.f_fatlba() 2025-11-15 20:17:26 +01:00
Irmen de Jong 818774ab84 added conv.hex2long(), added more new X16 extapi calls (rom 49+) 2025-11-15 17:52:34 +01:00
Irmen de Jong d667312acc Merge branch 'refs/heads/12.1-SNAPSHOT'
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-11-15 15:15:06 +01:00
Irmen de Jong 64d0cd87a8 diskio.f_seek() and f_tell() now use longs instead of separate words
also fix bug when returning multiple long values
2025-11-15 15:13:26 +01:00
Irmen de Jong 2fffbdde89 todo 2025-11-14 20:05:46 +01:00
Irmen de Jong dc8ea31c49 fix codegen error for a word array equality test 2025-11-13 21:18:04 +01:00
Irmen de Jong f4ead66e91 fix param name breakage in PET txt.setcc for ignored charcolor parameter 2025-11-12 22:00:30 +01:00
Irmen de Jong 314e7f5691 doc updates 2025-11-10 23:42:29 +01:00
Irmen de Jong da31465b7f added the missing smallstack to buffers module 2025-11-10 00:21:13 +01:00
Irmen de Jong 397a907088 fix handling and errors of using long addresses in memread/memwrite 2025-11-09 18:17:57 +01:00
Irmen de Jong 50c6962e1f fix symbol lookup crash 2025-11-09 15:10:10 +01:00
Irmen de Jong b7d1fb1342 translate address-of into + expression for non-const address ptrs too,
also IR optimization to use immediate arithmetic opcodes if possible
2025-11-09 01:36:34 +01:00
Irmen de Jong afb458a7da fix crash in long address-of 2025-11-08 22:24:37 +01:00
Irmen de Jong da3c7f267f improved identifier const value handling if types differ 2025-11-08 20:34:51 +01:00
Irmen de Jong b7ba7c50b1 handle &ptr[offset] for non-uword ptr 2025-11-08 20:31:28 +01:00
Irmen de Jong bdbfe7048b updated library symboldumps 2025-11-08 00:21:53 +01:00
Irmen de Jong 79039b66d4 beta6 2025-11-07 23:40:03 +01:00
Irmen de Jong 833e463525 String indexing bound check now includes the terminating 0 character. Also fix negative indexes on strings.
fixes #190
2025-11-07 23:20:44 +01:00
Irmen de Jong 6611e4e092 improve error message 2025-11-07 01:22:41 +01:00
Irmen de Jong 1f31cb18e4 IR: fix missing source lines in p8ir file 2025-11-06 00:39:31 +01:00
Irmen de Jong bc8ba252a5 made Position line, col, endcol all 1-based
added a precise (but slow) back face culling routine to the cobra-mk3 example
2025-11-05 00:25:43 +01:00
Irmen de Jong c353dd40bf put on..call jumplist in correct scope, fixes #197 2025-11-03 23:46:49 +01:00
Irmen de Jong 928ef6bbaa fix on..call parse problem, also struct name cannot be a keyword 2025-11-03 21:21:07 +01:00
Irmen de Jong 45dde856c6 upgrade to kotlin 2.2.21 2025-11-02 20:08:59 +01:00
Irmen de Jong e1ccef4e89 sort the library routines better, updated result of crc32 benchmark 2025-11-02 16:58:49 +01:00
Irmen de Jong 4d3f0ec223 sizeof(string) is now defined 2025-11-01 21:19:30 +01:00
Irmen de Jong b73c958c4a ir immediate syntax fixes 2025-11-01 16:55:00 +01:00
Irmen de Jong 2eac457e1c clarify string assignment (or rather, the lack thereof) 2025-11-01 00:40:52 +01:00
Irmen de Jong 815ef7e654 add check for not yet implement long expression comparisons 2025-11-01 00:26:36 +01:00
Irmen de Jong 0cd8c4f87e add strings.ncopy and nappend (length limited) 2025-10-31 21:22:15 +01:00
Irmen de Jong b02a8ed954 strings can no longer be assigned by-value. Use strings.copy() instead. Fixes #189 2025-10-31 19:22:40 +01:00
Irmen de Jong b1e07f3fdb better error when trying to use a const pointer (which is not supported yet) 2025-10-30 20:58:21 +01:00
Irmen de Jong fc8727f81e allow pointer to pointer assignment in cast codegen, fixes #191 2025-10-30 19:35:44 +01:00
Irmen de Jong ec728bad52 fix certain float array assignment, fixes #193 2025-10-30 01:45:25 +01:00
Irmen de Jong a5e827e40f implement some more long array operations 2025-10-30 01:20:59 +01:00
Irmen de Jong 34061c5a63 implement inplace bitwise operations on long struct fields 2025-10-28 23:44:34 +01:00
Irmen de Jong 9c2bcab4a5 fix more ptr/long issues 2025-10-28 23:08:12 +01:00
Irmen de Jong 2f07f41664 detect self-referencing aliases 2025-10-23 21:24:14 +02:00
Irmen de Jong 7027304597 put some upper bounds on more internal compiler loops 2025-10-23 20:37:44 +02:00
Irmen de Jong ee75333891 implement more long assignments through pointers 2025-10-21 22:55:46 +02:00
Irmen de Jong 6a70fb0480 deal with invalid sqrt results (negative argument), allow sqrt of longs (like floats) 2025-10-21 21:01:03 +02:00
Irmen de Jong ebc738b132 'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts 2025-10-20 00:33:40 +02:00
Irmen de Jong e5939be0bd some more long operations implemented 2025-10-19 23:03:17 +02:00
Irmen de Jong 0c0affd1bf cx16 diskio.load_size() now returns full long size. Fixes #184 2025-10-19 21:18:46 +02:00
Irmen de Jong c7158fd968 implement more long operations on struct fields 2025-10-17 01:55:11 +02:00
Irmen de Jong de2f3f0d0d tweak TODO messages for longs 2025-10-16 23:20:34 +02:00
Irmen de Jong c0286e3349 added txt.size() to return text screen width and height in 1 call. Also fixed txt.width/height on the C128 (where SCREEN reports 1 less for them...) 2025-10-16 00:33:14 +02:00
Irmen de Jong 513ee25432 beta5 2025-10-15 18:29:36 +02:00
Irmen de Jong be74290ddc many more TODO messages now log proper source positions 2025-10-15 17:58:40 +02:00
Irmen de Jong a36501a9ed fix long bitwise expressions temp register usage 2025-10-14 02:57:48 +02:00
Irmen de Jong ee5d33a230 fix long if-expressions temp register usage 2025-10-14 02:16:42 +02:00
Irmen de Jong a8fd66cfdb fix long==0 long!=0 register usage 2025-10-14 01:49:42 +02:00
Irmen de Jong f5ce744748 undo the math crc param optimization that now causes the footgun warning when you import math. + doc fix about arithmetic expressions 2025-10-14 00:31:29 +02:00
Irmen de Jong 68066acdec changed (and fixed) msb(long) and lsb(long) 2025-10-13 21:34:03 +02:00
Irmen de Jong 6286035d89 support long +/- number in expressions 2025-10-13 02:50:48 +02:00
Irmen de Jong 9a37eb9146 save A and Y when storing longs 2025-10-13 02:12:07 +02:00
Irmen de Jong 307796f115 fixed some bugs in optimizing long values 2025-10-12 23:15:42 +02:00
Irmen de Jong e962431139 added bcd.addtol()/subfroml() for in-place long calculations
fix long overflow messages, IR register type error
2025-10-12 22:08:10 +02:00
Irmen de Jong b7620abc9a longs: fix msw() and lsw() 2025-10-12 19:49:36 +02:00
Irmen de Jong d2719d23f5 C128: adjust MEMTOP value to $C000 during runtime (41 Kb of available space because Basic ROM is banked out) 2025-10-12 15:02:36 +02:00
Irmen de Jong d69eead6d8 fix abs(long), mklong(), mklong2(), peekl(), pokel() 2025-10-12 14:17:27 +02:00
Irmen de Jong 6db3611d93 fix typecast error when assigning pointer to long 2025-10-12 12:44:28 +02:00
Irmen de Jong a84320c7e2 add sgn(long) support, fix sgn() result type in IR 2025-10-12 12:22:32 +02:00
Irmen de Jong dfc720557c fix some more long hex value comparisons 2025-10-12 04:40:50 +02:00
Irmen de Jong 32149d073a bitwise long exprs now use R12-R13 instead of R2-R3 2025-10-12 03:02:09 +02:00
Irmen de Jong fc38be6376 fix a const long value hex asm mistake, fix conv.str_l(0) empty output 2025-10-12 01:52:35 +02:00
Irmen de Jong 13f6efc1d1 long +/- long now use R12-R15 instead of R0-R3 2025-10-12 01:35:02 +02:00
Irmen de Jong 8296002887 fix combined reg asm name 2025-10-12 01:34:00 +02:00
Irmen de Jong 424b89f357 longs now returned in R14+R15 instead of R0+R1 2025-10-12 00:27:17 +02:00
Irmen de Jong 4f5590fbff doc 2025-10-11 21:37:59 +02:00
Irmen de Jong 598e70c49a allow negative values on poke and pokew without an explicit cast 2025-10-11 16:39:36 +02:00
Irmen de Jong a6d051b496 update syntax files 2025-10-11 16:15:34 +02:00
Irmen de Jong 796d3242e1 optimize word pointer indexing and fix long pointer indexing assignment (pokel) 2025-10-11 15:59:27 +02:00
Irmen de Jong 1e8ff6f82a warning in doc about problems when using long + R0/R1 together 2025-10-11 15:36:57 +02:00
Irmen de Jong 07bb5c36bd avoid fallback JSR peek for common case pointer+offset 2025-10-11 04:45:03 +02:00
Irmen de Jong 7e26ecb0b6 allow 'then' in if-expressions to separate condition and result value 2025-10-10 23:26:53 +02:00
Irmen de Jong 0c59ad70d4 fix aliased pointer deref problem 2025-10-10 22:13:24 +02:00
Irmen de Jong 53ce688cc5 fix pointer type check for R0-R15 subroutine arg 2025-10-10 00:54:52 +02:00
Irmen de Jong bb8b44d9d0 fix missing byte to long assignment, add c-bench-64 comparison 2025-10-10 00:01:54 +02:00
Irmen de Jong 51ae32d23e fix up todo 2025-10-09 21:14:06 +02:00
markjreed c873fac0dc feat: point doc source links to matching version (#179)
* feat: point doc source links to matching version

* fix: formatting, remove debug
2025-10-09 20:03:46 +02:00
Irmen de Jong f0a67fff8a added missing byte to long type assignment 2025-10-09 02:52:33 +02:00
Irmen de Jong f85ccd837d tweak conv.str_l() so that it properly reuses the conv.string_out buffer 2025-10-08 22:33:03 +02:00
Irmen de Jong 396fcbc927 implement missing long typecasts 2025-10-07 17:46:10 +02:00
Irmen de Jong 0f564b301d implement peekl() and pokel() 2025-10-07 00:53:00 +02:00
Irmen de Jong f4f34fc2ed added symboltable into CompilationResult because it might be useful to inspect in tests 2025-10-07 00:26:43 +02:00
Irmen de Jong f7639cb78f implement long comparisons > and <= 2025-10-06 23:42:40 +02:00
Irmen de Jong f5f5aef722 implement long comparisons < and >= 2025-10-06 22:30:17 +02:00
Irmen de Jong d6e30d8468 fix signed word and long cmp() 2025-10-06 22:05:34 +02:00
Irmen de Jong 37afdf5a18 tweak antlr grammar to improve parsing performance 2025-10-06 01:28:33 +02:00
Irmen de Jong 5eb7074172 fix gfx_lores and gfx_hires disc() inaccurracy 2025-10-05 23:21:19 +02:00
Irmen de Jong 9aff280d10 fix monogfx.disc() inaccurracy 2025-10-05 23:03:53 +02:00
Irmen de Jong 44741a8e32 fix c64 graphics.disc() inaccurracy 2025-10-05 22:57:39 +02:00
Irmen de Jong 37535f2913 optimize charfade example, added textspotlight example 2025-10-05 21:18:47 +02:00
Irmen de Jong ec9475c308 use pointer arithmetic instead of raw peeks and pokes 2025-10-05 18:38:01 +02:00
Irmen de Jong b961ce97d6 add -libdump and -libsearch compiler options 2025-10-05 17:07:55 +02:00
Irmen de Jong 4ed92d71a7 remove "@split" tag
The default is to split word arrays. If you need your word array to not be split, use @nosplit on the array.
2025-10-05 17:06:21 +02:00
Irmen de Jong 3e1386a987 remove the -dontsplitarrays compiler option
it was still there for backward compatibility reasons with really old prog8 code. If you need a word array to be not split, just use @nosplit on the array.
2025-10-05 14:44:29 +02:00
Irmen de Jong 4195e3968a remove needless library module imports in unit tests 2025-10-05 13:10:02 +02:00
Irmen de Jong 961095bfec fix output path for shadow jar 2025-10-04 23:31:52 +02:00
198 changed files with 7694 additions and 3624 deletions

No files matched your search

+10 -10
View File
@@ -1,23 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21-sources.jar!/" />
</SOURCES>
</library>
</component>
+16 -4
View File
@@ -34,8 +34,11 @@ How to get it/build it
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
Note that if you are not using *gradle* to build it, you might have to perform some manual
tasks once to make it compile fully. These are explained in the linked instructions.
- Alternatively, you can also install the compiler as a package on some linux distros:
- Alternatively, you can also install the compiler as a package on some linux distros (which will take care of dependencies automatically):
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
- Finally there's a Homebrew recipe for Mac OS (but also for Linux, and WSL2 on Windows, this also takes care of dependencies automatically):
``brew install prog8``
Community
---------
@@ -58,9 +61,9 @@ What does Prog8 provide?
- all advantages of a higher level language over having to write assembly code manually
- programs run very fast because it's compiled to native machine code
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- compiled code is very small; much smaller than equivalent C code compiled with CC65, and usually runs faster as well
- modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings)
- various data types other than just bytes (16-bit words, long integers, floats, strings)
- Structs and typed pointers
- floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally
@@ -82,7 +85,7 @@ What does Prog8 provide?
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
- 50 Kb of available program RAM size on the C64 by default (41 Kb on the C128) because Basic ROM is banked out by default
*Rapid edit-compile-run-debug cycle:*
@@ -193,3 +196,12 @@ For instance here's a well known space ship animated in 3D with hidden line remo
in the CommanderX16 emulator:
![cobra3d](docs/source/_static/cobra3d.png)
Performance comparison with various C compilers
-----------------------------------------------
Here is a performance comparison with various C compilers for the 6502/C64 so you can get
an idea of how Prog8 stacks up.
[comparison](benchmark-c/benchmark.md)
+39
View File
@@ -0,0 +1,39 @@
# C-Bench-64 comparison
Several benchmarks of https://thred.github.io/c-bench-64/
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
* Prog8 program size is consistently one of the smallest.
* Prog8 execution speed places it more or less in the middle of the stack.
Measured with Prog8 V12.0 .
Benchmarks ran on a PAL C64 emulated through VICE.
textual results:
| benchmark | size | time |
|-----------|------|----------|
| crc8 | 1568 | 2.0 sec |
| crc16 | 1608 | 2.5 sec |
| crc32 | 1812 | 4.2 sec |
| pow | 2152 | 11.7 sec |
| sieve | 1772 | 22.1 sec |
| sieve_bit | 1918 | 40.1 sec |
![crc8](result-crc8.png)
![crc16](result-crc16.png)
![crc32](result-crc32.png)
![pow](result-pow.png)
![sieve](result-sieve.png)
![sieve-bit](result-sieve-bit.png)
+98
View File
@@ -0,0 +1,98 @@
%import textio
cia {
ubyte freq
const ubyte CNT = 200
sub calibrate() {
txt.print("calibrating frequency: ")
tod_init(0)
txt.print_ub(tod_freq())
txt.print(" hz\n")
tod_reset()
}
sub print_time() {
uword t = tod_get10()
txt.print("(cia) time: ")
txt.print_uw(t / 10)
txt.chrout('.')
txt.print_uw(t % 10)
txt.print(" sec.\n")
}
sub tod_reset() {
; set the tod to 0
c64.CIA2TODHR = 0
c64.CIA2TODMIN = 0
c64.CIA2TODSEC = 0
c64.CIA2TOD10 = 0
}
sub tod_get10() -> uword {
ubyte h, m, s, t
uword time
h = c64.CIA2TODHR
m = c64.CIA2TODMIN
s = c64.CIA2TODSEC
t = c64.CIA2TOD10
time = t
time += (s & $0f) * (10 as uword)
time += (s >> 4) * (100 as uword)
time += (m & $0f) * 600
time += (m >> 4) * 6000
return time
}
sub tod_init(ubyte f) {
if (f == 0)
freq = tod_detect_freq()
else
freq = f
if (freq == 50)
c64.CIA2CRA |= $80
else
c64.CIA2CRA &= $7f
}
sub tod_freq() -> ubyte {
return freq
}
sub tod_detect_freq() -> ubyte {
uword cbl
c64.CIA2CRB = $40 ; stop timer
c64.CIA2CRA = $80 ; stop timer
; set ta to overflow every 10000 count (~= 10ms)
c64.CIA2TAL = $10
c64.CIA2TAH = $27
c64.CIA2TBL = CNT
c64.CIA2TBH = 0
tod_reset()
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
while c64.CIA2TODSEC == 0 {
; wait for tod to count 1s
}
; cal=CIA2.ta_lo;
; cah=CIA2.ta_hi;
cbl = c64.CIA2TBL
; cbh=CIA2.tb_hi;
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
; printf("elapsed ~= %d0ms\n",CNT-cbl);
if CNT - cbl > 90
return 50
else
return 60
}
}
+71
View File
@@ -0,0 +1,71 @@
%import textio
%import ciatimer
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
const uword EXPECTED = $ffd0
uword crc_result
sub benchmark_name()
{
txt.print("crc16.p8\n")
txt.print("Calculates the CRC16 of the C64 Kernal\n")
}
sub benchmark()
{
crc_result = CRC16($e000, $2000)
}
sub benchmark_check() -> bool
{
txt.print("CRC=")
txt.print_uwhex(crc_result, true)
if crc_result == EXPECTED
{
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uwhex(EXPECTED, true)
txt.nl()
return true
}
sub CRC16(^^ubyte data, uword length) -> uword {
; CRC-16/XMODEM
uword crc
repeat length
{
crc ^= mkword(@(data),0) ; currently there's no easy way to affect only the MSB of the variable
repeat 8
{
crc <<=1
if_cs
crc ^= $1021
}
data++
}
return crc
}
}
+72
View File
@@ -0,0 +1,72 @@
%import textio
%import ciatimer
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
const long EXPECTED = $e1fa84c6
long crc_result
sub benchmark_name()
{
txt.print("crc32.p8\n")
txt.print("Calculates the CRC32 of the C64 Kernal\n")
}
sub benchmark()
{
crc_result = CRC32($e000, $2000)
}
sub benchmark_check() -> bool
{
txt.print("CRC=")
txt.print_ulhex(crc_result, true)
if crc_result == EXPECTED
{
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ulhex(EXPECTED, true)
txt.nl()
return true
}
sub CRC32(^^ubyte data, uword length) -> long {
; CRC-32/CKSUM
long crc
repeat length
{
crc ^= (@(data) as long)<<24 ; currently there's no easy way to affect only the MSB of the variable
repeat 8
{
crc <<=1
if_cs
crc ^= $04c11db7
}
data++
}
crc ^= $ffffffff
return crc
}
}
+72
View File
@@ -0,0 +1,72 @@
%import textio
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
sub benchmark_name()
{
txt.print("crc8.p8\n")
txt.print("Calculates the CRC8 of the C64 Kernal\n")
}
sub benchmark()
{
crc_result = CRC8($e000, $2000)
}
sub benchmark_check() -> bool
{
txt.print("CRC=")
txt.print_ubhex(crc_result, true)
if crc_result == EXPECTED
{
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ubhex(EXPECTED, true)
txt.nl()
return true
}
const ubyte EXPECTED = $a2
ubyte crc_result
sub CRC8(^^ubyte data, uword length) -> ubyte
{
; CRC-8/GSM-A
ubyte crc
repeat length
{
crc ^= @(data)
repeat 8
{
if (crc & $80) != 0
crc = (crc << 1) ^ $1d
else
crc <<= 1
}
data++
}
return crc
}
}
+93
View File
@@ -0,0 +1,93 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
const ubyte N_ITER = 10
const ubyte SIZE = 32
float[SIZE] array
const float expected = 3614007361536.000000
const float epsilon = 10000000
float res
sub benchmark_name()
{
txt.print("pow.p8\n")
txt.print("Calculates floating point exponential (")
txt.print_uw(N_ITER)
txt.print(" iterations)\n")
}
sub benchmark()
{
ubyte i,j
res = 0
for j in 0 to SIZE-1 {
array[j]=0
}
for i in 0 to N_ITER-1 {
for j in 0 to SIZE-1 {
array[j] += testpow(2.5 / ((i + 1) as float), j)
}
}
for j in 0 to SIZE-1 {
res += array[j]
}
}
sub testpow(float x, ubyte y) -> float
{
float tmp = x
if y == 0
return 1
repeat y-1
tmp *= x
return tmp
}
sub benchmark_check() -> bool
{
txt.print("res = ")
txt.print_f(res)
float diff = expected - res;
txt.print("\nexpected = ")
txt.print_f(expected)
txt.print("\nepsilon = ")
txt.print_f(epsilon)
txt.print("\ndiff = ")
txt.print_f(diff)
if (diff < epsilon and diff > -epsilon)
{
txt.print(" [OK]\n")
return false
}
txt.print("[FAIL]\n")
return true
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

+85
View File
@@ -0,0 +1,85 @@
%import textio
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
const ubyte N_ITER = 10
const uword SIZE = 8191
const uword EXPECTED = 1900
uword prime_count
^^bool @zp flags = memory("flags", SIZE, 0)
sub benchmark_name()
{
txt.print("sieve.c\n")
txt.print("Calculates the primes from 1 to ")
txt.print_uw(SIZE * 2 + 2)
txt.print(" (")
txt.print_ub(N_ITER)
txt.print(" iterations)\n")
}
sub benchmark()
{
repeat N_ITER
prime_count = sieve(SIZE)
}
sub benchmark_check() -> bool
{
txt.print("count=")
txt.print_uw(prime_count)
if prime_count == EXPECTED
{
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
sub sieve(uword size) -> uword
{
uword i, prime, k
uword count = 1
for i in 0 to size-1
flags[i] = true
for i in 0 to size-1
{
if flags[i]
{
prime = i + i + 3
k = i + prime
while k < size
{
flags[k] = false
k += prime
}
count++
}
}
return count
}
}
+107
View File
@@ -0,0 +1,107 @@
%import textio
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
test.benchmark()
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
test {
const ubyte N_ITER = 4
const uword SIZE = 16000
const uword EXPECTED = 3432
uword prime_count
^^ubyte @zp flags = memory("flags", SIZE/8+1, 0)
sub benchmark_name()
{
txt.print("sieve_bit.p8\n")
txt.print("Calculates the primes from 1 to ")
txt.print_uw(SIZE * 2 + 2)
txt.print(" (")
txt.print_ub(N_ITER)
txt.print(" iterations)\n")
}
sub benchmark()
{
repeat N_ITER
prime_count = sieve(SIZE)
}
sub benchmark_check() -> bool
{
txt.print("count=")
txt.print_uw(prime_count)
if prime_count == EXPECTED
{
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
ubyte[] bitv = [
$01,
$02,
$04,
$08,
$10,
$20,
$40,
$80
]
sub check_flag(uword idx) -> bool
{
return flags[idx / 8] & bitv[lsb(idx) & 7] != 0
}
sub clear_flag(uword idx)
{
flags[idx / 8] &= ~(bitv[lsb(idx) & 7])
}
sub sieve(uword size) -> uword
{
uword i, prime, k
uword count=1
for i in 0 to (size / 8)
flags[i] = $ff
for i in 0 to size-1
{
if check_flag(i)
{
prime = i + i + 3
k = i + prime;
while k < size
{
clear_flag(k)
k += prime
}
count++
}
}
return count
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ adpcm {
while cbm.RDTIM16()<max_time {
adpcm.init(0,0)
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
repeat 252/2 {
unroll 2 {
ubyte @zp nibble = @(nibbles_ptr)
+5 -5
View File
@@ -472,7 +472,7 @@ elite_galaxy {
generate_next_planet()
textelite.num_commands++
}
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
init_market_for_planet()
}
@@ -494,7 +494,7 @@ elite_galaxy {
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
if elite_util.prefix_matches(nameptr, elite_planet.name) {
ubyte distance = elite_planet.distance(x, y)
if distance < current_distance {
@@ -532,7 +532,7 @@ elite_galaxy {
; else
; txt.chrout('-')
; txt.spc()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.display(true, distance)
}
pn++
@@ -548,7 +548,7 @@ elite_galaxy {
str current_name = " " ; 8 max
ubyte pn = 0
current_name = elite_planet.name
void strings.copy(elite_planet.name, current_name)
init(number)
; txt.clear_screen()
; txt.print("Galaxy #")
@@ -569,7 +569,7 @@ elite_galaxy {
generate_next_planet()
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
+3 -3
View File
@@ -35,7 +35,7 @@ main {
txt.color2(1, 6)
txt.clear_screen()
txt.print("\n\n\n prog8 compiler benchmark tests.\n")
txt.print("\n\n\n prog8 compiler benchmark tests.\n\n benchmark version v12\n\n")
sys.wait(60)
benchmark_number = 0
@@ -65,7 +65,7 @@ main {
benchmark_number++
announce_benchmark("circles with gfx_lores")
benchmark_score[benchmark_number] = circles.draw(false, 300)
benchmark_score[benchmark_number] = circles.draw(false, 400)
benchmark_number++
announce_benchmark("text-elite")
@@ -88,7 +88,7 @@ main {
txt.color2(1, 6)
uword total_score
benchmark_number = 0
txt.print("\nscore benchmark\n\n")
txt.print("\nscore benchmark (v12)\n\n")
do {
txt.spc()
txt.print_uw(benchmark_score[benchmark_number])
+1 -1
View File
@@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins {
kotlin("jvm") version "2.2.20"
kotlin("jvm") version "2.2.21"
}
allprojects {
@@ -37,7 +37,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
val returns: ReturnConvention = when (returnType) {
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R0R1_32)
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32)
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
null -> ReturnConvention(null, null)
@@ -46,7 +46,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
when (val paramType = actualParamTypes.first()) {
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R0R1_32)
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R14R15_32)
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
else -> ReturnConvention(paramType, null)
@@ -63,7 +63,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
val paramConv = when(val paramType = actualParamTypes[0]) {
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.LONG -> ParamConvention(paramType, RegisterOrPair.R0R1_32, false)
BaseDataType.LONG -> ParamConvention(paramType, RegisterOrPair.R14R15_32, false)
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
@@ -95,7 +95,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns result in the cpu status flags, but not asa proper return value
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
@@ -112,13 +112,16 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__long" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.LONG)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"lsb__long" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)),
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msb__long" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.LONG)),
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
"mklong" to FSignature(true, BaseDataType.LONG, FParam("msb", BaseDataType.UBYTE), FParam("b2", BaseDataType.UBYTE), FParam("b1", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
@@ -146,10 +149,10 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE, BaseDataType.BYTE)),
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD, BaseDataType.WORD)),
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
@@ -29,7 +29,6 @@ class CompilationOptions(val output: OutputType,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var dontSplitWordArrays: Boolean = false,
var breakpointCpuInstruction: String? = null,
var ignoreFootguns: Boolean = false,
var outputDir: Path = Path(""),
+5 -2
View File
@@ -13,9 +13,12 @@ fun Number.toHex(): String {
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
val integer = this.toLong()
if(integer<0) {
if(integer==-2147483648L)
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
return '-' + abs(integer).toHex()
}
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
+1 -2
View File
@@ -440,7 +440,7 @@ enum class RegisterOrPair {
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
BaseDataType.UWORD, BaseDataType.POINTER, null -> ""
else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg")
}
return listOf("cx16", name.lowercase()+suffix)
@@ -530,6 +530,5 @@ enum class ZeropageWish {
enum class SplitWish {
DONTCARE,
SPLIT,
NOSPLIT
}
+9 -1
View File
@@ -6,7 +6,15 @@ import java.nio.file.InvalidPathException
import kotlin.io.path.Path
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
override fun toString(): String = "[$file: line $line col ${startCol}-${endCol}]"
init {
if(!file.startsWith('~') ||!file.endsWith('~'))
require(line>0 && startCol>=0 && endCol>=startCol) {
"Invalid position: $this"
}
}
fun toClickableStr(): String {
if(this===DUMMY)
return ""
@@ -451,7 +451,7 @@ class AsmGen6502Internal (
if(line.length>=4 && invalid.matchesAt(line, 0)) {
errors.err(
"invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}",
Position("<output-assemblycode>", index, 1, 1)
Position("~output-assemblycode~", index+1, 1, 1)
)
}
}
@@ -758,6 +758,15 @@ class AsmGen6502Internal (
CpuRegister.Y -> out(" tay")
}
}
expr.type.isLong -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(" asl a | asl a")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
expr.type.isFloat -> {
if(options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${expr.position}")
@@ -844,7 +853,7 @@ class AsmGen6502Internal (
return
}
TargetStorageKind.POINTER -> {
TODO("assign to pointer ${target.position}")
pointerGen.assignByte(PtrTarget(target), 0)
return
}
else -> { }
@@ -854,7 +863,10 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
}
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
target.datatype.isPointer -> {
assignExpressionToRegister(value, RegisterOrPair.AX)
pointerGen.assignWordReg(PtrTarget(target), RegisterOrPair.AX)
}
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
@@ -866,23 +878,23 @@ class AsmGen6502Internal (
}
target.datatype.isLong -> {
if(value is PtNumber) {
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0')
val hex = value.asConstInteger()!!.toLongHex()
when(target.kind) {
TargetStorageKind.VARIABLE -> {
out("""
lda #${hex.substring(6,8)}
lda #$${hex.substring(6,8)}
sta ${target.asmVarname}
lda #${hex.substring(4, 6)}
lda #$${hex.substring(4, 6)}
sta ${target.asmVarname}+1
lda #${hex.substring(2, 4)}
lda #$${hex.substring(2, 4)}
sta ${target.asmVarname}+2
lda #${hex.take(2)}
lda #$${hex.take(2)}
sta ${target.asmVarname}+3""")
}
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> pointerGen.assignLong(target.pointer!!, value.number.toInt())
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtIdentifier && value.type.isLong) {
@@ -904,8 +916,8 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> pointerGen.assignLongVar(target.pointer!!, asmSymbolName(value))
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtTypeCast && value.type.isLong) {
@@ -917,10 +929,10 @@ class AsmGen6502Internal (
out(" lda $valuesym | sta ${target.asmVarname}")
signExtendLongVariable(target.asmVarname, value.value.type.base)
}
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value.value.type.isWord) {
@@ -933,11 +945,11 @@ class AsmGen6502Internal (
sta ${target.asmVarname}+1""")
signExtendLongVariable(target.asmVarname, value.value.type.base)
}
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val startreg = target.register.toString().take(2).lowercase()
val startreg = target.register!!.startregname()
out("""
lda $valuesym
sta cx16.$startreg
@@ -945,15 +957,19 @@ class AsmGen6502Internal (
sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base)
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else throw AssemblyError("weird casted type")
} else {
TODO("assign typecasted expression $value to a long target ${target.position} - use simple expressions and temporary variables for now")
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
} else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else {
TODO("assign long expression $value to a target ${target.position} - use simple expressions and temporary variables for now")
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
}
target.datatype.isFloat -> {
@@ -1261,12 +1277,12 @@ $repeatLabel""")
// print a message when more optimal code is possible for 65C02 cpu
val variable = symbolTable.lookup(arrayVariable.name)!!
if(variable is StStaticVariable && variable.length!!<=128u)
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
errors.info("the jump address array is split, but @nosplit would create more efficient code here", jump.position)
}
} else {
// print a message when more optimal code is possible for 6502 cpu
if(!arrayIdx.splitWords)
errors.info("the jump address array is @nosplit, but @split would create more efficient code here", jump.position)
errors.info("the jump address array is @nosplit, but split would create more efficient code here", jump.position)
}
}
// we can do the address evaluation right now and just use a temporary pointer variable
@@ -1422,7 +1438,10 @@ $repeatLabel""")
sta $asmvar+3""")
}
BaseDataType.UWORD -> {
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
if(isTargetCpu(CpuType.CPU65C02))
out(" stz $asmvar+2 | stz $asmvar+3")
else
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
}
BaseDataType.WORD -> {
out("""
@@ -1512,7 +1531,7 @@ $repeatLabel""")
val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt()
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
TODO("address-of array element ${addrOf.position}")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
@@ -1554,9 +1573,9 @@ $repeatLabel""")
val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt()
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
TODO("address-of array element ${addrOf.position}")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
TODO("read &dereference ${addrOf.position}")
} else {
out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true
@@ -1604,7 +1623,7 @@ $repeatLabel""")
val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt()
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
TODO("address-of array element ${addrOf.position}")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else {
@@ -1637,9 +1656,9 @@ $repeatLabel""")
val constOffset = (ptrAndIndex.second as? PtNumber)?.number?.toInt()
if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf")
TODO("address-of array element ${addrOf.position}")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
TODO("read &dereference ${addrOf.position}")
} else {
out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true
@@ -1984,9 +2003,9 @@ $repeatLabel""")
lda #<$leftName
ldy #>$leftName""")
if(lessOrEquals)
out("jsr floats.vars_lesseq_f")
out(" jsr floats.vars_lesseq_f")
else
out("jsr floats.vars_less_f")
out(" jsr floats.vars_less_f")
}
fun lessf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
@@ -2105,6 +2124,22 @@ $repeatLabel""")
}
}
internal fun loadIndirectLongIntoR14R15(zpPtrVar: String, offset: UByte) {
out("""
ldy #$offset
lda ($zpPtrVar),y
sta cx16.r14
iny
lda ($zpPtrVar),y
sta cx16.r14+1
iny
lda ($zpPtrVar),y
sta cx16.r14+2
iny
lda ($zpPtrVar),y
sta cx16.r14+3""")
}
internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
@@ -2241,7 +2276,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #$offset
@@ -2308,7 +2343,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #0
@@ -2417,6 +2452,70 @@ $repeatLabel""")
}
}
}
internal fun pushLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
// may clobber P8ZP_SCRATCH_B1, X, Y.
if(numberOfLongs==0) return
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
tax
lda cx16.$startregName
pha
lda cx16.$startregName+1
pha
lda cx16.$startregName+2
pha
lda cx16.$startregName+3
pha
txa""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
tax
sty P8ZP_SCRATCH_B1
ldy #0
- lda cx16.$startregName,y
pha
iny
cpy #$numbytes
bne -
ldy P8ZP_SCRATCH_B1
txa""")
}
internal fun popLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
if(numberOfLongs==0) return
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
tax
pla
sta cx16.$startregName+3
pla
sta cx16.$startregName+2
pla
sta cx16.$startregName+1
pla
sta cx16.$startregName
txa""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
tax
sty P8ZP_SCRATCH_B1
ldy #$numbytes-1
- pla
sta cx16.$startregName,y
dey
bpl -
ldy P8ZP_SCRATCH_B1
txa""")
}
}
/**
@@ -2430,4 +2529,8 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
}
internal fun Double.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
internal fun Int.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
@@ -544,7 +544,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
val third = lines[2].value
if(!haslabel(second)) {
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
if ((" jmp" in first || "\tjmp" in first || " bra" in first || "\tbra" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null)
}
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
@@ -30,15 +30,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"msw" -> funcMsw(fcall, resultRegister)
"lsw" -> funcLsw(fcall, resultRegister)
"msb" -> funcMsb(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister)
"msb__long" -> funcMsbLong(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister, false)
"lsb__long" -> funcLsb(fcall, resultRegister, true)
"mkword" -> funcMkword(fcall, resultRegister)
"mklong", "mklong2" -> funcMklong(fcall) // result is in R0:R1
"mklong", "mklong2" -> funcMklong(fcall) // result is in R14:R15
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(fcall, resultRegister)
"min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(fcall, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall)
@@ -351,31 +353,61 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("args for cmp() should have same dt")
} else if(arg1.type.isWord) {
if(arg2.type.isWord) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy ${asmgen.asmVariableName(arg2)}+1
bne +
cmp ${asmgen.asmVariableName(arg2)}
+""")
if(arg1.type.isSigned) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
sec
sbc ${asmgen.asmVariableName(arg2)}
tya
sbc ${asmgen.asmVariableName(arg2)}+1""")
}
is PtBool -> TODO("word compare against bool ${arg2.position}")
is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
sec
sbc #<${arg2.number.toInt()}
tya
sbc #>${arg2.number.toInt()}""")
}
else -> {
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
asmgen.out("""
sec
sbc P8ZP_SCRATCH_W1
tya
sbc P8ZP_SCRATCH_W1+1""")
}
}
is PtBool -> TODO("word compare against bool ${arg2.position}")
is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy #>${arg2.number.toInt()}
bne +
cmp #<${arg2.number.toInt()}
} else {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy ${asmgen.asmVariableName(arg2)}+1
bne +
cmp ${asmgen.asmVariableName(arg2)}
+""")
}
else -> {
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
}
is PtBool -> TODO("word compare against bool ${arg2.position}")
is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy #>${arg2.number.toInt()}
bne +
cmp #<${arg2.number.toInt()}
+""")
}
else -> {
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
}
}
}
} else
@@ -384,54 +416,45 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg2.type.isLong) {
if(arg1 is PtIdentifier && arg2 is PtNumber) {
val var1 = asmgen.asmVariableName(arg1)
val hex = arg2.number.toUInt().toString(16).padStart(8, '0')
val hex = arg2.number.toLongHex()
asmgen.out("""
sec
lda $var1
cmp #${hex.substring(6, 8)}
bne +
sbc #$${hex.substring(6, 8)}
lda $var1+1
cmp #${hex.substring(4, 6)}
bne +
sbc #$${hex.substring(4, 6)}
lda $var1+2
cmp #${hex.substring(2, 4)}
bne +
sbc #$${hex.substring(2, 4)}
lda $var1+3
cmp #${hex.take(2)}
+""")
sbc #$${hex.take(2)}""")
} else if(arg1 is PtIdentifier && arg2 is PtIdentifier) {
val var1 = asmgen.asmVariableName(arg1)
val var2 = asmgen.asmVariableName(arg2)
asmgen.out("""
sec
lda $var1
cmp $var2
bne +
sbc $var2
lda $var1+1
cmp $var2+1
bne +
sbc $var2+1
lda $var1+2
cmp $var2+2
bne +
sbc $var2+2
lda $var1+3
cmp $var2+3
+""")
sbc $var2+3""")
} else {
assignAsmGen.assignExpressionToRegister(arg2, RegisterOrPair.R2R3_32, true)
assignAsmGen.assignExpressionToRegister(arg1, RegisterOrPair.R0R1_32, true)
// cmp() doesn't return a value and as such can't be used in an expression, so no need to save the temp registers' original values
assignAsmGen.assignExpressionToRegister(arg2, RegisterOrPair.R14R15_32, true)
assignAsmGen.assignExpressionToRegister(arg1, RegisterOrPair.R12R13_32, true)
asmgen.out("""
lda cx16.r0
cmp cx16.r2
bne +
lda cx16.r0+1
cmp cx16.r2+1
bne +
lda cx16.r0+2
cmp cx16.r2+2
bne +
lda cx16.r0+3
cmp cx16.r2+3
+""")
sec
lda cx16.r12
sbc cx16.r14
lda cx16.r12+1
sbc cx16.r14+1
lda cx16.r12+2
sbc cx16.r14+2
lda cx16.r12+3
sbc cx16.r14+3""")
}
// TODO: carry flag is not correct......
} else
throw AssemblyError("args for cmp() should have same dt")
}
@@ -480,7 +503,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
}
BaseDataType.LONG -> TODO("sqrt LONG")
BaseDataType.LONG -> TODO("sqrt LONG ${fcall.position}")
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
@@ -515,7 +538,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -534,7 +557,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -554,7 +577,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
sta $variable+3
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -597,7 +620,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -618,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -629,7 +652,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+3 | ror $variable+2 | ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -661,7 +684,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -680,7 +703,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -698,7 +721,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
inc $variable
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -740,7 +763,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -761,7 +784,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -772,7 +795,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1 | rol $variable+2 | rol $variable+3")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -846,7 +869,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
BaseDataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
BaseDataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
BaseDataType.LONG -> TODO("sgn LONG")
BaseDataType.LONG -> asmgen.out(" jsr prog8_lib.func_sign_l_r14r15_into_A") // note: long arg is stored in R14:R15
BaseDataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
@@ -866,8 +889,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
BaseDataType.LONG -> {
asmgen.out(" jsr prog8_lib.abs_l_into_R0R1")
assignAsmGen.assignRegisterLong(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R0R1_32, true, fcall.position, scope, asmgen), RegisterOrPair.R0R1_32)
asmgen.out(" jsr prog8_lib.abs_l_into_R14R15")
assignAsmGen.assignRegisterLong(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R14R15_32, true, fcall.position, scope, asmgen), RegisterOrPair.R14R15_32)
}
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
@@ -946,6 +969,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.restoreRegisterStack(CpuRegister.Y, true)
asmgen.out(" sta ($varname),y")
return
} else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
asmgen.assignExpressionToRegister(addrExpr.right, RegisterOrPair.AY, false)
val ptrName = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
asmgen.out("""
clc
adc $ptrName
sta P8ZP_SCRATCH_W2
tya
adc $ptrName+1
sta P8ZP_SCRATCH_W2+1""")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // TODO *could* overwerite SCRATCH_W2 if it's a compliated expression...
asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W2", 0u, false, false)
return
}
}
else -> { /* fall through */ }
@@ -996,6 +1032,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
iny
sta ($varname),y""")
return
} else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
asmgen.assignExpressionToRegister(addrExpr.right, RegisterOrPair.AY, false)
val ptrName = asmgen.asmVariableName(addrExpr.left as PtIdentifier)
asmgen.out("""
clc
adc $ptrName
sta P8ZP_SCRATCH_W2
tya
adc $ptrName+1
sta P8ZP_SCRATCH_W2+1""")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // TODO *could* overwerite SCRATCH_W2 if it's a compliated expression...
asmgen.out(" jsr prog8_lib.func_pokew_scratchW2")
//asmgen.storeIndirectWordReg(RegisterOrPair.AY, "P8ZP_SCRATCH_W2", 0u)
return
}
}
else -> { /* fall through */ }
@@ -1007,9 +1057,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcPokeL(fcall: PtBuiltinFunctionCall) {
val addrExpr = fcall.args[0]
val value = fcall.args[1]
TODO("pokel $addrExpr, $value")
// TODO optimize for the simple cases
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
// it's a statement so no need to preserve R14:R15
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R14R15_32, true)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out(" jsr prog8_lib.func_pokel")
}
private fun funcPeekF(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
@@ -1046,6 +1102,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(pointer)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.out(" lda ($varname),y")
} else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
readValueFromPointerPlusOffset(addrExpr.left as PtIdentifier, addrExpr.right, BaseDataType.BOOL)
} else fallback()
}
else -> fallback()
@@ -1091,6 +1149,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda ($varname),y
tay
txa""")
} else if(addrExpr.operator=="+" && addrExpr.left is PtIdentifier) {
readValueFromPointerPlusOffset(addrExpr.left as PtIdentifier, addrExpr.right, BaseDataType.UWORD)
} else fallback()
}
else -> fallback()
@@ -1108,10 +1168,33 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun readValueFromPointerPlusOffset(ptr: PtIdentifier, offset: PtExpression, dt: BaseDataType) {
val varname = asmgen.asmVariableName(ptr)
asmgen.assignExpressionToRegister(offset, RegisterOrPair.AY)
asmgen.out("""
clc
adc $varname
sta P8ZP_SCRATCH_W1
tya
adc $varname+1
sta P8ZP_SCRATCH_W1+1""")
if (dt.isByteOrBool) {
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(" lda (P8ZP_SCRATCH_W1)")
} else {
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W1),y")
}
} else if(dt.isWord) {
asmgen.out(" jsr prog8_lib.func_peekw.from_scratchW1")
} else throw AssemblyError("unsupported type for peek $dt")
}
private fun funcPeekL(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val addrExpr = fcall.args[0]
val value = fcall.args[1]
TODO("peekl $addrExpr, $value")
// TODO optimize for the simple cases
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekl")
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R14R15_32, true, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterLong(targetReg, RegisterOrPair.R14R15_32)
}
@@ -1277,7 +1360,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcMklong(fcall: PtBuiltinFunctionCall) {
// result long in R0:R1 (r0=lsw, r1=msw)
// result long in R14:R15 (r14=lsw, r15=msw)
fun isArgRegister(expression: PtExpression, reg: RegisterOrPair): Boolean {
if(expression !is PtIdentifier)
@@ -1287,25 +1370,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(fcall.args.size==2) {
// mklong2(msw, lsw)
if(isArgRegister(fcall.args[0], RegisterOrPair.R0) || isArgRegister(fcall.args[0], RegisterOrPair.R1) ||
isArgRegister(fcall.args[1], RegisterOrPair.R0) || isArgRegister(fcall.args[1], RegisterOrPair.R1)) {
error("cannot use R0 and/or R1 as arguments for mklong2 because the result should go into R0:R1 ${fcall.position}")
if(isArgRegister(fcall.args[0], RegisterOrPair.R14) || isArgRegister(fcall.args[0], RegisterOrPair.R15) ||
isArgRegister(fcall.args[1], RegisterOrPair.R14) || isArgRegister(fcall.args[1], RegisterOrPair.R15)) {
error("cannot use R14 and/or R15 as arguments for mklong2 because the result should go into R0:R1 ${fcall.position}")
} else {
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r1", DataType.UWORD)
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r0", DataType.UWORD)
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r15", DataType.UWORD)
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r14", DataType.UWORD)
}
} else {
// mklong(msb, b2, b1, lsb)
if(isArgRegister(fcall.args[0], RegisterOrPair.R0) || isArgRegister(fcall.args[0], RegisterOrPair.R1) ||
isArgRegister(fcall.args[1], RegisterOrPair.R0) || isArgRegister(fcall.args[1], RegisterOrPair.R1) ||
isArgRegister(fcall.args[2], RegisterOrPair.R0) || isArgRegister(fcall.args[2], RegisterOrPair.R1) ||
isArgRegister(fcall.args[3], RegisterOrPair.R0) || isArgRegister(fcall.args[3], RegisterOrPair.R1)) {
error("cannot use R0 and/or R1 as arguments for mklong because the result should go into R0:R1 ${fcall.position}")
if(isArgRegister(fcall.args[0], RegisterOrPair.R14) || isArgRegister(fcall.args[0], RegisterOrPair.R15) ||
isArgRegister(fcall.args[1], RegisterOrPair.R14) || isArgRegister(fcall.args[1], RegisterOrPair.R15) ||
isArgRegister(fcall.args[2], RegisterOrPair.R14) || isArgRegister(fcall.args[2], RegisterOrPair.R15) ||
isArgRegister(fcall.args[3], RegisterOrPair.R14) || isArgRegister(fcall.args[3], RegisterOrPair.R15)) {
error("cannot use R14 and/or R15 as arguments for mklong because the result should go into R14:R15 ${fcall.position}")
} else {
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r1H", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r1L", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[2], "cx16.r0H", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[3], "cx16.r0L", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r15H", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r15L", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[2], "cx16.r14H", DataType.UBYTE)
assignAsmGen.assignExpressionToVariable(fcall.args[3], "cx16.r14L", DataType.UBYTE)
}
}
}
@@ -1377,6 +1460,39 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun funcMsbLong(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (!arg.type.isLong)
throw AssemblyError("msb__long requires long argument")
if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away")
if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg)
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+3")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+3")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+3")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+3 | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+3 | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+3 | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" lda $sourceName+3 | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName+3 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
in combinedLongRegisters -> {
TODO("msb__long into long register ${fcall.position}")
}
else -> throw AssemblyError("invalid reg")
}
} else {
TODO("msb__long from $arg ${fcall.position}")
}
}
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (!arg.type.isWord)
@@ -1399,6 +1515,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
in combinedLongRegisters -> {
TODO("msb into long register ${fcall.position}")
}
else -> throw AssemblyError("invalid reg")
}
} else {
@@ -1475,15 +1594,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" sty ${reg}L | lda #0 | sta ${reg}H | lda ${reg}L")
}
in combinedLongRegisters -> {
TODO("msb into long register ${fcall.position}")
}
else -> throw AssemblyError("invalid reg")
}
}
}
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, fromLong: Boolean) {
val arg = fcall.args.single()
if (!arg.type.isWord)
throw AssemblyError("lsb required word argument")
if(fromLong) {
if (!arg.type.isLong) throw AssemblyError("lsb__long requires long")
} else {
if (!arg.type.isWord) throw AssemblyError("lsb requires word")
}
if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away")
@@ -1503,6 +1628,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
in combinedLongRegisters -> {
TODO("lsb into long register ${fcall.position}")
}
else -> throw AssemblyError("invalid reg")
}
} else {
@@ -1561,6 +1689,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToRegister(arg, RegisterOrPair.AY)
asmgen.out(" sta ${reg}L | ldy #0 | sty ${reg}H | cmp #0")
}
in combinedLongRegisters -> {
TODO("lsb into long register ${fcall.position}")
}
else -> throw AssemblyError("invalid reg")
}
}
@@ -1585,7 +1716,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
TODO("msw(expression) ${fcall.position} - use a temporary variable for now")
// TODO can't efficiently preserve R14:R15 on the stack because the result of the routine is likely to be in CPU registers,
// which in turn would be clobbered by popping stuff from the stack...
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15_32, true)
when(resultRegister) {
RegisterOrPair.AX -> asmgen.out(" lda cx16.r15 | ldx cx16.r15+1")
null, RegisterOrPair.AY -> asmgen.out(" lda cx16.r15 | ldy cx16.r15+1")
RegisterOrPair.XY -> asmgen.out(" ldx cx16.r15 | ldy cx16.r15+1")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
asmgen.out(" lda cx16.r15 | sta cx16.$regname | lda cx16.r15+1 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
}
}
}
@@ -1608,7 +1751,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
TODO("lsw(expression) ${fcall.position} - use a temporary variable for now")
// TODO can't efficiently preserve R14:R15 on the stack because the result of the routine is likely to be in CPU registers,
// which in turn would be clobbered by popping stuff from the stack...
asmgen.assignExpressionToRegister(arg, RegisterOrPair.R14R15_32, true)
when(resultRegister) {
RegisterOrPair.AX -> asmgen.out(" lda cx16.r14 | ldx cx16.r14+1")
null, RegisterOrPair.AY -> asmgen.out(" lda cx16.r14 | ldy cx16.r14+1")
RegisterOrPair.XY -> asmgen.out(" ldx cx16.r14 | ldy cx16.r14+1")
in Cx16VirtualRegisters -> {
if(resultRegister!=RegisterOrPair.R14) {
val regname = resultRegister.name.lowercase()
asmgen.out(" lda cx16.r14 | sta cx16.$regname | lda cx16.r14+1 | sta cx16.$regname+1")
}
}
else -> throw AssemblyError("invalid reg")
}
}
}
@@ -1655,7 +1812,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
val src = when {
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt==BaseDataType.LONG -> TODO("LONG argument for builtin func")
conv.dt==BaseDataType.LONG -> TODO("LONG argument for builtin func ${call.position}")
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
@@ -279,7 +279,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
// pass argument via a register parameter
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
throw AssemblyError("argument type incompatible ${value.type} vs param ${parameter.value.type} at ${parameter.value.position}")
val paramRegister: RegisterOrStatusflag = when(sub) {
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
@@ -362,12 +362,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType)
return true
if(argType.isBool && paramType.isBool)
return true
// signed/unsigned args vs parameters are considered compatible
if(argType.isByte && paramType.isByte)
return true
if(argType.isWord && paramType.isWord)
return true
// only signed longs and floats are available so these were already covered
// if(argType.isLong && paramType.isLong)
// return true
// if(argType.isFloat && paramType.isFloat)
// return true
// we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa
@@ -594,10 +594,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
return when (condition.operator) {
"==" -> longEqualsValue(condition.left, condition.right, false, jumpAfterIf, stmt)
"!=" -> longEqualsValue(condition.left, condition.right, true, jumpAfterIf, stmt)
"<" -> TODO("long < 0")
"<=" -> TODO("long <= 0")
">" -> TODO("long > 0")
">=" -> TODO("long >= 0")
"<", "<=", ">", ">=" -> compareLongValues(condition.left, condition.operator, condition.right, jumpAfterIf, stmt)
else -> throw AssemblyError("expected comparison operator")
}
}
@@ -945,7 +942,7 @@ _jump jmp (${target.asmLabel})
asmgen.out(" lda $varname+1")
}
is PtAddressOf -> {
require(!long)
require(!long) {"addresses must still be words not longs"}
if(value.isFromArrayElement) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
@@ -958,9 +955,14 @@ _jump jmp (${target.asmLabel})
}
}
else -> {
require(!long)
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
if(long) {
// note: clobbers R14+R15
asmgen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, true)
asmgen.out(" lda cx16.r14+3")
} else {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
}
}
}
}
@@ -1263,12 +1265,25 @@ _jump jmp (${target.asmLabel})
}
private fun longEqualsZero(value: PtExpression, notEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.R0R1_32, value.type.isSigned)
asmgen.out("""
lda cx16.r0
ora cx16.r0+1
ora cx16.r0+2
ora cx16.r0+3""")
if(value is PtIdentifier) {
val varname = asmgen.asmVariableName(value)
asmgen.out("""
lda $varname
ora $varname+1
ora $varname+2
ora $varname+3""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
ora cx16.r14+2
ora cx16.r14+3
sta P8ZP_SCRATCH_REG""")
asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.out(" lda P8ZP_SCRATCH_REG ; restore flags")
}
if(notEquals) {
if (jump != null)
translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
@@ -1284,7 +1299,11 @@ _jump jmp (${target.asmLabel})
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long <= 0")
loadAndCheck0OrSignIntoCarry(value, true)
if (jump != null)
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1296,7 +1315,11 @@ _jump jmp (${target.asmLabel})
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long >= 0")
loadAndCheck0OrSignIntoCarry(value, false)
if (jump != null)
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1306,6 +1329,28 @@ _jump jmp (${target.asmLabel})
}
}
private fun loadAndCheck0OrSignIntoCarry(value: PtExpression, negative: Boolean) {
if(value is PtIdentifier) {
val varname = asmgen.asmVariableName(value)
asmgen.out("""
lda #<$varname
ldy #>$varname
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
asmgen.out(if(negative) " sec" else " clc")
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
} else {
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
asmgen.out("""
lda #<cx16.r14
ldy #>cx16.r14
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
asmgen.out(if(negative) " sec" else " clc")
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
}
}
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
// special case for (u)word == 0
@@ -1390,71 +1435,75 @@ _jump jmp (${target.asmLabel})
jump: PtJump?,
stmt: PtIfElse
) {
// TODO this can be optimized somewhat more when the left operand is a variable as well
// we only optimize for a const right value for now
val constRight = right.asConstInteger()
val variableRight = (right as? PtIdentifier)?.name
if(constRight!=null) {
asmgen.assignExpressionToRegister(left, RegisterOrPair.R0R1_32, left.type.isSigned)
val hex = constRight.toUInt().toString(16).padStart(8, '0')
asmgen.out("""
lda cx16.r0
cmp #$${hex.substring(6,8)}
bne +
lda cx16.r0+1
cmp #$${hex.substring(4, 6)}
bne +
lda cx16.r0+2
cmp #$${hex.substring(2, 4)}
bne +
lda cx16.r0+3
cmp #$${hex.take(2)}
+""")
} else if(variableRight!=null) {
require(right.type.isLong)
val variableLeft = (left as? PtIdentifier)?.name
if(variableLeft!=null) {
if(left is PtIdentifier) {
val leftvar = asmgen.asmVariableName(left)
if(constRight!=null) {
val hex = constRight.toLongHex()
asmgen.out("""
lda #<$variableLeft
ldy #>$variableLeft
lda $leftvar
cmp #$${hex.substring(6,8)}
bne +
lda $leftvar+1
cmp #$${hex.substring(4, 6)}
bne +
lda $leftvar+2
cmp #$${hex.substring(2, 4)}
bne +
lda $leftvar+3
cmp #$${hex.take(2)}
+""")
} else if(variableRight!=null) {
require(right.type.isLong)
asmgen.out("""
lda #<$leftvar
ldy #>$leftvar
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$variableRight
ldy #>$variableRight
jsr prog8_lib.long_not_equals""")
} else {
asmgen.assignExpressionToRegister(left, RegisterOrPair.R0R1_32, left.type.isSigned)
}
}
else
{
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
if(constRight!=null) {
val hex = constRight.toLongHex()
asmgen.out("""
lda cx16.r0
lda cx16.r14
cmp #$${hex.substring(6,8)}
bne +
lda cx16.r14+1
cmp #$${hex.substring(4, 6)}
bne +
lda cx16.r14+2
cmp #$${hex.substring(2, 4)}
bne +
lda cx16.r14+3
cmp #$${hex.take(2)}
+""")
} else if(variableRight!=null) {
require(right.type.isLong)
asmgen.out("""
lda cx16.r14
cmp $variableRight
bne +
lda cx16.r0+1
lda cx16.r14+1
cmp $variableRight+1
bne +
lda cx16.r0+2
lda cx16.r14+2
cmp $variableRight+2
bne +
lda cx16.r0+3
lda cx16.r14+3
cmp $variableRight+3
+""")
} else {
TODO("long == value expression ${right.position}")
}
} else {
asmgen.assignExpressionToRegister(left, RegisterOrPair.R2R3_32, left.type.isSigned)
asmgen.assignExpressionToRegister(right, RegisterOrPair.R0R1_32, right.type.isSigned)
asmgen.out("""
lda cx16.r0
cmp cx16.r2
bne +
lda cx16.r0+1
cmp cx16.r2+1
bne +
lda cx16.r0+2
cmp cx16.r2+2
bne +
lda cx16.r0+3
cmp cx16.r2+3
+""")
}
if(notEquals) {
@@ -1470,6 +1519,50 @@ _jump jmp (${target.asmLabel})
}
}
private fun compareLongValues(
left: PtExpression,
operator: String,
right: PtExpression,
jump: PtJump?,
stmt: PtIfElse
) {
// this comparison is not part of an expression but part of an if statement, there's no need to save the previous values of the temp registers
if(operator=="<" || operator ==">=") {
assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R14R15_32, left.type.isSigned)
assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R12R13_32, left.type.isSigned)
} else {
// flip operands
assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R12R13_32, left.type.isSigned)
}
asmgen.out("""
sec
lda cx16.r12
sbc cx16.r14
lda cx16.r12+1
sbc cx16.r14+1
lda cx16.r12+2
sbc cx16.r14+2
lda cx16.r12+3
sbc cx16.r14+3""")
when(operator) {
"<", ">" -> {
if (jump != null)
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
else
translateIfElseBodies("bpl", stmt)
}
">=", "<=" -> {
if (jump != null)
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
else
translateIfElseBodies("bmi", stmt)
}
else -> throw RuntimeException("invalid operator $operator")
}
}
private fun wordEqualsValue(
left: PtExpression,
right: PtExpression,
@@ -1876,7 +1969,7 @@ _jump jmp (${target.asmLabel})
if(notEquals)
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
else
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
translateAYEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
}
}
}
@@ -2056,4 +2149,5 @@ _jump jmp (${target.asmLabel})
else -> throw AssemblyError("expected comparison operator")
}
}
}
}
@@ -15,40 +15,27 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord || expr.type.isString -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isLong -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.R0R1_32, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.R0R1_32, true)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterLong(target, RegisterOrPair.R0R1_32)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
if(expr.type.isByteOrBool) {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
} else if(expr.type.isFloat) {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
} else {
asmgen.assignExpressionTo(expr.truevalue, target)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionTo(expr.falsevalue, target)
asmgen.out(endLabel)
}
}
@@ -381,36 +368,37 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
private fun translateLongExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if L==number
// TODO reuse code from ifElse?
val hex = number.toUInt().toString(16).padStart(8, '0')
val hex = number.toLongHex()
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #${hex.substring(6, 8)}
cmp #$${hex.substring(6, 8)}
bne $falseLabel
lda $varname+1
cmp #${hex.substring(4, 6)}
cmp #$${hex.substring(4, 6)}
bne $falseLabel
lda $varname+2
cmp #${hex.substring(2, 4)}
cmp #$${hex.substring(2, 4)}
bne $falseLabel
lda $varname+3
cmp #${hex.take(2)}
cmp #$${hex.take(2)}
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r0
cmp #${hex.substring(6, 8)}
lda cx16.r14
cmp #$${hex.substring(6, 8)}
bne $falseLabel
lda cx16.r0+1
cmp #${hex.substring(4, 6)}
lda cx16.r14+1
cmp #$${hex.substring(4, 6)}
bne $falseLabel
lda cx16.r0+2
cmp #${hex.substring(2, 4)}
lda cx16.r14+2
cmp #$${hex.substring(2, 4)}
bne $falseLabel
lda cx16.r0+3
cmp #${hex.take(2)}
lda cx16.r14+3
cmp #$${hex.take(2)}
bne $falseLabel""")
}
}
@@ -435,18 +423,19 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
cmp $varname2+3
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r0
lda cx16.r14
cmp $varname2
bne $falseLabel
lda cx16.r0+1
lda cx16.r14+1
cmp $varname2+1
bne $falseLabel
lda cx16.r0+2
lda cx16.r14+2
cmp $varname2+2
bne $falseLabel
lda cx16.r0+3
lda cx16.r14+3
cmp $varname2+3
bne $falseLabel""")
}
@@ -503,13 +492,16 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+3
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r0
ora cx16.r0+1
ora cx16.r0+2
ora cx16.r0+3
bne $falseLabel""")
lda cx16.r14
ora cx16.r14+1
ora cx16.r14+2
ora cx16.r14+3
sta P8ZP_SCRATCH_REG""")
asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.out(" lda P8ZP_SCRATCH_REG | bne $falseLabel")
}
}
@@ -525,13 +517,16 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+3
beq $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r0
ora cx16.r0+1
ora cx16.r0+2
ora cx16.r0+3
beq $falseLabel""")
lda cx16.r14
ora cx16.r14+1
ora cx16.r14+2
ora cx16.r14+3
sta P8ZP_SCRATCH_REG""")
asmgen.popLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.out(" lda P8ZP_SCRATCH_REG | beq $falseLabel")
}
}
@@ -412,10 +412,9 @@ internal class ProgramAndVarsGen(
asmgen.out("; struct types")
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
symboltable.allStructTypes().distinctBy { it.name }.forEach { structtype ->
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
asmgen.out("${it.structName} .struct $structargs\n")
asmgen.out("${structtype.scopedNameString} .struct $structargs\n")
structtype.fields.withIndex().forEach { (index, field) ->
val dt = field.first
val varname = "f${index}"
@@ -425,7 +424,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .endstruct\n")
}
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
val (instancesNoInit, instances) = symboltable.allStructInstances().partition { it.initialValues.isEmpty() }
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
asmgen.out(" .section BSS\n")
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
@@ -791,7 +790,7 @@ internal class ProgramAndVarsGen(
}
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
dt.isPointerArray -> {
TODO("pointers are not supported yet")
TODO("pointers are not supported yet for uninitialized array ${variable.astNode?.position}")
}
else -> {
throw AssemblyError("weird dt")
@@ -938,7 +937,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.sortedBy { it.name }.forEach {
if(it.dt==BaseDataType.FLOAT)
if(it.dt.isFloat)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.name} = ${it.value.toHex()}")
@@ -1044,7 +1043,7 @@ internal class ProgramAndVarsGen(
}
dt.isLongArray -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
val hexnum = number.absoluteValue.toLongHex()
if(number>=0)
"$$hexnum"
else
@@ -185,7 +185,7 @@ internal class AnyExprAsmGen(
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
else -> TODO("float expression operator ${expr.operator}")
else -> TODO("float expression operator ${expr.operator} ${expr.position}")
}
}
@@ -43,7 +43,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
variableAsmName!!
else {
if(array.variable==null)
TODO("asmVarname for array with pointer")
TODO("asmVarname for array with pointer $position")
asmgen.asmVariableName(array.variable!!)
}
}
@@ -121,7 +121,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
in combinedLongRegisters -> {
val dt = if(signed) DataType.LONG
else
TODO("unsigned long")
TODO("unsigned long $pos")
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
else -> throw AssemblyError("weird register $registers")
File diff suppressed because it is too large. Load diff
@@ -4,6 +4,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
@@ -112,9 +113,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
SourceStorageKind.EXPRESSION -> inplacemodificationLongWithExpression(target.asmVarname, operator, value.expression!!)
SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(target.asmVarname, operator, regName(value))
SourceStorageKind.ARRAY -> TODO("inplace modify long with array value ${target.position}")
SourceStorageKind.MEMORY -> TODO("inplace modify long with memread value ${target.position}")
}
}
target.datatype.isFloat -> {
@@ -123,7 +124,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -163,18 +164,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
is PtIdentifier -> {
val pointer = memory.address as PtIdentifier
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into pointer")
SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
SourceStorageKind.LITERALBOOLEAN -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
SourceStorageKind.REGISTER -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
SourceStorageKind.MEMORY -> ptrgen. inplacemodificationBytePointerWithValue(pointer, operator, value.memory!!)
SourceStorageKind.ARRAY -> ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
} else {
inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
}
}
}
@@ -308,14 +309,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(targetVarName, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(targetVarName, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("inplace long modifiication ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace long modifiication ${target.position}")
SourceStorageKind.MEMORY -> TODO("inplace long modification with memread value ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace long modification with array value ${target.position}")
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
TODO("inplace long modifiication ${target.position}")
TODO("inplace long modification ${target.position}")
} else {
TODO("inplace long modifiication ${target.position}")
TODO("inplace long modification ${target.position}")
}
}
}
@@ -327,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float array")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(targetVarName, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(targetVarName, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -518,7 +519,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(tempvar, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(tempvar, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -554,8 +555,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R0R1_32, value.type.isSigned)
inplacemodificationLongWithVariable(targetVar, operator, "cx16.r0")
// it's not an expression so no need to preserve R14-R15
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
inplacemodificationLongWithVariable(targetVar, operator, "cx16.r14")
}
internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
@@ -709,13 +711,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $variable
sta $variable+1
lda #0
sta $variable
sta $variable""")
if(shift==1) {
asmgen.out("""
asl $variable+1
rol $variable+2
rol $variable+3""")
} else {
asmgen.out("""
ldy #$shift
- asl $variable+1
rol $variable+2
rol $variable+3
dey
bne -""")
}
}
value == 16 -> {
asmgen.out("""
@@ -736,12 +746,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+2
lda #0
sta $variable
sta $variable+1
sta $variable+1""")
if(shift==1) {
asmgen.out(" asl $variable+2 | rol $variable+3")
} else {
asmgen.out("""
ldy #$shift
- asl $variable+2
rol $variable+3
dey
bne -""")
}
}
value == 24 -> {
asmgen.out("""
@@ -754,12 +769,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
value <= 31 -> {
val shift = value-24
asmgen.out("""
if(shift==1) {
asmgen.out(" lda $variable | asl a")
} else {
asmgen.out("""
lda $variable
ldy #$shift
- asl a
dey
bne -
bne -""")
}
asmgen.out("""
sta $variable+3
lda #0
sta $variable
@@ -858,7 +878,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
inc $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
clc
lda $variable
@@ -883,44 +903,34 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
1 -> {
asmgen.out("""
lda $variable
bne +
dec $variable+1
bne +
dec $variable+2
bne +++
lda $variable+1
bne ++
lda $variable+2
bne +
dec $variable+3
+ dec $variable+2
+ dec $variable+1
+ dec $variable""")
}
else -> {
if(value in 1..255) {
asmgen.out("""
lda $variable
sec
lda $variable
sbc #$value
sta $variable
bcs +
dec $variable+1
bne +
dec $variable+2
bne +
dec $variable+3
+""")
} else if(value in 1..65535) {
asmgen.out("""
lda $variable
sec
sbc #<$value
sta $variable
bcs +++
lda $variable+1
sbc #>$value
sta $variable+1
bcs +
dec $variable+2
bne ++
lda $variable+2
bne +
dec $variable+3
+ dec $variable+2
+ dec $variable+1
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
sec
lda $variable
@@ -942,7 +952,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<<" -> if (value > 0) inplaceLongShiftLeft()
">>" -> if (value > 0) inplaceLongShiftRight()
"|" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
ora #$${hex.substring(6,8)}
@@ -958,7 +968,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"&" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
and #$${hex.substring(6,8)}
@@ -974,7 +984,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"^" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
eor #$${hex.substring(6,8)}
@@ -1384,188 +1394,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return false
}
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
}
private fun inplacemodificationBytePointerWithVariable(pointervar: PtIdentifier, operator: String, otherName: String) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
"+" -> asmgen.out(" clc | adc $otherName")
"-" -> asmgen.out(" sec | sbc $otherName")
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
"<<" -> {
asmgen.out("""
ldy $otherName
beq +
- asl a
dey
bne -
+""")
}
">>" -> {
asmgen.out("""
ldy $otherName
beq +
- lsr a
dey
bne -
+""")
}
"&" -> asmgen.out(" and $otherName")
"|" -> asmgen.out(" ora $otherName")
"^" -> asmgen.out(" eor $otherName")
"==" -> {
asmgen.out("""
cmp $otherName
beq +
lda #0
beq ++
+ lda #1
+""")
}
"!=" -> {
asmgen.out("""
cmp $otherName
bne +
lda #0
beq ++
+ lda #1
+""")
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
private fun inplacemodificationBytePointerWithLiteralval(pointervar: PtIdentifier, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
when (operator) {
"+" -> {
if(value==1) {
if(asmgen.options.romable) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" inc a")
else
asmgen.out(" clc | adc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out($$"+\tinc $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" clc | adc #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"-" -> {
if(value==1) {
if(asmgen.options.romable) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" dec a")
else
asmgen.out(" sec | sbc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out($$"+\tdec $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" sec | sbc #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"*" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value in asmgen.optimizedByteMultiplications)
asmgen.out(" jsr prog8_math.mul_byte_${value}")
else
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"/" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value==0)
throw AssemblyError("division by zero")
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm | tya")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"%" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value==0)
throw AssemblyError("division by zero")
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"<<" -> {
if (value > 0) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
repeat(value) { asmgen.out(" asl a") }
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
">>" -> {
if (value > 0) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
repeat(value) { asmgen.out(" lsr a") }
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"&" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"|"-> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"^" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"==" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("""
cmp #$value
beq +
lda #0
beq ++
+ lda #1
+""")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"!=" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("""
cmp #$value
bne +
lda #0
beq ++
+ lda #1
+""")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
private fun inplacemodificationByteWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
require(dt.isByteOrBool)
if(!value.isSimple()) {
File diff suppressed because it is too large. Load diff
@@ -24,7 +24,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(extsub!=null) {
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
if (funcCall.multipleResultFpRegs.isNotEmpty())
TODO("deal with (multiple?) FP return registers")
TODO("deal with (multiple?) FP return registers ${assignment.position}")
if (extsub.returns.size == assignmentTargets.size) {
// Targets and values match. Assign all the things. Skip 'void' targets.
extsub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
@@ -123,6 +123,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
if(augAssign.target.type.isString)
throw AssemblyError("cannot assign to str type ${augAssign.position}")
// augmented assignment always has just a single target
if (augAssign.target.children.single() is PtIrRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@@ -188,7 +191,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> {
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
addInstr(inplaceInstrs, IRInstruction(opc, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
}
"or=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
@@ -467,7 +473,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
if(assignment.target.type.isString) {
// assigning array and string values is done via an explicit memcopy/stringcopy function calls.
throw AssemblyError("cannot assign to str type ${assignment.position}")
}
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
@@ -550,7 +560,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
if(constOffset in 0..65535) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
@@ -560,18 +570,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
}
val tr = expressionEval.translateExpression(memory!!.address)
@@ -702,9 +700,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.MSIGB, IRDataType.WORD, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
@@ -717,9 +715,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.MSIGB, IRDataType.WORD, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
@@ -969,7 +967,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorDivideInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression, signed: Boolean): IRCodeChunks? {
if(array!=null) {
TODO("/ in array")
TODO("/ in array ${array.position}")
}
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place /"")
@@ -1312,11 +1310,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val shiftReg = shiftTr.resultReg
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(constAddress!=null)
IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
@@ -1374,11 +1368,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val shiftReg = shiftTr.resultReg
addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
else
@@ -1433,7 +1423,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorModuloInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
TODO("% in array")
TODO("% in array ${array.position}")
}
if(constAddress==null && memory!=null)
return null // TODO("optimized memory in-place %"")
@@ -17,7 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
@@ -26,8 +26,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"call" -> funcCall(call)
"msw" -> funcMsw(call)
"lsw" -> funcLsw(call)
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
"msb" -> funcMsb(call, false)
"msb__long" -> funcMsb(call, true)
"lsb" -> funcLsb(call, false)
"lsb__long" -> funcLsb(call, true)
"memory" -> funcMemory(call)
"peek" -> funcPeek(call, IRDataType.BYTE)
"peekbool" -> funcPeek(call, IRDataType.BYTE)
@@ -230,7 +232,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
val resultReg = codeGen.registers.next(tr.dt)
val resultReg = codeGen.registers.next(IRDataType.BYTE)
if(tr.dt==IRDataType.FLOAT) {
addToResult(result, tr, -1, tr.resultFpReg)
@@ -267,7 +269,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
BaseDataType.LONG -> TODO("SQRT LONG")
BaseDataType.LONG -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.WORD) // sqrt of a long still produces just a word result
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.LONG, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
@@ -577,12 +586,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
}
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
private fun funcLsb(call: PtBuiltinFunctionCall, fromLong: Boolean): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
if(fromLong)
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
else
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
@@ -592,16 +604,19 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
private fun funcMsb(call: PtBuiltinFunctionCall, fromLong: Boolean): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
if(fromLong)
addInstr(result, IRInstruction(Opcode.MSIGB, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
else
addInstr(result, IRInstruction(Opcode.MSIGB, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
@@ -611,7 +626,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
addInstr(result, IRInstruction(Opcode.MSIGW, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
@@ -99,7 +99,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
var pointerReg: Int
if(deref.startpointer.type.isStructInstance) {
TODO("translate structinstance deref???")
TODO("translate structinstance deref??? ${deref.position}")
/*
val arrayIndexer = deref.startpointer as? PtArrayIndexer
if(arrayIndexer==null)
@@ -406,7 +406,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
if(constOffset in 0..65535) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
@@ -416,18 +416,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val tr = translateExpression(mem.address)
@@ -686,7 +674,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
}
valueDt.isLong -> TODO("typecast long ${cast.position}")
valueDt.isLong -> {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.LONG, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
}
valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also {
@@ -704,9 +695,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.LONG -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.LONG -> TODO("cast UBYTE to LONG ${cast.position}")
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
@@ -721,9 +715,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.LONG -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIGB, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.LONG -> TODO("cast BYTE to LONG ${cast.position}")
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
@@ -746,7 +743,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg
}
BaseDataType.LONG -> TODO("cast UWORD to LONG ${cast.position}")
BaseDataType.LONG -> {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
@@ -772,7 +772,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.UWORD -> {
actualResultReg2 = tr.resultReg
}
BaseDataType.LONG -> TODO("cast WORD to LONG ${cast.position}}")
BaseDataType.LONG -> {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.LSIGW, IRDataType.LONG, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
}
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
@@ -796,7 +799,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
}
BaseDataType.UWORD -> {
BaseDataType.UWORD, BaseDataType.POINTER -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
@@ -804,7 +807,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.FTOSL, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
}
}
BaseDataType.FLOAT -> {
@@ -822,6 +829,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
BaseDataType.LONG -> {
addInstr(result, IRInstruction(Opcode.FFROMSL, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
@@ -831,7 +841,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// no further conversion required, pointers are all just uwords
}
BaseDataType.ARRAY_POINTER -> {
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
TODO("typecast to array of pointers $valueDt -> ${cast.type} ${cast.position}")
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
@@ -1169,7 +1179,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
)
}
else TODO("extsub with banked address got called ${callTarget.name}")
else TODO("extsub with banked address got called ${callTarget.name} ${fcall.position}")
}
addInstr(result, call, null)
val resultRegs = returnRegisters.filter{it.dt!=IRDataType.FLOAT}.map{it.registerNum}
@@ -1747,9 +1757,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += pointerTr.chunks
pointerReg = pointerTr.resultReg
} else if(left.pointerderef!=null) {
TODO("get pointer from deref $left")
TODO("get pointer from deref $left ${left.position}")
} else {
throw AssemblyError("weird arrayindexer $left")
throw AssemblyError("weird arrayindexer $left ${left.position}")
}
val struct = left.type.subType!! as StStruct
val constindex = left.index as? PtNumber
@@ -157,6 +157,7 @@ class IRCodeGen(
is IRCodeChunk -> {
val replacement = IRCodeChunk(sub.label, first.next)
replacement.instructions += first.instructions
replacement.appendSrcPositions(first.sourceLinesPositions)
replacement
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.label, first.assembly, first.isIR, first.next)
@@ -272,7 +273,7 @@ class IRCodeGen(
is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
is PtStructDecl -> emptyList()
is PtSubSignature -> emptyList()
else -> TODO("missing codegen for $node")
else -> TODO("missing codegen for $node ${node.position}")
}
val nonEmptyChunks = chunks.filter { it.isNotEmpty() || it.label != null }
@@ -410,6 +411,7 @@ class IRCodeGen(
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.next)
newChunk.instructions += first.instructions
newChunk.appendSrcPositions(first.sourceLinesPositions)
newChunk
}
is IRInlineAsmChunk -> {
@@ -1142,7 +1144,7 @@ class IRCodeGen(
if(identifier!=null && !isIndirectJump(goto))
IRInstruction(branchOpcode, labelSymbol = identifier.name)
else
TODO("JUMP to expression address ${goto.target}")
TODO("JUMP to expression address ${goto.target} ${goto.position}")
}
}
@@ -1860,7 +1862,7 @@ class IRCodeGen(
is PtNop -> { /* nothing */ }
is PtAssignment, is PtAugmentedAssign -> { /* global variable initialization is done elsewhere */ }
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
is PtAlign -> TODO("ir support for inline %align")
is PtAlign -> TODO("ir support for inline %align ${child.position}")
is PtSub -> {
val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
for (subchild in child.children) {
@@ -1912,7 +1914,7 @@ class IRCodeGen(
}
}
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
else -> TODO("weird block child node $child")
else -> TODO("weird block child node $child ${child.position}")
}
}
return irBlock
@@ -55,6 +55,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|| cleanupPushPop(chunk1, indexedInstructions)
|| simplifyConstantReturns(chunk1, indexedInstructions)
|| removeNeedlessLoads(chunk1, indexedInstructions)
|| useArrayIndexingInsteadOfAdds(chunk1, indexedInstructions)
|| removeNops(chunk1, indexedInstructions) // last time, in case one of the optimizers replaced something with a nop
} while (changed)
}
}
@@ -415,13 +417,19 @@ jump p8_label_gen_2
chunk.instructions.removeAt(idx)
changed = true
}
-1 if ins.type == IRDataType.LONG -> {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
Opcode.OR -> {
if (ins.immediate == 0) {
chunk.instructions.removeAt(idx)
changed = true
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) ||
(ins.immediate == 65535 && ins.type == IRDataType.WORD) ||
(ins.immediate == -1 && ins.type == IRDataType.LONG)) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
changed = true
}
@@ -434,6 +442,54 @@ jump p8_label_gen_2
}
else -> {}
}
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
// look if the given register gets an immediate value 1 or 2 istructions back
// returns (index of load instruction, immediate value) or null.
if(idx>=1) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-1 to previous.immediate!!
}
if(idx>=2) {
val previous = indexedInstructions[idx-2].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-2 to previous.immediate!!
}
return null
}
if(ins.reg1!=null) {
val immediate1 = getImmediateLoad(ins.reg1!!)
if(immediate1!=null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
chunk.instructions.removeAt(immediate1.first)
changed=true
} else {
val immediate2 = getImmediateLoad(ins.reg2!!)
if (immediate2 != null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
chunk.instructions.removeAt(immediate2.first)
changed=true
}
}
}
}
// try to use immediate arithmetic instruction if possible
when(ins.opcode) {
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
// Opcode.SUBR -> TODO("ir peephole Subr")
// Opcode.DIVR -> TODO("ir peephole Divr")
// Opcode.DIVSR -> TODO("ir peephole Divsr")
// Opcode.MODR -> TODO("ir peephole Modr")
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
else -> {}
}
}
return changed
}
@@ -499,4 +555,40 @@ jump p8_label_gen_2
}
return changed
}
private fun useArrayIndexingInsteadOfAdds(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.ADD && ins.immediate!=null && idx>0) {
val load = indexedInstructions[idx-1].value
if((load.opcode==Opcode.LOAD) && load.labelSymbol!=null) {
val lastInstruction = indexedInstructions[idx+1].value
if(lastInstruction.opcode==Opcode.LOADI) {
val targetRegister = lastInstruction.reg1!!
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
val loadm = IRInstruction(Opcode.LOADM, lastInstruction.type, reg1=targetRegister, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
chunk.instructions[idx-1] = loadm
chunk.instructions.removeAt(idx+1)
chunk.instructions.removeAt(idx)
changed = true
}
} else if(lastInstruction.opcode==Opcode.STOREI) {
val targetRegister = lastInstruction.reg1!!
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
val valueLoad = indexedInstructions[idx-2].value
if(valueLoad.opcode==Opcode.LOAD && valueLoad.reg1==targetRegister) {
val storem = IRInstruction(Opcode.STOREM, lastInstruction.type, reg1=valueLoad.reg1, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
chunk.instructions[idx-1] = storem
chunk.instructions.removeAt(idx+1)
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
}
}
return changed
}
}
@@ -122,7 +122,6 @@ private fun convert(variable: StMemVar): IRStMemVar {
private fun convert(constant: StConstant): IRStConstant {
val dt = DataType.forDt(constant.dt)
val scopedName = if('.' in constant.name) {
constant.name
} else {
@@ -132,7 +131,7 @@ private fun convert(constant: StConstant): IRStConstant {
constant.name
}
}
return IRStConstant(scopedName, dt, constant.value)
return IRStConstant(scopedName, constant.dt, constant.value)
}
+11 -5
View File
@@ -205,21 +205,27 @@ class TestIRPeepholeOpt: FunSpec({
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.WORD, reg1=42, immediate = 0),
IRInstruction(Opcode.AND, IRDataType.LONG, reg1=42, immediate = 0),
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, immediate = 255),
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535)
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, immediate = 65535),
IRInstruction(Opcode.OR, IRDataType.LONG, reg1=42, immediate = -1)
))
irProg.chunks().single().instructions.size shouldBe 4
irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize(true, ErrorReporterForTests())
val instr = irProg.chunks().single().instructions
instr.size shouldBe 4
instr.size shouldBe 6
instr[0].opcode shouldBe Opcode.LOAD
instr[1].opcode shouldBe Opcode.LOAD
instr[2].opcode shouldBe Opcode.LOAD
instr[3].opcode shouldBe Opcode.LOAD
instr[4].opcode shouldBe Opcode.LOAD
instr[5].opcode shouldBe Opcode.LOAD
instr[0].immediate shouldBe 0
instr[1].immediate shouldBe 0
instr[2].immediate shouldBe 255
instr[3].immediate shouldBe 65535
instr[2].immediate shouldBe 0
instr[3].immediate shouldBe 255
instr[4].immediate shouldBe 65535
instr[5].immediate shouldBe -1
}
})
@@ -349,7 +349,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("constant fold pointer[i]")
TODO("constant fold pointer[i] ${arrayIndexedExpression.position}")
}
}
}
@@ -244,8 +244,16 @@ class VarConstantValueTypeAdjuster(
dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float"
dt.isLong -> {
val value = functionCallExpr.args[0].constValue(program)?.number
if(value!=null && value<0) {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
}
"sqrt__long"
}
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
@@ -254,6 +262,15 @@ class VarConstantValueTypeAdjuster(
functionCallExpr))
}
}
else if(func==listOf("lsb") || func==listOf("msb")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isLong) {
val replaceFunc = func[0]+"__long"
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
return noModifications
}
@@ -81,7 +81,7 @@ class UnusedCodeRemover(private val program: Program,
private fun reportUnreachable(stmt: Statement) {
when(val next = stmt.nextSibling()) {
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is Break, is Jump -> {}
else -> errors.warn("unreachable code", next.position)
}
}
+62
View File
@@ -38,6 +38,36 @@ bcd {
return a
}
sub addtol(^^long a, long b) {
; -- inplace long BCD addition (avoids copying long values by value)
setbcd()
;; NOT YET IMPLEMENTED IN PROG8: a^^ += b, so inline asm
%asm {{
lda p8v_a
ldy p8v_a+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
clc
lda (P8ZP_SCRATCH_W1),y
adc p8v_b
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+1
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+2
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
adc p8v_b+3
sta (P8ZP_SCRATCH_W1),y
}}
clearbcd()
}
sub subb(byte a, byte b) -> byte {
setbcd()
a -= b
@@ -66,7 +96,39 @@ bcd {
return a
}
sub subfroml(^^long a, long b) {
; -- inplace long BCD subtraction (avoids copying long values by value)
setbcd()
;; NOT YET IMPLEMENTED IN PROG8: a^^ -= b, so inline asm
%asm {{
lda p8v_a
ldy p8v_a+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
sec
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+1
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+2
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W1),y
sbc p8v_b+3
sta (P8ZP_SCRATCH_W1),y
}}
clearbcd()
}
inline asmsub setbcd() {
; be safe and 6502 compatible and prohibit interrupts during BCD mode
%asm {{
php
sei
+59 -1
View File
@@ -70,7 +70,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
+20 -1
View File
@@ -358,7 +358,7 @@ extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without r
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
extsub $FF7D = PRIMM() ; print immediate string
extsub $FF7D = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; ---- C128 specific system utility routines: ----
@@ -1140,6 +1140,16 @@ cx16 {
&word r14s = $1bfc
&word r15s = $1bfe
; signed long versions
&long r0r1sl = $1be0
&long r2r3sl = $1be4
&long r4r5sl = $1be8
&long r6r7sl = $1bec
&long r8r9sl = $1bf0
&long r10r11sl = $1bf4
&long r12r13sl = $1bf8
&long r14r15sl = $1bfc
; ubyte versions (low and high bytes)
&ubyte r0L = $1be0
&ubyte r1L = $1be2
@@ -1302,6 +1312,11 @@ asmsub init_system() {
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
; basic is not banked in, adjust MEMTOP
ldx #<$c000
ldy #>$c000
clc
jsr cbm.MEMTOP
cli
rts
}}
@@ -1323,6 +1338,10 @@ asmsub cleanup_at_exit() {
sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
ldx #<$ff00
ldy #>$ff00
clc
jsr cbm.MEMTOP ; adjust MEMTOP to original value again
lda _exitcarry
lsr a
lda _exitcode
+12
View File
@@ -406,6 +406,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
jsr cbm.SCREEN
inx
txa
rts
}}
@@ -415,11 +416,22 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
jsr cbm.SCREEN
iny
tya
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr cbm.SCREEN
inx
iny
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+71 -1
View File
@@ -2,7 +2,8 @@
FL_ONE_const .byte 129 ; 1.0
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
; note: don't add too many constants here because they all end up in the resulting program
.section BSS
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
@@ -126,6 +127,75 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
.pend
cast_from_long .proc
; convert long pointed to by AY into a float pointed to by R0
; a bit slow algorithm implemented below: (msw(l) as word) as float * 65536.0 + (lsw(l) as float)
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #3
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVAYFAY
lda #<FL_65536_const
ldy #>FL_65536_const
jsr CONUPK
jsr FMULTT
jsr pushFAC1
ldy #1
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jsr MOVEF
clc
jsr popFAC
jsr FADDT
ldx cx16.r0L
ldy cx16.r0H
jmp MOVMF
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
; !notreached!
.pend
cast_as_long .proc
; convert float pointed to by R0 into a long pointed to by AY
FACHO = FAC_ADDR + 1
; save the target variable pointer on the stack
pha
tya
pha
lda cx16.r0L
ldy cx16.r0H
jsr MOVFM
jsr QINT
; restore the target variable pointer from the stack, and put the result in it
pla
sta P8ZP_SCRATCH_PTR+1
pla
sta P8ZP_SCRATCH_PTR
ldx #3
ldy #0
- lda FACHO,x
sta (P8ZP_SCRATCH_PTR),y
iny
dex
bpl -
rts
.pend
copy_float .proc
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
+1
View File
@@ -11,6 +11,7 @@ floats {
; note: the fac1 and fac2 are working registers and take 6 bytes each,
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
const uword FAC_ADDR = $61
; 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.
+4 -1
View File
@@ -2,8 +2,11 @@
func_sign_f_into_A .proc
; sign in A, also sets status flags
jsr MOVFM
jmp SIGN
jsr SIGN
cmp #0
rts
.pend
+3 -20
View File
@@ -288,29 +288,16 @@ hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %1111
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
; Midpoint algorithm, filled.
; Overdraws horizontal lines unfortunately.
if radius==0
return
ubyte @zp yy = 0
word decisionOver2 = (1 as word)-radius
ubyte last_y3 = ycenter+radius
ubyte last_y4 = ycenter-radius
ubyte new_y3, new_y4
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
horizontal_line(xcenter-yy, last_y3, yy*2+1)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
horizontal_line(xcenter-yy, last_y4, yy*2+1)
last_y4 = new_y4
}
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
yy++
if decisionOver2>=0 {
radius--
@@ -319,10 +306,6 @@ hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %1111
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
horizontal_line(xcenter-yy, last_y3, yy*2+1)
horizontal_line(xcenter-yy, last_y4, yy*2+1)
}
+14
View File
@@ -1150,6 +1150,16 @@ cx16 {
&word r14s = $cffc
&word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r9sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0
&ubyte r1L = $cfe2
@@ -1313,6 +1323,8 @@ asmsub init_system() {
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
lda #$80
sta 650 ; enable key repeat for normal keys too
lda #PROG8_C64_BANK_CONFIG ; apply bank config
sta $01
and #1
@@ -1349,6 +1361,8 @@ asmsub cleanup_at_exit() {
jsr cbm.MEMTOP ; adjust MEMTOP down again
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
lda #0
sta 650 ; disable keyrepeats
lda _exitcarry
lsr a
lda _exitcode
+8
View File
@@ -424,6 +424,14 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+120 -45
View File
@@ -6,8 +6,6 @@ conv {
; ----- number conversions to decimal strings ----
ubyte[16] @shared string_out ; result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
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)
%asm {{
@@ -257,53 +255,56 @@ sub str_l (long value) -> str {
; source: https://codebase64.net/doku.php?id=base:32_bit_hexadecimal_to_decimal_conversion
; NOTE: this is a pretty slow conversion, don't use for time critical displays!
bool negative
string_out[0] = '0'
if value<0 {
negative = true
value = -value
}
%asm {{
jsr hex2dec
jsr hex2dec
ldx #0
- lda string_out+1,x
cmp #'0'
bne +
inx
bne -
+ cpx #10 ; just all 0? keep one
bne +
dex
ldx #0
- lda result,x
bne +
inx ; skip leading zeros
cpx #9
bne -
+ ; x points at the rightmost leading 0
lda negative
beq +
lda #'-' ; sign in front
sta string_out,x
dex
bmi _done
+ ; x still points at rightmost leading 0
; remove leading 0's (there is at least one if we're here)
ldy #0
- lda string_out+1,x
sta string_out,y
iny
inx
cpx #10
bne -
lda #0
sta string_out,y
_done
; convert to petscii numbers and return ptr to first nonzero
+ stx P8ZP_SCRATCH_B1
inc P8ZP_SCRATCH_B1
- lda result,x
ora #$30
sta result,x
inx
cpx #10
bne -
lda #0
sta result,x
; sign needed?
lda negative
beq +
dec P8ZP_SCRATCH_B1
ldx P8ZP_SCRATCH_B1
lda #'-'
sta result_sign,x
+ lda #<result_sign
ldy #>result_sign
clc
adc P8ZP_SCRATCH_B1
bcc +
iny
+ rts
lda #<string_out
ldy #>string_out
rts
hex2dec:
; converts 10 digits (32 bit values have max. 10 decimal digits)
hex2dec
ldx #9
ldx #9+1
l3 jsr div10
sta result,x
ora #$30
sta string_out,x
dex
bpl l3
bne l3
rts
; divides a 32 bit value by 10
@@ -323,13 +324,6 @@ skip rol value
dey
bpl l4
rts
.section BSS
result_sign .byte ? ; room for the '-' sign
result .fill 10
.byte ? ; string 0 terminator
.send
}}
}
@@ -578,6 +572,85 @@ _try_iso
}}
}
asmsub hex2long(str string @AY) -> long @R0R1_32 {
; -- hexadecimal string (with or without '$') to long.
; string may be in petscii or c64-screencode encoding.
; stops parsing at the first character that's not a hex digit (except leading $)
; result in R0:R1, number of characters processed also remains in cx16.r15L if you want to use it!! (0 = error)
%asm {{
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
sty cx16.r0
sty cx16.r0+1
sty cx16.r0+2
sty cx16.r0+3
sty cx16.r15H
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #'$'
bne _loop
iny
_loop
lda #0
sta P8ZP_SCRATCH_B1
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #7 ; screencode letters A-F are 1-6
bcc _add_letter
and #127
cmp #97
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
cmp #'g'
bcs _stop
cmp #'a'
bcs _add_letter
cmp #'0'
bcc _stop
cmp #'9'+1
bcs _stop
_calc
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
and #$0f
clc
adc P8ZP_SCRATCH_B1
ora cx16.r0
sta cx16.r0
iny
bne _loop
_stop
sty cx16.r15L
rts
_add_letter
pha
lda #9
sta P8ZP_SCRATCH_B1
pla
jmp _calc
_try_iso
cmp #103
bcs _stop
and #63
bne _add_letter
; !notreached!
}}
}
asmsub bin2uword(str string @AY) -> uword @AY {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
@@ -865,4 +938,6 @@ output .fill 5 ; 0-terminated output buffer (to make printing easier)
}}
}
ubyte[16] @shared string_out ; internal result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
}
+61 -3
View File
@@ -68,7 +68,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
@@ -78,7 +136,7 @@ stack {
ubyte bank
sub init(ubyte rambank) {
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the stack, must be called before use. Supply the HIRAM bank to use as buffer space.
sp = $bfff
bank = rambank
}
@@ -157,7 +215,7 @@ ringbuffer {
ubyte bank = 255 ; set via init()
sub init(ubyte rambank) {
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the ringbuffer, must be called before use. Supply the HIRAM bank to use as buffer space.
head = $a000
tail = $bfff
fill = 0
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
%import shared_compression
+69 -59
View File
@@ -244,6 +244,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=d")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -252,6 +253,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -566,7 +568,7 @@ return_status:
sub internal_f_open_w(str filename, bool open_for_seeks) -> bool {
f_close_w()
list_filename = filename
void strings.copy(filename, list_filename)
str modifier = ",s,?"
modifier[3] = 'w'
if open_for_seeks
@@ -624,7 +626,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
@@ -818,8 +820,13 @@ io_error:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> long {
&long banksize = &cx16.r12
banksize = cx16.getrambank() - startbank
banksize <<= 13 ; * 8192
banksize += endaddress
banksize -= startaddress
return banksize
}
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
@@ -994,15 +1001,12 @@ io_error:
return false
}
sub f_seek(uword pos_hiword, uword pos_loword) {
sub f_seek(long position) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
pokel(&command+2, position)
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1011,13 +1015,10 @@ io_error:
}
sub f_seek_w(uword pos_hiword, uword pos_loword) {
sub f_seek_w(long position) {
; -- seek in the output file opened with f_open_w_seek, to the given 32-bits position
diskio.f_seek.command[1] = WRITE_IO_CHANNEL ; f_open_w uses this secondary address
diskio.f_seek.command[2] = lsb(pos_loword)
diskio.f_seek.command[3] = msb(pos_loword)
diskio.f_seek.command[4] = lsb(pos_hiword)
diskio.f_seek.command[5] = msb(pos_hiword)
pokel(&diskio.f_seek.command+2, position)
cbm.SETNAM(sizeof(diskio.f_seek.command), &diskio.f_seek.command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1025,54 +1026,63 @@ io_error:
reset_write_channel() ; back to the write io channel
}
asmsub f_tell() -> uword @R0, uword @R1, uword @R2, uword @R3 {
; -- Returns the current read position of the opened read file,
; in R0 and R1 (low + high words) and the file size in R2 and R3 (low + high words).
; Returns 0 as size if the command is not supported by the DOS implementation/version.
%asm {{
jmp internal_f_tell
}}
}
sub internal_f_tell() {
; gets the (32 bits) position + file size of the opened read file channel
ubyte[2] command = ['t',0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
sub f_tell() -> long, long {
; -- Returns the current read position of the opened read file, and the file size.
; Returns 0,0 if the command is not supported by the DOS implementation/version.
ubyte[3] command = ['?','?',0]
bool success=false
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
cx16.r1 = read4hex()
cx16.r0 = read4hex() ; position in R1:R0
void cbm.CHRIN() ; separator space
cx16.r3 = read4hex()
cx16.r2 = read4hex() ; filesize in R3:R2
success = true
long filesize, filepos
ubyte commandoffset
command[1] = 't'
commandoffset = 1
f_tell_internal()
return filepos, filesize
sub f_tell_internal() {
; this code is used for both the T (tell) and FL (fatlba) commands
command[2] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command)-commandoffset, &command+commandoffset)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
filepos = read8hex()
void cbm.CHRIN() ; separator space
filesize = read8hex()
success = true
} else {
filesize = filepos = 0
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
if success
return
cx16.r0 = cx16.r1 = cx16.r2 = cx16.r3 = 0
}
sub read4hex() -> uword {
str hex = "0000"
hex[0] = cbm.CHRIN()
hex[1] = cbm.CHRIN()
hex[2] = cbm.CHRIN()
hex[3] = cbm.CHRIN()
return conv.hex2uword(hex)
sub f_fatlba() -> long, long {
; -- Return the current LBA, cluster number (sector index and shift value are ignored for now)
diskio.f_tell.command[0] = 'f'
diskio.f_tell.command[1] = 'l'
diskio.f_tell.commandoffset = 0
diskio.f_tell.f_tell_internal()
return diskio.f_tell.filepos, diskio.f_tell.filesize
}
sub read8hex() -> long {
str hex = "00000000"
for cx16.r0L in 0 to 7 {
hex[cx16.r0L] = cbm.CHRIN()
}
return conv.hex2long(hex)
}
}
+2
View File
@@ -14,6 +14,8 @@ floats {
; 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.
const uword FAC_ADDR = $c3
extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY)
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
+4 -37
View File
@@ -433,25 +433,12 @@ gfx_hires {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
last_y4 = new_y4
}
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
yy++
if decisionOver2>=0 {
radius--
@@ -460,11 +447,6 @@ gfx_hires {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
}
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) {
@@ -474,23 +456,12 @@ gfx_hires {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
last_y4 = new_y4
}
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
yy++
if decisionOver2>=0 {
radius--
@@ -499,10 +470,6 @@ gfx_hires {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
}
sub plot(uword @zp xx, uword @zp yy, ubyte @zp color) {
+12 -44
View File
@@ -505,25 +505,11 @@ gfx_lores {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
ubyte last_y3 = ycenter+radius
ubyte last_y4 = ycenter-radius
ubyte new_y3, new_y4
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
last_y4 = new_y4
}
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
yy++
if decisionOver2>=0 {
radius--
@@ -532,11 +518,6 @@ gfx_lores {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
}
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) {
@@ -546,29 +527,22 @@ gfx_lores {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
uword liney = ycenter+yy
if msb(liney)==0
safe_horizontal_line(xcenter-radius, lsb(ycenter+yy), radius*$0002+1, color)
safe_horizontal_line(xcenter-radius, lsb(liney), radius*$0002+1, color)
liney = ycenter-yy
if msb(liney)==0
safe_horizontal_line(xcenter-radius, lsb(ycenter-yy), radius*$0002+1, color)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
if msb(last_y3)==0
safe_horizontal_line(xcenter-yy, lsb(last_y3), yy*$0002+1, color)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
if msb(last_y4)==0
safe_horizontal_line(xcenter-yy, lsb(last_y4), yy*$0002+1, color)
last_y4 = new_y4
}
safe_horizontal_line(xcenter-radius, lsb(liney), radius*$0002+1, color)
liney = ycenter+radius
if msb(liney)==0
safe_horizontal_line(xcenter-yy, lsb(liney), yy*$0002+1, color)
liney = ycenter-radius
if msb(liney)==0
safe_horizontal_line(xcenter-yy, lsb(liney), yy*$0002+1, color)
yy++
if decisionOver2>=0 {
radius--
@@ -577,12 +551,6 @@ gfx_lores {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
if msb(last_y3)==0
safe_horizontal_line(xcenter-yy, lsb(last_y3), yy*$0002+1, color)
if msb(last_y4)==0
safe_horizontal_line(xcenter-yy, lsb(last_y4), yy*$0002+1, color)
}
asmsub plot(uword x @AX, ubyte y @Y, ubyte color @R0) {
+5 -35
View File
@@ -595,23 +595,12 @@ drawmode: ora cx16.r15L
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
last_y4 = new_y4
}
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
yy++
if decisionOver2>=0 {
radius--
@@ -620,10 +609,6 @@ drawmode: ora cx16.r15L
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
}
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
@@ -634,23 +619,12 @@ drawmode: ora cx16.r15L
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
last_y4 = new_y4
}
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
yy++
if decisionOver2>=0 {
radius--
@@ -659,10 +633,6 @@ drawmode: ora cx16.r15L
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
}
sub plot(uword @zp xx, uword @zp yy, bool @zp draw) {
@@ -1038,7 +1008,7 @@ _doplot beq +
; y*40 lookup table. Pretty compact because it all fits in a word and we only need 240 y positions.
; a y*80 lookup table would be very large (lo,mid,hi for 480 values...)
uword[240] @split @shared times40 = [
uword[240] @shared times40 = [
0, 40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600,
640, 680, 720, 760, 800, 840, 880, 920, 960, 1000, 1040, 1080, 1120, 1160,
1200, 1240, 1280, 1320, 1360, 1400, 1440, 1480, 1520, 1560, 1600, 1640, 1680,
+68 -19
View File
@@ -217,6 +217,16 @@ cx16 {
&word r14s = $001e
&word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r9sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes)
&ubyte r0L = $0002
&ubyte r1L = $0004
@@ -463,7 +473,7 @@ extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobb
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
extsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) ; note: The the zero page address containing the base address is passed in stavec ($03B2)
extsub $ff7d = PRIMM()
extsub $ff7d = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; high level graphics & fonts
extsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
@@ -620,6 +630,10 @@ const ubyte EXTAPI_scnsiz = $0D
const ubyte EXTAPI_kbd_leds = $0E
const ubyte EXTAPI_memory_decompress_from_func = $0F
const ubyte EXTAPI_default_palette = $10
const ubyte EXTAPI_has_machine_property = $11
const ubyte EXTAPI_kbdbuf_get = $12
const ubyte EXTAPI_kbdbuf_clear = $13
const ubyte EXTAPI_blink_enable = $14
; extapi16 call numbers
const ubyte EXTAPI16_test = $00
@@ -676,6 +690,30 @@ sub mouse_present() -> bool {
return cx16.r0L != $fc ; $fc = BAT_FAIL
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
%asm {{
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
}}
}
; shims for the kernal routines called via the extapi call:
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
@@ -758,31 +796,42 @@ asmsub set_default_palette() {
}}
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
asmsub has_machine_property(ubyte property @X) clobbers(A,X) -> bool @Pc {
; requires rom v49+
%asm {{
lda #EXTAPI_has_machine_property
jmp cx16.extapi
}}
}
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
asmsub kbdbuf_get() clobbers(X,Y) -> ubyte @A {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_get
jmp cx16.extapi
}}
}
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
asmsub kbdbuf_clear() clobbers(A) {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_clear
jmp cx16.extapi
}}
}
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
asmsub blink_enable(bool enable @X) clobbers(A,X) {
; requires rom v49+
%asm {{
txa
and #1
eor #1
tax
lda #EXTAPI_blink_enable
jmp cx16.extapi
}}
}
; TODO : implement shims for the remaining extapi calls.
; ---- end of kernal routines ----
+7
View File
@@ -615,6 +615,13 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+10 -12
View File
@@ -554,14 +554,13 @@ log2_tab
}
sub crc16(^^ubyte data, uword length) -> uword {
; calculates the CRC16 (XMODEM) checksum of the buffer.
; calculates the CRC16 (XMODEM) checksum of the buffer. Clobbers R14 and R15.
; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc16 for data that doesn't fit in a single memory block.
cx16.r14 = data
crc16_start()
cx16.r13 = data
cx16.r14 = data+length
while cx16.r13!=cx16.r14 {
crc16_update(@(cx16.r13))
cx16.r13++
repeat length {
crc16_update(@(cx16.r14))
cx16.r14++
}
return crc16_end()
}
@@ -610,14 +609,13 @@ log2_tab
sub crc32(^^ubyte data, uword length) -> long {
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
; Clobbers R12 through R15, returns the 32 bits result as long
; Clobbers R13 through R15, returns the 32 bits result as long
; There are also "streaming" crc32_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block.
cx16.r13 = data
crc32_start()
cx16.r12 = data
cx16.r13 = data+length
while cx16.r12!=cx16.r13 {
crc32_update(@(cx16.r12))
cx16.r12++
repeat length {
crc32_update(@(cx16.r13))
cx16.r13++
}
return crc32_end()
}
+10
View File
@@ -605,6 +605,16 @@ cx16 {
&word r14s = $7ffc
&word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r9sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0
&ubyte r1L = $7fe2
+13 -2
View File
@@ -182,8 +182,8 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
}
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
; ---- set char at the given position on the screen. charcolor is ignored on PET
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char at the given position on the screen. NOTE: charcolor is ignored on PET
%asm {{
lda row
asl a
@@ -236,6 +236,17 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr width
tax
jsr height
tay
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+96 -23
View File
@@ -29,33 +29,33 @@ abs_w_into_AY .proc
+ rts
.pend
abs_l_into_R0R1 .proc
; -- R0:R1 = abs(R0:R1)
lda cx16.r1H
abs_l_into_R14R15 .proc
; -- R14:R15 = abs(R14:R15)
lda cx16.r15H
bmi + ; Negative if high bit of highest byte is set
rts
+
; Invert all four bytes
lda cx16.r0L
lda cx16.r14L
eor #$FF
sta cx16.r0L
lda cx16.r0H
sta cx16.r14L
lda cx16.r14H
eor #$FF
sta cx16.r0H
lda cx16.r1L
sta cx16.r14H
lda cx16.r15L
eor #$FF
sta cx16.r1L
lda cx16.r1H
sta cx16.r15L
lda cx16.r15H
eor #$FF
sta cx16.r1H
sta cx16.r15H
; Add 1 to whole 32-bit value
inc cx16.r0L
inc cx16.r14L
bne +
inc cx16.r0H
inc cx16.r14H
bne +
inc cx16.r1L
inc cx16.r15L
bne +
inc cx16.r1H
inc cx16.r15H
+ rts
.pend
@@ -89,17 +89,39 @@ _possibly_zero cmp #0
func_sign_w_into_A .proc
cpy #0
beq _possibly_zero
bmi _neg
_pos lda #1
rts
_neg lda #-1
rts
_possibly_zero cmp #0
bne _pos
bmi _negative
bne _positive
cmp #0
beq _zero
_positive lda #1
_zero rts
_negative lda #-1
rts
.pend
func_sign_l_r14r15_into_A .proc
lda cx16.r14+3 ; msb
bmi _negative
bne _positive
lda cx16.r14+2
bne _positive
lda cx16.r14+1
bne _positive
lda cx16.r14
beq _zero
lda #1
_zero
rts
_negative
lda #-1
rts
_positive
lda #1
rts
.pend
func_sqrt16_into_A .proc
; integer square root
@@ -435,6 +457,7 @@ func_peek .proc
; -- read the byte value on the address in AY, into A
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
rts
@@ -444,6 +467,7 @@ func_peekw .proc
; -- read the word value on the address in AY, into AY
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
pha
@@ -454,6 +478,26 @@ func_peekw .proc
rts
.pend
func_peekl .proc
; -- read the ;pmg value on the address in AY, into R14:R15
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
from_scratchW1
ldy #0
lda (P8ZP_SCRATCH_W1),y
sta cx16.r14
iny
lda (P8ZP_SCRATCH_W1),y
sta cx16.r14+1
iny
lda (P8ZP_SCRATCH_W1),y
sta cx16.r14+2
iny
lda (P8ZP_SCRATCH_W1),y
sta cx16.r14+3
rts
.pend
func_pokew .proc
; -- store the word value in AY in the address in P8ZP_SCRATCH_W1
@@ -466,6 +510,35 @@ func_pokew .proc
rts
.pend
func_pokew_scratchW2 .proc
; -- store the word value in AY in the address in P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_REG
ldy #0
sta (P8ZP_SCRATCH_W2),y
iny
lda P8ZP_SCRATCH_REG
sta (P8ZP_SCRATCH_W2),y
rts
.pend
func_pokel .proc
; -- store the long value in R14:R15 in the address in AY
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda cx16.r14
sta (P8ZP_SCRATCH_W1),y
iny
lda cx16.r14+1
sta (P8ZP_SCRATCH_W1),y
iny
lda cx16.r14+2
sta (P8ZP_SCRATCH_W1),y
iny
lda cx16.r14+3
sta (P8ZP_SCRATCH_W1),y
rts
.pend
func_clamp_byte .proc
; signed value in A, result in A
+47
View File
@@ -266,6 +266,25 @@ strcpy .proc
rts
.pend
strncpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; with maximum length to copy in X.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #$ff
- iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
beq +
dex
bne -
iny
lda #0
sta (P8ZP_SCRATCH_W1),y
+ rts
.pend
strcmp_expression .proc
; -- compare strings, result in A
lda _arg_s2
@@ -641,3 +660,31 @@ long_shiftrightX_inplace .proc
bne -
_end rts
.pend
compare_long_0_and_sign .proc
; pointer to 32 bits signed long in P8ZP_SCRATCH_W1, carry Set=check NEGATIVE (<=0), carry Clear=check POSITIVE (>=0)
; returns carry Set if comparison succeeded otherwise carry Clear
ldy #0
lda (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
beq _success
lda (P8ZP_SCRATCH_W1),y
bcc _checkpositive
bmi _success
bpl _fail
_checkpositive bpl _success
_fail clc
rts
_success sec
rts
.pend
+3 -1
View File
@@ -222,6 +222,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=c") ; on C64 (1581 diskdrive) the type for directories is CBM
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -230,6 +231,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -550,7 +552,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
; This file contains the shared routines that work on all targets.
compression {
@@ -160,6 +160,9 @@ sub log2(float value) -> float {
ldy #>FL_LOG2_const
jsr MOVFM
jmp FDIVT
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
; !notreached!
}}
}
+17 -20
View File
@@ -138,31 +138,29 @@ _done
}
}
; TODO convert to ^^uword once code size regression is fixed
sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
sub shellsort_uw(^^uword @requirezp values, ubyte num_elements) {
; sorts the values array (no-split unsigned words).
num_elements--
ubyte gap
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
uword @zp temp = peekw(values+i*$0002)
uword @zp temp = values[i]
ubyte @zp j = i
ubyte @zp k = j-gap
while j>=gap {
uword @zp v = peekw(values+k*$0002)
uword @zp v = values[k]
if v <= temp break
pokew(values+j*$0002, v)
values[j] = v
j = k
k -= gap
}
pokew(values+j*$0002, temp)
values[j] = temp
}
}
}
; TODO convert to ^^uword once code size regression is fixed
sub shellsort_by_ub(^^ubyte @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
sub shellsort_by_ub(^^ubyte @requirezp ub_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
num_elements--
ubyte @zp gap
@@ -170,7 +168,7 @@ _done
ubyte i
for i in gap to num_elements {
ubyte @zp temp = ub_keys[i]
uword temp_wv = peekw(wordvalues + i*$0002)
uword temp_wv = wordvalues[i]
ubyte @zp j = i
ubyte @zp k = j-gap
repeat {
@@ -178,18 +176,17 @@ _done
if v <= temp break
if j < gap break
ub_keys[j] = v
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
wordvalues[j] = wordvalues[k]
j = k
k -= gap
}
ub_keys[j] = temp
pokew(wordvalues + j*$0002, temp_wv)
wordvalues[j] = temp_wv
}
}
}
; TODO convert to ^^uword once code size regression is fixed
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
sub shellsort_by_uw(^^uword @requirezp uw_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned.
num_elements--
@@ -197,20 +194,20 @@ _done
for gap in [132, 57, 23, 10, 4, 1] {
ubyte i
for i in gap to num_elements {
uword @zp temp = peekw(uw_keys+i*$0002)
uword temp_wv = peekw(wordvalues + i*$0002)
uword @zp temp = uw_keys[i]
uword temp_wv = wordvalues[i]
ubyte @zp j = i
ubyte @zp k = j-gap
while j>=gap {
uword @zp v = peekw(uw_keys+k*2)
uword @zp v = uw_keys[k]
if v <= temp break
pokew(uw_keys+j*2, v)
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002))
uw_keys[j] = v
wordvalues[j] = wordvalues[k]
j = k
k -= gap
}
pokew(uw_keys+j*2, temp)
pokew(wordvalues + j*$0002, temp_wv)
uw_keys[j] = temp
wordvalues[j] = temp_wv
}
}
}
+47 -2
View File
@@ -213,8 +213,6 @@ _found tya
asmsub copy(str source @R0, str target @AY) clobbers(A) -> ubyte @Y {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you don’t have to call this explicitly and can just write string1 = string2
; but this function is useful if you’re dealing with addresses for instance.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -224,6 +222,18 @@ _found tya
}}
}
asmsub ncopy(str source @R0, str target @AY, ubyte maxlength @X) clobbers(A, X) -> ubyte @Y {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strncpy
}}
}
asmsub append(str target @R0, str suffix @R1) clobbers(Y) -> ubyte @A {
; Append the suffix string to the target. (make sure the buffer is large enough!)
; Returns the length of the resulting string.
@@ -249,6 +259,41 @@ _found tya
}}
}
asmsub nappend(str target @R0, str suffix @R1, ubyte maxlength @X) clobbers(Y) -> ubyte @A {
; Append the suffix string to the target. (make sure the buffer is large enough!)
; Returns the length of the resulting string.
%asm {{
lda cx16.r0
ldy cx16.r0+1
jsr length
sty P8ZP_SCRATCH_B1
cpx P8ZP_SCRATCH_B1
beq _max_too_small
bmi _max_too_small
txa
sec
sbc P8ZP_SCRATCH_B1
sta P8ZP_SCRATCH_B1
tya
clc
adc cx16.r0
sta P8ZP_SCRATCH_W1
lda cx16.r0+1
adc #0
sta P8ZP_SCRATCH_W1+1
lda cx16.r1
ldy cx16.r1+1
ldx P8ZP_SCRATCH_B1
jsr prog8_lib.strncpy
tya
clc
adc P8ZP_SCRATCH_B1
rts
_max_too_small
rts
}}
}
asmsub compare(str string1 @R0, str string2 @AY) clobbers(Y) -> byte @A {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
+3
View File
@@ -0,0 +1,3 @@
bcd {
; virtual machine does not support BCD
}
+20
View File
@@ -279,6 +279,26 @@ sub hex2uword(str string) -> uword {
}
}
sub hex2long(str string) -> long {
; -- hexadecimal string (with or without '$') to long.
; stops parsing at the first character that's not a hex digit (except leading $)
long result
ubyte char
if @(string)=='$'
string++
repeat {
char = @(string)
if char==0
return result
result <<= 4
if char>='0' and char<='9'
result |= char-'0'
else
result |= char-'a'+10
string++
}
}
sub bin2uword(str string) -> uword {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
+3 -3
View File
@@ -206,7 +206,7 @@ diskio {
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,0
load.b r99100,#0
loadm.w r99000,diskio.save.filenameptr
loadm.w r99001,diskio.save.start_address
loadm.w r99002,diskio.save.savesize
@@ -218,7 +218,7 @@ diskio {
; like save() but omits the 2 byte prg header.
sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,1
load.b r99100,#1
loadm.w r99000,diskio.save_raw.filenameptr
loadm.w r99001,diskio.save_raw.start_address
loadm.w r99002,diskio.save_raw.savesize
@@ -284,7 +284,7 @@ diskio {
; get the load adress from a PRG file (usually $0801 but it can be different)
if f_open(filename) {
uword address
f_read(&address, 2)
void f_read(&address, 2)
f_close()
return address
}
+4 -36
View File
@@ -302,23 +302,11 @@ monogfx {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
last_y4 = new_y4
}
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
yy++
if decisionOver2>=0 {
radius--
@@ -327,10 +315,6 @@ monogfx {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
}
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
@@ -340,23 +324,11 @@ monogfx {
return
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-radius
uword last_y3 = ycenter+radius
uword last_y4 = ycenter-radius
uword new_y3, new_y4
while radius>=yy {
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
new_y3 = ycenter+radius
if new_y3 != last_y3 {
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
last_y3 = new_y3
}
new_y4 = ycenter-radius
if new_y4 != last_y4 {
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
last_y4 = new_y4
}
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
yy++
if decisionOver2>=0 {
radius--
@@ -365,10 +337,6 @@ monogfx {
decisionOver2 += yy*$0002
decisionOver2++
}
; draw the final two spans
yy--
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
}
sub plot(uword @zp xx, uword @zp yy, bool @zp draw) {
+24 -3
View File
@@ -106,12 +106,23 @@ strings {
sub copy(str source, str target) -> ubyte {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you don’t have to call this explicitly and can just write string1 = string2
; but this function is useful if you’re dealing with addresses for instance.
%ir {{
loadm.w r99000,strings.copy.source
loadm.w r99001,strings.copy.target
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
sub ncopy(str source, str target, ubyte maxlength) -> ubyte {
; Copy a string to another, overwriting that one, but limited to the given length.
; Returns the length of the string that was copied.
%ir {{
loadm.w r99000,strings.ncopy.source
loadm.w r99001,strings.ncopy.target
loadm.b r99100,strings.ncopy.maxlength
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
@@ -123,6 +134,16 @@ strings {
return copy(suffix, target+cx16.r0L) + cx16.r0L
}
sub nappend(str target, str suffix, ubyte maxlength) -> ubyte {
; Append the suffix string to the target, up to the given maximum length of the combined string.
; Returns the length of the resulting string.
cx16.r0L = length(target)
if maxlength<cx16.r0L
return cx16.r0L
maxlength -= cx16.r0L
return ncopy(suffix, target+cx16.r0L, maxlength) + cx16.r0L
}
sub compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
+12 -1
View File
@@ -53,7 +53,8 @@ sys {
%ir {{
loadm.w r99000,sys.internal_stringcopy.source
loadm.w r99001,sys.internal_stringcopy.tgt
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
}}
}
@@ -306,6 +307,16 @@ cx16 {
&word r14s = $ff1e
&word r15s = $ff20
; signed long versions
&long r0r1sl = $ff02
&long r2r3sl = $ff06
&long r4r5sl = $ff0a
&long r6r7sl = $ff0e
&long r8r9sl = $ff12
&long r10r11sl = $ff16
&long r12r13sl = $ff1a
&long r14r15sl = $ff1e
; ubyte versions (low and high bytes)
&ubyte r0L = $ff02
&ubyte r1L = $ff04
+8 -3
View File
@@ -8,7 +8,7 @@ txt {
sub width() -> ubyte {
%ir {{
syscall 46 (): r99000.w
lsig.b r99100,r99000
lsigb.w r99100,r99000
returnr.b r99100
}}
}
@@ -16,11 +16,16 @@ sub width() -> ubyte {
sub height() -> ubyte {
%ir {{
syscall 46 (): r99000.w
msig.b r99100,r99000
msigb.w r99100,r99000
returnr.b r99100
}}
}
sub size() -> ubyte, ubyte {
; -- returns the text screen width and height (number of columns and rows)
return width(), height()
}
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{
@@ -171,7 +176,7 @@ sub input_chars (str buffer) -> ubyte {
; It assumes the keyboard is selected as I/O channel!
%ir {{
loadm.w r99000,txt.input_chars.buffer
load.b r99100,80
load.b r99100,#80
syscall 6 (r99000.w, r99100.b): r99100.b
returnr.b r99100
}}
+78 -12
View File
@@ -2,6 +2,7 @@ package prog8
import kotlinx.cli.*
import prog8.ast.AstException
import prog8.buildversion.VERSION
import prog8.code.source.ImportFileSystem.expandTilde
import prog8.code.target.CompilationTargets
import prog8.code.target.Cx16Target
@@ -12,13 +13,10 @@ import prog8.compiler.CompilerArguments
import prog8.compiler.ErrorReporter
import prog8.compiler.compileProgram
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds
import java.nio.file.WatchKey
import java.net.URI
import java.nio.file.*
import java.time.LocalDateTime
import kotlin.io.path.Path
import kotlin.io.path.isReadable
import kotlin.io.path.*
import kotlin.system.exitProcess
@@ -46,6 +44,8 @@ private fun compileMain(args: Array<String>): Boolean {
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
val libSearch by cli.option(ArgType.String, fullName = "libsearch", description = "search for a regex pattern in the embedded library files")
val libDump by cli.option(ArgType.String, fullName = "libdump", description = "dump all the embedded library files into the specified output directory")
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
@@ -60,14 +60,13 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
@@ -90,6 +89,16 @@ private fun compileMain(args: Array<String>): Boolean {
if(quietAll!=true)
banner()
if(libDump!=null) {
scanLibraryFiles(libDump, null)
return true
}
if(libSearch!=null) {
scanLibraryFiles(null, libSearch)
return true
}
val outputPath = pathFrom(outputDir)
if(!outputPath.toFile().isDirectory) {
System.err.println("Output path doesn't exist")
@@ -136,7 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
System.err.println("On the Commander X16, HIRAM bank 0 is used by the kernal and can't be used.")
return false
}
@@ -193,7 +202,6 @@ private fun compileMain(args: Array<String>): Boolean {
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
dontSplitWordArrays == true,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,
@@ -278,7 +286,6 @@ private fun compileMain(args: Array<String>): Boolean {
slabsHighBank,
slabsGolden == true,
compilationTarget!!,
dontSplitWordArrays == true,
breakpointCpuInstruction,
printAst1 == true,
printAst2 == true,
@@ -363,3 +370,62 @@ fun runVm(irFilename: String, quiet: Boolean) {
val vmdef = VMTarget()
vmdef.launchEmulator(0, irFile, quiet)
}
private fun scanLibraryFiles(dump: String?, searchPattern: String?) {
val library_prefix = "/prog8lib"
val dumpPath = if(dump!=null) pathFrom(dump) else null
val pattern = searchPattern?.toRegex(RegexOption.IGNORE_CASE)
if(pattern!=null) {
println("You can also have a look in the documentation for the libraries at https://prog8.readthedocs.io/en/latest/libraries.html")
println("The library source files are available in the Github repository at https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib")
println("Searching for pattern '$pattern' in embedded library files.\n")
}
if(dumpPath!=null) {
println("Dumping embedded library files into $dumpPath\n")
dumpPath.createDirectories()
val license = dumpPath / "${library_prefix.drop(1)}-$VERSION/LICENSE.txt"
license.parent.createDirectories()
license.writeText("These library files belong to the Prog8 compiler project, see https://github.com/irmen/prog8/\n" +
"They are licensed under the GNU GPL 3.0 software license, see https://www.gnu.org/licenses/gpl.html\n")
}
fun search(path: Path) {
val hits = mutableListOf<String>()
path.useLines { lines ->
lines.forEachIndexed { index, line ->
if (pattern!!.containsMatchIn(line)) {
hits.add("${(index + 1).toString().padStart(6)}: ${line.trimStart()}")
}
}
}
if(hits.isNotEmpty()) {
println("Found in Library file '${path.absolutePathString().drop(library_prefix.length+1)}':")
hits.forEach(::println)
println()
}
}
fun dump(path: Path) {
val targetfolder = dumpPath!! / (path.first().pathString + "-$VERSION")
val target = targetfolder / path.pathString.drop(library_prefix.length+1)
target.parent.createDirectories()
target.writeBytes(path.readBytes())
}
// Get the location of the current JAR
val jarUrl = object {}.javaClass.protectionDomain.codeSource.location
FileSystems.newFileSystem(URI.create("jar:$jarUrl"), emptyMap<String, String>()).use { fs ->
val dir = fs.getPath(library_prefix)
Files.walk(dir).use { paths ->
paths.filter { Files.isRegularFile(it) }.forEach { path ->
if(pattern!=null)
search(path)
else
dump(path)
}
}
}
}
@@ -20,10 +20,24 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"sqrt__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) {
val value=it.toDouble()
if(value<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(value)
} },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) {
if(it<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(it)
} },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsb__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 65535).toDouble() } },
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} },
"msb__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 24 and 255).toDouble()} },
"msw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 16 and 65535).toDouble()} },
"mkword" to ::builtinMkword,
"mklong" to ::builtinMklong,
@@ -69,11 +83,14 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
} else {
if(constval.type!=BaseDataType.UBYTE && constval.type!=BaseDataType.UWORD)
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
}
val integer = constval.number.toInt()
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
val result = function(integer)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral.optimalInteger(result.toInt(), args[0].position)
}
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
@@ -82,8 +99,10 @@ private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, p
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != BaseDataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position)
return NumericLiteral(BaseDataType.FLOAT, function(constval.number), args[0].position)
val result = function(constval.number)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral(BaseDataType.FLOAT, result, args[0].position)
}
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteral {
@@ -136,7 +155,14 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val elementDt = dt.getOrUndef().elementType()
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
}
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
dt.isString -> {
if(target is VarDecl) {
val length = (target.value as StringLiteral).value.length + 1 // add the closing 0-byte
NumericLiteral(BaseDataType.UBYTE, length.toDouble(), position)
}
else
throw SyntaxError("sizeof(str) is undefined here. Perhaps use len, or strings.length?", position)
}
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
}
} else {
+7 -8
View File
@@ -35,6 +35,7 @@ import kotlin.time.measureTimedValue
class CompilationResult(val compilerAst: Program, // deprecated, use codegenAst instead
val codegenAst: PtProgram?,
val codegenSymboltable: SymbolTable?,
val compilationOptions: CompilationOptions,
val importedFiles: List<Path>)
@@ -55,7 +56,6 @@ class CompilerArguments(val filepath: Path,
val slabsHighBank: Int?,
val slabsGolden: Boolean,
val compilationTarget: String,
val dontSplitWordArrays: Boolean,
val breakpointCpuInstruction: String?,
val printAst1: Boolean,
val printAst2: Boolean,
@@ -88,6 +88,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
}
try {
var symbolTable: SymbolTable? = null
val totalTime = measureTime {
val libraryDirs = if(compTarget.libraryPath!=null) listOf(compTarget.libraryPath.toString()) else emptyList()
val (parseresult, parseDuration) = measureTimedValue {
@@ -120,7 +122,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
varsGolden = args.varsGolden
slabsHighBank = args.slabsHighBank
slabsGolden = args.slabsGolden
dontSplitWordArrays = args.dontSplitWordArrays
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
}
@@ -178,19 +179,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
println("*********** COMPILER AST END *************\n")
}
var symbolTable: SymbolTable
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
symbolTable = stMaker.make()
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
postprocessSimplifiedAst(intermediateAst, symbolTable!!, compilationOptions, args.errors)
args.errors.report()
symbolTable = stMaker.make() // need an updated ST because the postprocessing changes stuff
if (compilationOptions.optimize) {
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable!!, args.errors)
args.errors.report()
symbolTable = stMaker.make() // need an updated ST because the optimization changes stuff
}
@@ -210,7 +209,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
createAssemblyDuration = measureTime {
if (!createAssemblyAndAssemble(
intermediateAst,
symbolTable,
symbolTable!!,
args.errors,
compilationOptions,
program.generatedLabelSequenceNumber
@@ -251,7 +250,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
if(!args.quietAll) {
println("\nTotal compilation+assemble time: ${totalTime.toString(DurationUnit.SECONDS, 3)}.")
}
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
return CompilationResult(resultingProgram!!, ast, symbolTable, compilationOptions, importedFiles)
} catch (px: ParseError) {
args.errors.printSingleError("${px.position.toClickableStr()} parse error: ${px.message}".trim())
} catch (ac: ErrorsReportedException) {
@@ -50,8 +50,8 @@ class ModuleImporter(private val program: Program,
fun importImplicitLibraryModule(name: String): Module? {
val import = Directive("%import", listOf(
DirectiveArg(name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
), Position("<<<implicit-import>>>", 0, 0, 0))
DirectiveArg(name, 42u, position = Position("~implicit-import~", 0, 0, 0))
), Position("~implicit-import~", 0, 0, 0))
return executeImportDirective(import, null)
}
@@ -497,8 +497,8 @@ internal class AstChecker(private val program: Program,
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
}
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && param.first.type!=DataType.pointer(BaseDataType.UBYTE)) {
err("parameter '${param.first.name}' should be (u)word, str or ^^ubyte")
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && !param.first.type.isPointer) {
err("parameter '${param.first.name}' should be (u)word, str or a pointer")
}
}
else if(param.second.statusflag!=null) {
@@ -588,8 +588,8 @@ internal class AstChecker(private val program: Program,
else {
if(!compilerOptions.ignoreFootguns)
errors.warn("\uD83D\uDCA3 footgun: reusing R0-R15 as parameters risks overwriting due to clobbering or no callstack", subroutine.position)
if(!p.type.isWord && !p.type.isByteOrBool) {
errors.err("can only use register param when type is boolean, byte or word", p.position)
if(!p.type.isWordOrByteOrBool && !p.type.isPointer) {
errors.err("can only use register param when type is boolean, byte, word or pointer", p.position)
}
}
}
@@ -651,6 +651,14 @@ internal class AstChecker(private val program: Program,
}
override fun visit(assignment: Assignment) {
if(assignment.target.inferType(program).isString) {
if(assignment.isAugmentable)
errors.err("cannot assign to string by value (use strings.(n)copy or strings.(n)append)", assignment.position)
else
errors.err("cannot assign to string by value (use strings.(n)copy)", assignment.position)
return
}
if(assignment.target.multi==null) {
val targetDt = assignment.target.inferType(program)
val valueDt = assignment.value.inferType(program)
@@ -874,9 +882,9 @@ internal class AstChecker(private val program: Program,
err("recursive var declaration")
// CONST can only occur on simple types (byte, word, float)
if(decl.type== VarDeclType.CONST) {
if(decl.type==VarDeclType.CONST) {
if (!decl.datatype.isNumericOrBool)
err("const can only be used on numeric types or booleans")
err("const can only be used on numbers and booleans")
}
// FLOATS enabled?
@@ -1090,8 +1098,8 @@ internal class AstChecker(private val program: Program,
}
if(decl.datatype.isPointerArray) {
if(decl.splitwordarray!= SplitWish.SPLIT)
errors.err("pointer arrays can only be @split", decl.position)
if(decl.splitwordarray==SplitWish.NOSPLIT)
errors.err("pointer arrays can only be split", decl.position)
}
if(decl.datatype.isStructInstance && decl.origin!=VarDeclOrigin.SUBROUTINEPARAM) {
@@ -1452,7 +1460,7 @@ internal class AstChecker(private val program: Program,
} else if(rightIndexer!=null) {
val leftDt = expr.left.inferType(program)
if(leftDt.isStructInstance) {
TODO("pointer[x].field[y] ??")
TODO("pointer[x].field[y] ?? ${expr.left.position}")
// // pointer[x].field[y] --> type is the dt of 'field'
// var struct = leftDt.getOrUndef().subType
// if (struct==null) {
@@ -1500,8 +1508,12 @@ internal class AstChecker(private val program: Program,
}
if(expr.operator=="+" || expr.operator=="-") {
if(leftDt.isString || rightDt.isString || leftDt.isArray || rightDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.position)
if(leftDt.isString || leftDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.left.position)
return
}
if(rightDt.isString || rightDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.right.position)
return
}
}
@@ -1639,6 +1651,10 @@ internal class AstChecker(private val program: Program,
errors.err("invalid type cast", typecast.position)
}
if(typecast.implicit && typecast.type.isLong && typecast.expression.inferType(program).isPointer) {
errors.err("cannot use a pointer as a long, a pointer is an unsigned word", typecast.position)
}
super.visit(typecast)
}
@@ -1946,7 +1962,7 @@ internal class AstChecker(private val program: Program,
} else if (target.datatype.isString) {
if (target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteral).value.length
val stringLen = (target.value as StringLiteral).value.length + 1 // include the 0-byte terminator
if (index != null && index !in 0..<stringLen)
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
@@ -2061,6 +2077,8 @@ internal class AstChecker(private val program: Program,
!iterableEltDt.isByte
} else if (elementDt.isWords) {
!iterableEltDt.isWord
} else if (elementDt.isLong) {
!iterableEltDt.isLong
} else {
false
}
@@ -2075,7 +2093,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memread: DirectMemoryRead) {
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
if(memread.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memread.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
val pointervar = memread.addressExpression as? IdentifierReference
@@ -2088,6 +2108,12 @@ internal class AstChecker(private val program: Program,
super.visit(memread)
}
override fun visit(numLiteral: NumericLiteral) {
super.visit(numLiteral)
if(numLiteral.type== BaseDataType.LONG) // other smaller types have already been validated
checkValueTypeAndRange(DataType.forDt(numLiteral.type), numLiteral)
}
private fun allowedMemoryAccessAddressExpression(addressExpression: Expression, program: Program): Boolean {
val dt = addressExpression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByteOrBool==true))
@@ -2102,7 +2128,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
if(memwrite.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memwrite.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
val pointervar = memwrite.addressExpression as? IdentifierReference
@@ -2347,7 +2375,7 @@ internal class AstChecker(private val program: Program,
if(value.type==BaseDataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
val number=value.number
if (number < -2147483647 || number > 2147483647)
if (number < -2147483648.0 || number > 2147483647.0)
return err("value '$number' out of range for long")
}
targetDt.isArray -> {
@@ -2459,7 +2487,7 @@ internal class AstChecker(private val program: Program,
val sourceIsBitwiseOperatorExpression = (sourceValue as? BinaryExpression)?.operator in BitwiseOperators
if(sourceDatatype.isWord && targetDatatype.isByte)
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
else if(sourceDatatype.isFloat && targetDatatype.isInteger)
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else if(targetDatatype.isUnsignedWord && (sourceDatatype.isPassByRef || sourceDatatype.isPointer)) {
// this is allowed: a pass-by-reference or pointer datatype into an uword (untyped pointer value).
@@ -34,8 +34,14 @@ internal fun Program.reorderStatements(options: CompilationOptions, errors: IErr
internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && changer.applyModifications() > 0)
changer.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("changeNotExpressionAndIfComparisonExpr() is looping endlessly")
}
}
@@ -93,8 +99,15 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
val desugar = CodeDesugarer(this, options.compTarget, errors)
desugar.visit(this)
while(errors.noErrors() && desugar.applyModifications()>0)
desugar.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && desugar.applyModifications() > 0)
desugar.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("desugaring() is looping endlessly")
}
}
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
@@ -121,16 +134,30 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0)
lit2decl.visit(this)
for(numCycles in 0..1000) {
if(errors.noErrors() && lit2decl.applyModifications() > 0)
lit2decl.visit(this)
else
break
if(numCycles==1000) {
throw InternalCompilerException("checkIdentifiers() is looping endlessly - check for circular aliases")
}
}
}
}
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options)
process.visit(this)
while(errors.noErrors() && process.applyModifications()>0) {
process.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && process.applyModifications() > 0)
process.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("variousCleanups() is looping endlessly")
}
}
@@ -44,6 +44,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
if(alias.alias == alias.target.nameInSource.first())
errors.err("alias references itself", alias.position)
}
override fun visit(block: Block) {
@@ -96,6 +99,50 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(decl)
}
private val keywords = setOf(
"void",
"on",
"in",
"to",
"if",
"downto",
"else",
"step",
"call",
"goto",
"inline",
"struct",
"then",
"alias",
"defer",
"const",
"sizeof",
"as",
"return",
"break",
"continue",
"true",
"false",
"sub",
"asmsub",
"extsub",
"clobbers",
"while",
"do",
"until",
"repeat",
"unroll",
"when"
)
override fun visit(struct: StructDecl) {
if(struct.name in BuiltinFunctions)
errors.err("builtin function cannot be redefined", struct.position)
else if(struct.name in keywords)
errors.err("struct name cannot be a keyword", struct.position)
super.visit(struct)
}
override fun visit(subroutine: Subroutine) {
if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
@@ -69,6 +69,13 @@ class AstPreprocessor(val program: Program,
return super.before(string, parent)
}
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if (parent is RangeExpression)
return noModifications
val constValue = expr.constValue(program) ?: return noModifications
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
}
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
val modifications = mutableListOf<IAstModification>()
@@ -113,8 +120,6 @@ class AstPreprocessor(val program: Program,
val replacements = mutableListOf<IAstModification>()
for(decl in vars) {
if(shouldUnSplitArray(decl))
continue // unsplitting must be done first
if(decl.type != VarDeclType.VAR) {
movements.add(IAstModification.InsertFirst(decl, parentscope))
replacements.add(IAstModification.Remove(decl, scope))
@@ -198,10 +203,6 @@ class AstPreprocessor(val program: Program,
}
}
if(shouldUnSplitArray(decl)) {
return makeUnSplitArray(decl)
}
// convert all antlr names to structs
val antlrTypeName = decl.datatype.subTypeFromAntlr
if(antlrTypeName!=null) {
@@ -232,18 +233,6 @@ class AstPreprocessor(val program: Program,
return noModifications
}
private fun shouldUnSplitArray(decl: VarDecl): Boolean =
options.dontSplitWordArrays && decl.datatype.isSplitWordArray
private fun makeUnSplitArray(decl: VarDecl): Iterable<IAstModification> {
val splitDt = DataType.arrayFor(decl.datatype.sub!!, false)
val newDecl = VarDecl(
decl.type, decl.origin, splitDt, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, emptyList(),
decl.value?.copy(), decl.sharedWithAsm, decl.alignment, false, decl.position
)
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// For non-kernal subroutines and non-asm parameters:
// inject subroutine params as local variables (if they're not there yet).
@@ -269,7 +258,7 @@ class AstPreprocessor(val program: Program,
.filter { it.name !in namesInSub && it.name !in existingAliases }
.forEach {
if (it.registerOrPair in Cx16VirtualRegisters) {
if(it.type.isWordOrByteOrBool) {
if(it.type.isWordOrByteOrBool || it.type.isPointer) {
val mappedParamVar = VarDecl.fromParameter(it)
mods += IAstModification.InsertFirst(mappedParamVar, subroutine)
} else {
@@ -56,7 +56,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
if(subroutine.returntypes.isNotEmpty())
errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
else {
val returnStmt = Return(arrayOf(), subroutine.position)
val returnStmt = Return(arrayOf(), Position.DUMMY)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
} else {
@@ -68,7 +68,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
// .... we cannot return this as an error, because that also breaks legitimate cases where the return is done from within a nested scope somewhere
// errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
} else {
val returnStmt = Return(arrayOf(), subroutine.position)
val returnStmt = Return(arrayOf(), Position.DUMMY)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
@@ -297,6 +297,17 @@ _after:
val peek = FunctionCallExpression(IdentifierReference(listOf("peekbool"), arrayIndexedExpression.position), mutableListOf(address), arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, peek, parent))
}
} else if(arrayVar.datatype.sub==BaseDataType.LONG) {
// use peekl/pokel
if(parent is AssignTarget) {
val assignment = parent.parent as Assignment
val args = mutableListOf(address, assignment.value)
val poke = FunctionCallStatement(IdentifierReference(listOf("pokel"), arrayIndexedExpression.position), args, false, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(assignment, poke, assignment.parent))
} else {
val peek = FunctionCallExpression(IdentifierReference(listOf("peekl"), arrayIndexedExpression.position), mutableListOf(address), arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, peek, parent))
}
} else if(arrayVar.datatype.sub==BaseDataType.FLOAT) {
// use peekf/pokef
if(parent is AssignTarget) {
@@ -313,7 +324,7 @@ _after:
return noModifications
} else if(arrayVar!=null) {
// it could be a pointer dereference instead of a simple array variable
TODO("deref[word] rewrite ???? $arrayIndexedExpression")
TODO("deref[word] rewrite ???? ${arrayIndexedExpression.position}")
// val dt = arrayIndexedExpression.plainarrayvar!!.traverseDerefChainForDt(null)
// if(dt.isUnsignedWord) {
// // ptr.field[index] --> @(ptr.field + index)
@@ -364,7 +375,8 @@ _after:
if(expr.operator=="*" && expr.inferType(program).isInteger && expr.left isSameAs expr.right) {
// replace squaring with call to builtin function to do this in a more optimized way
val function = if(expr.left.inferType(program).isBytes) "prog8_lib_square_byte" else "prog8_lib_square_word"
val leftDt = expr.left.inferType(program)
val function = if(leftDt.isBytes) "prog8_lib_square_byte" else if(leftDt.isWords) "prog8_lib_square_word" else "prog8_lib_square_long"
val squareCall = FunctionCallExpression(
IdentifierReference(listOf(function), expr.position),
mutableListOf(expr.left.copy()), expr.position)
@@ -609,10 +621,9 @@ _after:
else
mutableListOf(conditionVar, assignIndex!!, ifSt)
, ongoto.position)
val declscope = parent.definingScope
return listOf(
IAstModification.ReplaceNode(ongoto, replacementScope, parent),
IAstModification.InsertFirst(jumplistArray, declscope)
IAstModification.InsertFirst(jumplistArray, ongoto.definingScope)
)
}
@@ -651,7 +662,7 @@ _after:
val indexer = ArrayIndexedExpression(null, ptrDeref, idx.indexer, idx.position)
return listOf(IAstModification.ReplaceNode(expr, indexer, expr.parent))
} else {
TODO("convert ptr.p[idx]")
TODO("convert ptr.p[idx] ${idx.position}")
}
}
}
@@ -129,6 +129,7 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
val target = alias.target.targetStatement()
if(target is Alias) {
// shortcut the alias that refers to another alias
val newAlias = Alias(alias.alias, target.target, alias.position)
return listOf(IAstModification.ReplaceNode(alias, newAlias, parent))
}
@@ -212,8 +213,8 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
val tgt2 = deref.definingScope.lookup(deref.chain.take(1)) as? Alias
if(tgt2!=null && parent !is Alias) {
if(tgt2.target.targetStatement() !is Alias) {
val actual = IdentifierReference(tgt2.target.nameInSource + deref.chain.drop(1), deref.position)
return listOf(IAstModification.ReplaceNode(deref, actual, parent))
val unaliased = PtrDereference(tgt2.target.nameInSource + deref.chain.drop(1), deref.derefLast, deref.position)
return listOf(IAstModification.ReplaceNode(deref, unaliased, parent))
}
}
return noModifications
@@ -298,15 +298,6 @@ internal class StatementReorderer(
checkCopyArrayValue(assignment)
}
if(!assignment.isAugmentable) {
if (targetType issimpletype BaseDataType.STR) {
if (valueType issimpletype BaseDataType.STR || valueType.getOrUndef() == DataType.pointer(BaseDataType.UBYTE)) {
// replace string assignment by a call to stringcopy
return copyStringValue(assignment)
}
}
}
return noModifications
}
@@ -372,19 +363,6 @@ internal class StatementReorderer(
}
}
private fun copyStringValue(assign: Assignment): List<IAstModification> {
val identifier = assign.target.identifier!!
val strcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "internal_stringcopy"), assign.position),
mutableListOf(
assign.value as? IdentifierReference ?: assign.value,
identifier
),
false,
assign.position
)
return listOf(IAstModification.ReplaceNode(assign, strcopy, assign.parent))
}
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
if(parent is AssignTarget) {
val zeroIndexer = deref.chain.firstOrNull { it.second?.constIndex()==0 }
@@ -90,7 +90,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(leftDt.isKnown && rightDt.isKnown) {
if(expr.operator=="<<" && leftDt.isBytes) {
// uword ww = 1 << shift --> make the '1' a word constant
// uword/ulong ww = 1 << shift --> make the '1' a word (or long) constant
val leftConst = expr.left.constValue(program)
if(leftConst!=null) {
val leftConstAsWord =
@@ -98,19 +98,22 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
NumericLiteral(BaseDataType.UWORD, leftConst.number, leftConst.position)
else
NumericLiteral(BaseDataType.WORD, leftConst.number, leftConst.position)
val leftConstAsLong = NumericLiteral(BaseDataType.LONG, leftConst.number, leftConst.position)
val modifications = mutableListOf<IAstModification>()
if (parent is Assignment) {
if (parent.target.inferType(program).isWords) {
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
// if(rightDt.isBytes)
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
}
else if (parent.target.inferType(program).isLong) {
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsLong, expr)
}
} else if (parent is TypecastExpression && parent.type.isUnsignedWord && parent.parent is Assignment) {
val assign = parent.parent as Assignment
if (assign.target.inferType(program).isWords) {
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
// if(rightDt.isBytes)
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
}
else if (assign.target.inferType(program).isLong) {
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsLong, expr)
}
}
if(modifications.isNotEmpty())
@@ -129,7 +132,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
expr))
}
if(rightCv!=null && rightCv.number<0) {
val value = if(leftDt.isBytes) 256+rightCv.number else 65536+rightCv.number
val value = if(leftDt.isBytes) 256+rightCv.number else if(leftDt.isWords) 65536+rightCv.number else (0x100000000L+rightCv.number).toLong().toInt().toDouble()
return listOf(IAstModification.ReplaceNode(
expr.right,
NumericLiteral(leftDt.getOrUndef().base, value, expr.right.position),
@@ -268,7 +271,18 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
val cvalue = assignment.value.constValue(program)
if(cvalue!=null) {
return castLiteral(cvalue)
val castResult = castLiteral(cvalue)
if(castResult.isNotEmpty())
return castResult
// exception for @(...) = -byte (direct memory write) it's just convenient to be able to write negative numbers too (like pokew also allows)
if(assignment.target.memoryAddress!=null) {
if(valuetype.isByteOrBool && targettype.isUnsignedByte) {
require(cvalue.number.toInt() in -128..127)
val asUByte = NumericLiteral(BaseDataType.UBYTE, cvalue.number.toInt().toUByte().toDouble(), assignment.value.position)
return listOf(IAstModification.ReplaceNode(assignment.value, asUByte, assignment))
}
}
}
}
}
@@ -325,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memread.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -340,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memwrite.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -27,78 +27,62 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(valueType.isUnknown)
return noModifications
val valueDt = valueType.getOrUndef()
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
// using a array of words as initializer to a pointer array is fine
if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray)
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else if(!decl.datatype.isString) {
if (valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value?.constValue(program)
if (constValue != null)
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
if(!(valueDt.isWord && decl.datatype.isPointer)) {
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
// using a array of words as initializer to a pointer array is fine
if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray)
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else if(!decl.datatype.isString) {
if (valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value?.constValue(program)
if (constValue != null)
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
}
}
}
}
VarDeclType.CONST -> {
// change the vardecl type itself as well, but only if new type is smaller
if(valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
if(!decl.datatype.isLong &&
(valueDt.isSigned && decl.datatype.isUnsigned ||
valueDt.isUnsigned && decl.datatype.isSigned))
{
VarDeclType.CONST -> {
// change the vardecl type itself as well, but only if new type is smaller
if(valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
val changed = decl.copy(valueDt)
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
if(!decl.datatype.isLong &&
(valueDt.isSigned && decl.datatype.isUnsigned ||
valueDt.isUnsigned && decl.datatype.isSigned))
{
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
val changed = decl.copy(valueDt)
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
}
}
}
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
throw FatalAstException("value type for a memory var should be word or byte (address)")
}
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
throw FatalAstException("value type for a memory var should be word or byte (address)")
}
}
// check splitting of word arrays
if(decl.splitwordarray != SplitWish.DONTCARE && !decl.datatype.isWordArray && !decl.datatype.isPointerArray) {
if(decl.origin != VarDeclOrigin.ARRAYLITERAL)
errors.err("@split and @nosplit are for word or pointer arrays only", decl.position)
errors.err("@nosplit is for word or pointer arrays only", decl.position)
}
if(decl.datatype.isWordArray) {
var changeDataType: DataType?
var changeSplit: SplitWish = decl.splitwordarray
when(decl.splitwordarray) {
SplitWish.DONTCARE -> {
if(options.dontSplitWordArrays) {
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
changeSplit = SplitWish.NOSPLIT
}
else {
changeDataType = if(decl.datatype.isSplitWordArray) null else {
val eltDt = decl.datatype.elementType()
if(eltDt.isPointer)
TODO("convert array of pointers to split words array type")
else
DataType.arrayFor(eltDt.base)
}
changeSplit = SplitWish.SPLIT
}
}
SplitWish.SPLIT -> {
changeDataType = if(decl.datatype.isSplitWordArray) null else {
val eltDt = decl.datatype.elementType()
if(eltDt.isPointer)
TODO("convert array of pointers to split words array type")
TODO("convert array of pointers to split words array type ${decl.position}")
else
DataType.arrayFor(eltDt.base)
}
@@ -115,7 +99,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
value = ArrayLiteral(InferredTypes.knownFor(changeDataType), value.value, value.position)
}
val newDecl = VarDecl(decl.type, decl.origin, changeDataType, decl.zeropage,
changeSplit, decl.arraysize, decl.name, decl.names,
decl.splitwordarray, decl.arraysize, decl.name, decl.names,
value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
return listOf(IAstModification.ReplaceNode(decl, newDecl, parent))
}
@@ -225,17 +209,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
if(expr.operator=="<<") {
// << X --> X (X is word or byte)
// << X --> X (X is long or word or byte)
val valueDt = expr.expression.inferType(program)
if(valueDt.isBytes || valueDt.isWords) {
if(valueDt.isInteger) {
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
}
}
if(expr.operator=="^") {
// ^ X --> 0 (X is word or byte)
// ^ X --> 0 (X is long word or byte)
val valueDt = expr.expression.inferType(program)
if(valueDt.isBytes || valueDt.isWords) {
if(valueDt.isInteger) {
val zero = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.expression.position)
return listOf(IAstModification.ReplaceNode(expr, zero, parent))
}
@@ -448,9 +432,19 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
} else if(target?.datatype?.isString==true) {
val stringsize = (target.value as StringLiteral).value.length
if(stringsize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
}
// replace the negative index by the normal index
val newIndex = NumericLiteral.optimalNumeric(stringsize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
}
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("cleanup pointer indexing")
TODO("cleanup pointer indexing ${arrayIndexedExpression.position}")
}
}
return noModifications
@@ -486,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
if(addressOf.arrayIndex!=null) {
val tgt = addressOf.identifier?.constValue(program)
if (tgt != null && tgt.type.isWord) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(
IAstModification.ReplaceNode(addressOf, add, parent)
)
val tgt = addressOf.identifier
if(tgt!=null) {
val constAddress = tgt.constValue(program)
if (constAddress != null && constAddress.type.isInteger) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
} else {
val decl = tgt.targetVarDecl()
if(decl!=null && decl.datatype.isInteger) {
// &addressvar[idx] --> addressvar + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) istype decl.datatype)
indexExpr
else
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
}
}
}
}
return noModifications
@@ -145,7 +145,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
if(addrOf!=null) {
val identType = addrOf.identifier?.inferType(program)?.getOrUndef()
if(identType?.isSplitWordArray==true) {
return Pair("argument ${mismatch + 1} type mismatch, was: $actual (because arg is a @split word array) expected: $expected", call.args[mismatch].position)
return Pair("argument ${mismatch + 1} type mismatch, was: $actual (because arg is a split word array) expected: $expected", call.args[mismatch].position)
}
}
}
+8 -8
View File
@@ -149,8 +149,8 @@ class TestModuleImporter: FunSpec({
shouldThrow<ParseError> { act() }.let {
it.position.file shouldBe SourceCode.relative(srcPath).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
@@ -169,8 +169,8 @@ class TestModuleImporter: FunSpec({
shouldThrow<ParseError> { act() }.let {
it.position.file shouldBe SourceCode.relative(imported).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
@@ -230,8 +230,8 @@ class TestModuleImporter: FunSpec({
importer.importImplicitLibraryModule(srcPath.nameWithoutExtension) }.let {
it.position.file shouldBe SourceCode.relative(srcPath).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
@@ -252,8 +252,8 @@ class TestModuleImporter: FunSpec({
act() }.let {
it.position.file shouldBe SourceCode.relative(imported).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
Loaded 100 of 198 files, more files were not shown because too many files have changed in this diff. Show more