Compare commits

..
Author SHA1 Message Date
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
154 changed files with 5536 additions and 2843 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)
+26
View File
@@ -0,0 +1,26 @@
# 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.
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 the smallest by a fair bit.
* Prog8 execution speed places it more or less in the middle of the stack.
Measured with Prog8 V12.0 .
![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)
+71
View File
@@ -0,0 +1,71 @@
%import textio
%import floats
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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]")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uwhex(EXPECTED, true)
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 floats
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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]")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ulhex(EXPECTED, true)
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 floats
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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]")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ubhex(EXPECTED, true)
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
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+85
View File
@@ -0,0 +1,85 @@
%import textio
%import floats
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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]")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
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 floats
main {
sub start() {
txt.lowercase()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
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]")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
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
}
}
+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(""),
+1 -1
View File
@@ -13,7 +13,7 @@ fun Number.toHex(): String {
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
val integer = this.toLong()
if(integer<0)
return '-' + abs(integer).toHex()
return when (integer) {
+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} - 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} - 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
@@ -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,69 @@ $repeatLabel""")
}
}
}
internal fun pushLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
if(numberOfLongs==0) return
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
sta P8ZP_SCRATCH_REG
lda cx16.$startregName
pha
lda cx16.$startregName+1
pha
lda cx16.$startregName+2
pha
lda cx16.$startregName+3
pha
lda P8ZP_SCRATCH_REG""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
sta P8ZP_SCRATCH_REG
sty P8ZP_SCRATCH_B1
ldy #0
- lda cx16.$startregName,y
pha
iny
cpy #$numbytes
bne -
ldy P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_REG""")
}
internal fun popLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
if(numberOfLongs==0) return
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
sta P8ZP_SCRATCH_REG
pla
sta cx16.$startregName+3
pla
sta cx16.$startregName+2
pla
sta cx16.$startregName+1
pla
sta cx16.$startregName
lda P8ZP_SCRATCH_REG""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
sta P8ZP_SCRATCH_REG
sty P8ZP_SCRATCH_B1
ldy #$numbytes-1
- pla
sta cx16.$startregName,y
dey
bpl -
ldy P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_REG""")
}
}
/**
@@ -2430,4 +2528,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')
@@ -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)
@@ -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")
}
}
@@ -1263,12 +1260,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 +1294,7 @@ _jump jmp (${target.asmLabel})
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long <= 0")
TODO("long <= 0 ${value.position}")
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1296,7 +1306,7 @@ _jump jmp (${target.asmLabel})
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long >= 0")
TODO("long >= 0 ${value.position}")
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1390,71 +1400,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 +1484,54 @@ _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(left !is PtNumber && left !is PtIdentifier || right !is PtNumber && right !is PtIdentifier) {
TODO("long comparison $operator with expressions - use temporary long variable instead to simplify for now ${left.position}")
}
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,
@@ -2056,4 +2118,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")
}
}
@@ -791,7 +791,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 +938,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 +1044,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,
@@ -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 -> TODO("memread into float ${target.position}")
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -167,7 +168,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
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.MEMORY -> TODO("memread into pointer ${target.position}")
SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -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 -> TODO("memread into float array ${target.position}")
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 -> TODO("memread into float ${target.position}")
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) {
@@ -858,7 +860,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
@@ -920,7 +922,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
dec $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
sec
lda $variable
@@ -942,7 +944,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 +960,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 +976,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)}
@@ -1438,7 +1440,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
+""")
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons")
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.storeAIntoZpPointerVar(sourceName, false)
@@ -1561,7 +1563,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons")
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2
@@ -248,8 +249,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.loadIndirectFloat(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else if(value.type.isLong)
TODO("load long")
else if(value.type.isLong) {
asmgen.loadIndirectLongIntoR14R15(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.R14R15_32, target)
}
else
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}")
}
@@ -264,6 +267,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if (target.dt.isByte) inplaceByteAdd(target, value)
else if (target.dt.isWord) inplaceWordAdd(target, value)
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
else if (target.dt.isLong) inplaceLongAdd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
}
@@ -275,6 +279,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if (target.dt.isByte) inplaceByteSub(target, value)
else if (target.dt.isWord) inplaceWordSub(target, value)
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
else if (target.dt.isLong) inplaceLongSub(target, value)
else throw AssemblyError("weird dt ${target.position}")
}
}
@@ -282,41 +287,48 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
else if(target.dt.isWord) inplaceWordMul(target, value)
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FMULT", value)
else if(target.dt.isLong) TODO("inplace long mul ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"/" -> {
if(target.dt.isByte) TODO("inplaceByteDiv(target, value) ${target.position}")
else if(target.dt.isWord) inplaceWordDiv(target, value)
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FDIV", value)
else if(target.dt.isLong) TODO("inplace long div ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"%" -> TODO("inplace ptr %")
"%" -> TODO("inplace ptr % ${target.position}")
"<<" -> {
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else if(target.dt.isLong) TODO("inplace long << ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
">>" -> {
if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else if(target.dt.isLong) TODO("inplace long >> ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(target, value)
else if(target.dt.isLong) inplaceLongAnd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(target, value)
else if(target.dt.isLong) inplaceLongOr(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteXor(target, value)
else if(target.dt.isWord) inplaceWordXor(target, value)
else if(target.dt.isLong) inplaceLongXor(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
@@ -450,6 +462,42 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
TODO("array ptr assign long var ${target.position}")
}
internal fun assignLongVar(pointer: PtPointerDeref, varName: String) {
val (ptrVar, offset) = deref(pointer)
asmgen.out("""
ldy #$offset
lda $varName
sta ($ptrVar),y
iny
lda $varName+1
sta ($ptrVar),y
iny
lda $varName+2
sta ($ptrVar),y
iny
lda $varName+3
sta ($ptrVar),y""")
}
internal fun assignLong(pointer: PtPointerDeref, long: Int) {
val (ptrVar, offset) = deref(pointer)
val hex = long.toLongHex()
asmgen.out("""
ldy #$offset
lda #$${hex.substring(6,8)}
sta ($ptrVar),y
iny
lda #$${hex.substring(4, 6)}
sta ($ptrVar),y
iny
lda #$${hex.substring(2, 4)}
sta ($ptrVar),y
iny
lda #$${hex.take(2)}
sta ($ptrVar),y""")
}
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
// the only case we support here is: a.b.c[i] . value
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero), and finally the datatype of the field
@@ -467,7 +515,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
if(left.variable!=null) {
asmgen.assignExpressionToRegister(left.variable!!, RegisterOrPair.AY)
} 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")
}
@@ -943,6 +991,146 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long add with ${value.kind} ${target.position}")
}
}
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long sub with ${value.kind} ${target.position}")
}
}
private fun inplaceWordAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
@@ -1024,9 +1212,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
ldy $zpPtrVar+1
jsr floats.MOVMF""")
}
SourceStorageKind.VARIABLE -> TODO("variable + * float")
SourceStorageKind.EXPRESSION -> TODO("expression + * float")
SourceStorageKind.REGISTER -> TODO("register + * float")
SourceStorageKind.VARIABLE -> TODO("variable + * float ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("expression + * float ${target.position}")
SourceStorageKind.REGISTER -> TODO("register + * float ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1075,7 +1263,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
sbc P8ZP_SCRATCH_W1+1
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register - word")
SourceStorageKind.REGISTER -> TODO("register - word ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1127,7 +1315,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.assignRegister(register, multiplyArg)
multiply()
}
SourceStorageKind.EXPRESSION -> TODO("ptr * expr (word)")
SourceStorageKind.EXPRESSION -> TODO("ptr * expr (word) ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1165,14 +1353,14 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
SourceStorageKind.VARIABLE -> {
require(value.datatype.isWord)
val varname = value.asmVarname
TODO("inplace variable word divide")
TODO("inplace variable word divide ${target.position}")
}
SourceStorageKind.REGISTER -> {
val register = value.register!!
require(register.isWord())
TODO("inplace register word divide")
TODO("inplace register word divide ${target.position}")
}
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word)")
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word) ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1194,9 +1382,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
ldy $zpPtrVar+1
jsr floats.MOVMF""")
}
SourceStorageKind.VARIABLE -> TODO("variable - / float")
SourceStorageKind.EXPRESSION -> TODO("expression - / float")
SourceStorageKind.REGISTER -> TODO("register - / float")
SourceStorageKind.VARIABLE -> TODO("variable - / float ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("expression - / float ${target.position}")
SourceStorageKind.REGISTER -> TODO("register - / float ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1376,7 +1564,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
eor ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register ^ byte")
SourceStorageKind.REGISTER -> TODO("register ^ byte ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1420,7 +1608,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
eor ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register ^ word")
SourceStorageKind.REGISTER -> TODO("register ^ word ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1463,7 +1651,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register | byte")
SourceStorageKind.REGISTER -> TODO("register | byte ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1507,7 +1695,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
ora ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register | word")
SourceStorageKind.REGISTER -> TODO("register | word ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1550,7 +1738,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & byte")
SourceStorageKind.REGISTER -> TODO("register & byte ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
@@ -1594,12 +1782,225 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
and ($zpPtrVar),y
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("register & word")
SourceStorageKind.REGISTER -> TODO("register & word ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
private fun inplaceLongAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long &= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long |= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongXor(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long ^= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
internal fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
@@ -1669,6 +2070,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
lda $regname+1
pha""")
}
in combinedLongRegisters -> {
TODO("save on stack long register pair - do we really want to do this?")
}
else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false)
}
}
@@ -1701,6 +2105,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
pla
sta $regname""")
}
in combinedLongRegisters -> {
TODO("restore from stack long register - do we really want to do this?")
}
else -> asmgen.restoreRegisterStack(regs.asCpuRegister(), false)
}
}
@@ -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")
@@ -467,7 +470,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>()
@@ -702,9 +709,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 +724,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 +976,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 +1319,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 +1377,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 +1432,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)
@@ -686,7 +686,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 +707,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 +727,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 +755,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 +784,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 +811,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 +819,7 @@ 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}")
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
}
}
BaseDataType.FLOAT -> {
@@ -831,7 +846,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 +1184,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 +1762,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
@@ -415,13 +415,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
}
@@ -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
}
+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
+9
View File
@@ -1302,6 +1302,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 +1328,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
+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)
}
+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
+41 -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
}}
}
@@ -865,4 +859,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)
}
+9 -4
View File
@@ -566,7 +566,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 +624,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
@@ -818,8 +818,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 {
+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,
+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()
}
+11
View File
@@ -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
+19
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
+1 -1
View File
@@ -550,7 +550,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
+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
}
+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.
+2 -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
}}
}
+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
}}
+75 -9
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")
@@ -62,7 +62,6 @@ private fun compileMain(args: Array<String>): Boolean {
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 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)")
@@ -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")
@@ -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)
}
@@ -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
}
@@ -2088,6 +2106,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))
@@ -2347,7 +2371,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 < -2147483647.0 || number > 2147483647.0)
return err("value '$number' out of range for long")
}
targetDt.isArray -> {
@@ -2459,7 +2483,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
@@ -113,8 +113,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 +196,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 +226,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 +251,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))
}
}
}
}
}
@@ -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
@@ -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 }
+19 -1
View File
@@ -58,7 +58,7 @@ class TestBuiltinFunctions: FunSpec({
func.parameters[0].name shouldBe "address"
func.parameters[0].possibleDatatypes shouldBe arrayOf(BaseDataType.UWORD)
func.parameters[1].name shouldBe "value"
func.parameters[1].possibleDatatypes shouldBe arrayOf(BaseDataType.UBYTE)
func.parameters[1].possibleDatatypes shouldBe arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
func.pure shouldBe false
func.returnType shouldBe null
}
@@ -184,5 +184,23 @@ main {
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("sgn") {
val src="""
main {
sub start() {
byte @shared b1 = -100
word @shared w1 = -1000
long @shared l1 = -1000000
cx16.r0sL = sgn(b1)
cx16.r1sL = sgn(w1)
cx16.r2sL = sgn(l1)
}
}"""
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})
+1 -1
View File
@@ -47,7 +47,6 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
slabsHighBank = null,
slabsGolden = false,
compilationTarget = target.name,
dontSplitWordArrays = false,
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,
@@ -178,6 +177,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
"test_gfx_lores",
"test_gfx_hires",
"testmonogfx",
"textspotlight",
),
listOf(false, true)
)
@@ -39,7 +39,6 @@ class TestCompilerOptionSourcedirs: FunSpec({
slabsHighBank = null,
slabsGolden = false,
compilationTarget = Cx16Target.NAME,
dontSplitWordArrays = false,
breakpointCpuInstruction = null,
printAst1 = false,
printAst2 = false,
+84 -86
View File
@@ -23,10 +23,8 @@ class TestNumericLiteral: FunSpec({
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)
val v = NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
@@ -34,124 +32,124 @@ class TestNumericLiteral: FunSpec({
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
}
test("test truncating avoidance") {
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.BYTE, -2.345, dummyPos)
NumericLiteral(BaseDataType.BYTE, -2.345, Position.DUMMY)
}.message shouldContain "float value given for integer"
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.BYTE, -2.6, dummyPos)
NumericLiteral(BaseDataType.BYTE, -2.6, Position.DUMMY)
}.message shouldContain "float value given for integer"
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.UWORD, 2222.345, dummyPos)
NumericLiteral(BaseDataType.UWORD, 2222.345, Position.DUMMY)
}.message shouldContain "float value given for integer"
NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
NumericLiteral(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
NumericLiteral(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
NumericLiteral(BaseDataType.FLOAT, 123.456, dummyPos)
NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY).number shouldBe 2.0
NumericLiteral(BaseDataType.BYTE, -2.0, Position.DUMMY).number shouldBe -2.0
NumericLiteral(BaseDataType.UWORD, 2222.0, Position.DUMMY).number shouldBe 2222.0
NumericLiteral(BaseDataType.FLOAT, 123.456, Position.DUMMY)
}
test("testEqualsAndNotEquals") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 22239.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 22239.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 22239.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 22239.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 245.0, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) != NumericLiteral(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 245.0, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 245.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 245.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe false
}
test("testEqualsRef") {
(StringLiteral.create("hello", Encoding.PETSCII, dummyPos) == StringLiteral.create("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, dummyPos) != StringLiteral.create("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) == StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral.create("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral.create("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY) == StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY) != StringLiteral.create("bye", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) == StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) != StringLiteral.create("bye", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) != StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
val lvOne = NumericLiteral(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwo = NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThree = NumericLiteral(BaseDataType.UBYTE, 3.0, dummyPos)
val lvOneR = NumericLiteral(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwoR = NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThreeR = NumericLiteral(BaseDataType.UBYTE, 3.0, dummyPos)
val lvFour= NumericLiteral(BaseDataType.UBYTE, 4.0, dummyPos)
val lv1 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOne, lvTwo, lvThree), dummyPos)
val lv2 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
val lv3 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
val lvOne = NumericLiteral(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwo = NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThree = NumericLiteral(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvOneR = NumericLiteral(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwoR = NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThreeR = NumericLiteral(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvFour= NumericLiteral(BaseDataType.UBYTE, 4.0, Position.DUMMY)
val lv1 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOne, lvTwo, lvThree), Position.DUMMY)
val lv2 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvThreeR), Position.DUMMY)
val lv3 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvFour), Position.DUMMY)
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) > NumericLiteral(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) > NumericLiteral(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) > NumericLiteral(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) > NumericLiteral(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) >= NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) > NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) > NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) > NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) > NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) >= NumericLiteral(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe false
}
test("testLessThan") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) < NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) < NumericLiteral(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) < NumericLiteral(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) < NumericLiteral(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) <= NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) < NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) < NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) < NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) < NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) <= NumericLiteral(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe false
}
test("optimalInteger") {
+17 -10
View File
@@ -1001,7 +1001,6 @@ main {
test("no operand swap on logical expressions with shortcircuit evaluation") {
val src="""
%import diskio
%zeropage basicsafe
%option no_sysinit
@@ -1009,15 +1008,23 @@ main {
str scanline_buf = "?"* 20
sub start() {
if diskio.f_open("test.prg") and diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") and f_read(scanline_buf, 2)==2
cx16.r0++
if diskio.f_open("test.prg") or diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") or f_read(scanline_buf, 2)==2
cx16.r0++
if diskio.f_open("test.prg") xor diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") xor f_read(scanline_buf, 2)==2
cx16.r0++
}
sub f_open(str name) -> bool {
return cx16.r0==99
}
sub f_read(str buf, uword lengthy) -> uword {
return cx16.r0
}
}"""
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
@@ -1025,15 +1032,15 @@ main {
val ifCond1 = (st[0] as IfElse).condition as BinaryExpression
val ifCond2 = (st[1] as IfElse).condition as BinaryExpression
val ifCond3 = (st[2] as IfElse).condition as BinaryExpression
(ifCond1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
(ifCond2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
(ifCond3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
val right1 = ifCond1.right as BinaryExpression
val right2 = ifCond2.right as BinaryExpression
val right3 = ifCond3.right as BinaryExpression
(right1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
(right2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
(right3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
}
test("eliminate same target register assignments") {
+143 -18
View File
@@ -662,6 +662,58 @@ main {
err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer")
}
xtest("pointer uword assignments with cast") {
val src="""
main {
struct Node1 {
ubyte type
word ww
}
struct Node2 {
ubyte type
^^ubyte text
}
sub start() {
^^Node1 @shared next
^^Node2 @shared this
^^ubyte ubptr
ubptr = next as ^^ubyte
ubptr = 12345
ubptr = cx16.r0
ubptr = next as uword ; TODO fix type error; the cast should succeed
this.text = next as ^^ubyte
this.text = 12345
this.text = cx16.r0
this.text = next as uword ; TODO fix type error; the cast should succeed
}
}"""
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
compileText(C64Target(), false, src, outputDir) shouldNotBe null
}
test("const pointer gives proper error") {
val src="""
main {
sub start() {
^^uword @shared ptr = 7000
const ^^uword cptr = 8000
;ptr^^ = 12345
;cptr^^ = 12345
cx16.r0 = cptr
cx16.r1 = ptr
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = true) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain("const can only be used on numbers and booleans")
}
test("unknown field") {
val src="""
main {
@@ -1258,7 +1310,7 @@ main {
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
}
xtest("assigning struct instances") {
test("assigning struct instances") {
val src="""
main {
sub start() {
@@ -1273,16 +1325,26 @@ main {
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above)
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above)
;;; lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 99
st.size shouldBe 11
val memcopy1 = st[5] as FunctionCallStatement
val memcopy2 = st[6] as FunctionCallStatement
val memcopy3 = st[7] as FunctionCallStatement
val memcopy4 = st[8] as FunctionCallStatement
val memcopy5 = st[9] as FunctionCallStatement
memcopy1.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy2.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy3.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy4.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy5.target.nameInSource shouldBe listOf("sys", "memcopy")
}
test("a.b.c[i]^^.value as expression where pointer is struct") {
@@ -1564,33 +1626,34 @@ main {
test("hoist variable decl and initializer correctly in case of pointer type variable as well") {
val src="""
%import textio
main {
sub start() {
txt.print("one\n")
stuff()
if true {
txt.print("two\n")
stuff()
^^bool @shared successor = find_successor() ; testcase here
}
txt.print("four\n")
stuff()
sub find_successor() -> uword {
txt.print("three\n")
stuff()
cx16.r0++
return 0
}
}
sub stuff() {
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 6
st[0] shouldBe instanceOf<VarDecl>()
(st[1] as FunctionCallStatement).target.nameInSource shouldBe listOf("txt", "print")
(st[1] as FunctionCallStatement).target.nameInSource shouldBe listOf("stuff")
st[2] shouldBe instanceOf<IfElse>()
(st[3] as FunctionCallStatement).target.nameInSource shouldBe listOf("txt", "print")
(st[3] as FunctionCallStatement).target.nameInSource shouldBe listOf("stuff")
st[4] shouldBe instanceOf<Return>()
st[5] shouldBe instanceOf<Subroutine>()
}
@@ -1950,7 +2013,7 @@ main {
val src="""
main {
sub start() {
uword[10] @split @shared splitarray
uword[10] @shared splitarray
uword[10] @nosplit @shared nosplitarray
^^uword ptr1 = &&splitarray
@@ -1991,9 +2054,9 @@ main {
errors.errors.size shouldBe 3
errors.warnings.size shouldBe 0
errors.infos.size shouldBe 0
errors.errors[0] shouldContain "pointer arrays can only be @split"
errors.errors[1] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
errors.errors[2] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
errors.errors[0] shouldContain "pointer arrays can only be split"
errors.errors[1] shouldContain "was: ^^ubyte (because arg is a split word array) expected: ^^main.Node"
errors.errors[2] shouldContain "was: ^^ubyte (because arg is a split word array) expected: ^^main.Node"
}
test("passing split array of structpointers to a subroutine in various forms should be param type ptr to ubyte (the lsb part of the split array)") {
@@ -2004,7 +2067,7 @@ main {
}
sub start() {
^^Node[10] @split nodearray
^^Node[10] nodearray
func(&&nodearray)
func(&nodearray)
func(nodearray)
@@ -2437,6 +2500,48 @@ structdefs {
a6t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
}
test("pointer dereference aliasing") {
val src="""
main {
sub start() {
player.test()
}
}
cx16 {
%option merge
&^^word pword4 = &cx16.r4
}
player {
alias sxPtr = cx16.pword4
&^^word zxPtr = &cx16.r6
sub test() {
sxPtr^^ = -99 ; aliased assignment
if (zxPtr^^ - sxPtr^^) in -13 to 13 { ; aliased expression
cx16.r0++
}
}
}"""
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.allBlocks.single{it.name == "player"}.statements
st.size shouldBe 2
val test = (st[1] as Subroutine).statements
test.size shouldBe 3
val assignTgt = (test[0] as Assignment).target
val targetDeref = assignTgt.pointerDereference!!
targetDeref.chain shouldBe listOf("cx16", "pword4")
val ifCond = (test[1] as IfElse).condition as ContainmentCheck
val cond = ifCond.element as BinaryExpression
cond.left shouldBe instanceOf<PtrDereference>()
cond.right shouldBe instanceOf<PtrDereference>()
(cond.right as PtrDereference).chain shouldBe listOf("cx16", "pword4")
}
test("assigning field with same name should not confuse compiler") {
val src="""
main {
@@ -2505,4 +2610,24 @@ main {
be6.right shouldBe instanceOf<IdentifierReference>()
(a7.value as? ArrayIndexedExpression)?.indexer?.constIndex() shouldBe 0
}
test("long pointer assignments") {
val src="""
main {
sub start() {
long lvar = 999999
^^long lptr = 5000
pokel(5000, 11223344)
lptr^^ = 0
lptr^^ = lvar
lptr^^ = 82348234+lvar
lvar = lptr^^+1111111
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})
+84 -86
View File
@@ -20,10 +20,8 @@ class TestPtNumber: FunSpec({
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)
val v = PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
@@ -31,127 +29,127 @@ class TestPtNumber: FunSpec({
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
}
test("test truncating") {
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.BYTE, -2.345, dummyPos)
PtNumber(BaseDataType.BYTE, -2.345, Position.DUMMY)
}.message shouldContain "refused truncating"
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.BYTE, -2.6, dummyPos)
PtNumber(BaseDataType.BYTE, -2.6, Position.DUMMY)
}.message shouldContain "refused truncating"
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.UWORD, 2222.345, dummyPos)
PtNumber(BaseDataType.UWORD, 2222.345, Position.DUMMY)
}.message shouldContain "refused truncating"
PtNumber(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
PtNumber(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
PtNumber(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
PtNumber(BaseDataType.FLOAT, 123.456, dummyPos)
PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY).number shouldBe 2.0
PtNumber(BaseDataType.BYTE, -2.0, Position.DUMMY).number shouldBe -2.0
PtNumber(BaseDataType.UWORD, 2222.0, Position.DUMMY).number shouldBe 2222.0
PtNumber(BaseDataType.FLOAT, 123.456, Position.DUMMY)
}
test("testEqualsAndNotEquals") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) == PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) == PtNumber(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 22239.0, dummyPos) == PtNumber(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) == PtNumber(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 22239.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 254.0, dummyPos), PtNumber(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 22239.0, dummyPos), PtNumber(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 22239.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 245.0, dummyPos) != PtNumber(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) != PtNumber(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) != PtNumber(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 245.0, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 245.0, dummyPos), PtNumber(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 245.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe false
}
test("testEqualsRef") {
(PtString("hello", Encoding.PETSCII, dummyPos) == PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.PETSCII, dummyPos) != PtString("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) == PtString("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.PETSCII, Position.DUMMY) == PtString("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.PETSCII, Position.DUMMY) != PtString("bye", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) == PtString("hello", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) != PtString("bye", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) != PtString("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
val lvOne = PtNumber(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwo = PtNumber(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThree = PtNumber(BaseDataType.UBYTE, 3.0, dummyPos)
val lvOneR = PtNumber(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwoR = PtNumber(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThreeR = PtNumber(BaseDataType.UBYTE, 3.0, dummyPos)
val lvFour= PtNumber(BaseDataType.UBYTE, 4.0, dummyPos)
val lv1 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lvOne = PtNumber(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwo = PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThree = PtNumber(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvOneR = PtNumber(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwoR = PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThreeR = PtNumber(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvFour= PtNumber(BaseDataType.UBYTE, 4.0, Position.DUMMY)
val lv1 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOne, lvTwo, lvThree).forEach { lv1.add(it) }
val lv2 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lv2 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOneR, lvTwoR, lvThreeR).forEach { lv2.add(it) }
val lv3 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lv3 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOneR, lvTwoR, lvFour).forEach { lv3.add(it) }
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) > PtNumber(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) > PtNumber(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) > PtNumber(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) > PtNumber(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) > PtNumber(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) > PtNumber(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) >= PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) >= PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) >= PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) >= PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) > PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) > PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) > PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) > PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) > PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) > PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) >= PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) >= PtNumber(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) >= PtNumber(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) >= PtNumber(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe false
}
test("testLessThan") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) < PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) < PtNumber(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) < PtNumber(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) < PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) < PtNumber(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) < PtNumber(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) <= PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) <= PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) <= PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) <= PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) < PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) < PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) < PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) < PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) < PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) < PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) <= PtNumber(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) <= PtNumber(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) <= PtNumber(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) <= PtNumber(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe false
}
})
+2 -2
View File
@@ -208,8 +208,8 @@ private fun makeSt(): SymbolTable {
val sub12 = StNode("sub2", StNodeType.SUBROUTINE, astSub2)
block1.add(sub11)
block1.add(sub12)
block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2))
block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v2))
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
+25 -5
View File
@@ -193,17 +193,20 @@ main {
test("word to byte casts") {
val text = """
%import textio
main {
sub func(ubyte arg) -> word {
return arg-99
}
sub start() {
txt.print_ub(func(0) as ubyte)
txt.print_uw(func(0) as ubyte)
txt.print_w(func(0) as ubyte)
funcub(func(0) as ubyte)
funcuw(func(0) as ubyte)
funcw(func(0) as ubyte)
}
sub funcub(ubyte arg) { }
sub funcuw(uword arg) { }
sub funcw(word arg) { }
}"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = false)!!
val stmts = result.compilerAst.entrypoint.statements
@@ -595,7 +598,7 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain ":9:"
errors.errors[0] shouldContain "no cast"
errors.errors[0] shouldContain "no implicit cast"
errors.errors[1] shouldContain ":10:"
errors.errors[1] shouldContain "out of range"
errors.errors[2] shouldContain ":10:"
@@ -977,4 +980,21 @@ main {
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("negative numbers okay for pokes") {
val src = """
main {
sub start() {
@(4000) = 100
@(4000) = -100
poke(4000, 100)
poke(4000, -100)
pokew(4000, 9999)
pokew(4000, -9999)
pokel(4000, 999999)
pokel(4000, -999999)
}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
})
+47 -10
View File
@@ -63,17 +63,19 @@ class TestAstChecks: FunSpec({
test("can't do str or array expression without using address-of") {
val text = """
%import textio
main {
sub start() {
ubyte[] array = [1,2,3,4]
str s1 = "test"
ubyte ff = 1
txt.print(s1+ff)
txt.print(array+ff)
txt.print_uwhex(s1+ff, true)
txt.print_uwhex(array+ff, true)
print(s1+ff)
print(array+ff)
print_uwhex(s1+ff, true)
print_uwhex(array+ff, true)
}
sub print(str s) { cx16.r0++ }
sub print_uwhex(uword w, bool prefix) { cx16.r0++ }
}
"""
val errors = ErrorReporterForTests()
@@ -83,21 +85,23 @@ class TestAstChecks: FunSpec({
test("str or array expression with address-of") {
val text = """
%import textio
main {
sub start() {
ubyte[] array = [1,2,3,4]
str s1 = "test"
bool bb1, bb2
ubyte ff = 1
txt.print(&s1+ff)
txt.print(&array+ff)
txt.print_uwhex(&s1+ff, true)
txt.print_uwhex(&array+ff, true)
print(&s1+ff)
print(&array+ff)
print_uwhex(&s1+ff, true)
print_uwhex(&array+ff, true)
; also good:
bb1 = (s1 == "derp")
bb2 = (s1 != "derp")
}
sub print(str s) { cx16.r0++ }
sub print_uwhex(uword w, bool prefix) { cx16.r0++ }
}
"""
compileText(C64Target(), false, text, outputDir, writeAssembly = false) shouldNotBe null
@@ -485,4 +489,37 @@ main {
errors.errors[1] shouldContain "string var must be initialized with a string literal"
errors.errors[2] shouldContain "string var must be initialized with a string literal"
}
test("cannot assign strings by-value") {
val src="""
main {
sub start() {
str n1 = "irmen"
str n2 = "de jong 12345"
n1 = n2
n1 = "zsdfzsdf"
n1 = 0
n2 = 9999
void foo(n1)
}
sub foo(str arg) -> str {
arg = "bar"
arg = 0
arg = 9999
return arg
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":8:9: cannot assign to string"
errors.errors[1] shouldContain ":9:9: cannot assign to string"
errors.errors[2] shouldContain ":10:9: cannot assign to string"
errors.errors[3] shouldContain ":11:9: cannot assign to string"
}
})
+81 -63
View File
@@ -17,13 +17,14 @@ import prog8.code.core.BaseDataType
import prog8.code.core.Position
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestConst: FunSpec({
val outputDir = tempdir().toPath()
test("const folding multiple scenarios +/-") {
val source = """
main {
@@ -224,7 +225,7 @@ class TestConst: FunSpec({
}
test("const pointer variable indexing works") {
val src="""
val src = """
main {
sub start() {
const uword pointer=$1000
@@ -233,11 +234,11 @@ main {
}
}
"""
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
compileText(C64Target(), optimize = false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("advanced const folding of known library functions") {
val src="""
val src = """
%import floats
%import math
%import strings
@@ -260,7 +261,7 @@ main {
}
test("const address-of memory mapped arrays") {
val src= """
val src = """
main {
sub start() {
&uword[30] @nosplit wb = $2000
@@ -277,10 +278,10 @@ main {
st.size shouldBe 7
((st[0] as VarDecl).value as NumericLiteral).number shouldBe 0x2000
((st[1] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
((st[3] as Assignment).value as NumericLiteral).number shouldBe 0x9e00
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
}
test("address of a memory mapped variable") {
@@ -296,7 +297,7 @@ main {
&uword[20] @shared @nosplit wa = HIGH_MEMORY_START
}
}"""
val result = compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true)!!
val result = compileText(Cx16Target(), optimize = false, src, outputDir, writeAssembly = true)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 7
val arrayDeclV = (st[2] as VarDecl).value
@@ -306,7 +307,7 @@ main {
}
test("address of a const uword pointer array expression") {
val src= """
val src = """
main {
sub start() {
const uword buffer = 2000
@@ -328,7 +329,7 @@ main {
}
test("out of range const byte and word give correct error") {
var src="""
var src = """
main {
sub start() {
const byte MIN_BYTE = -129
@@ -339,7 +340,7 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "out of range"
@@ -348,7 +349,7 @@ main {
}
test("out of range var byte and word give correct error") {
var src="""
var src = """
main {
sub start() {
byte @shared v_MIN_BYTE = -129
@@ -359,7 +360,7 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 8
errors.errors[0] shouldContain "out of range"
errors.errors[2] shouldContain "out of range"
@@ -368,7 +369,7 @@ main {
}
test("out of range const byte and word no errors with explicit cast if possible") {
var src="""
var src = """
main {
sub start() {
const byte MIN_BYTE = -129 as byte ; still error
@@ -379,16 +380,16 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain(":4:31: const declaration needs a compile-time constant")
errors.errors[1] shouldContain(":4:32: no cast available")
errors.errors[2] shouldContain(":5:31: const declaration needs a compile-time constant")
errors.errors[3] shouldContain(":5:32: no cast available")
errors.errors[0] shouldContain (":4:31: const declaration needs a compile-time constant")
errors.errors[1] shouldContain (":4:32: no cast available")
errors.errors[2] shouldContain (":5:31: const declaration needs a compile-time constant")
errors.errors[3] shouldContain (":5:32: no cast available")
}
test("out of range var byte and word no errors with explicit cast if possible") {
var src="""
var src = """
main {
sub start() {
byte @shared v_min_byte2 = -129 as byte ; still error
@@ -399,15 +400,14 @@ main {
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors[0] shouldContain(":4:37: no cast available")
errors.errors[1] shouldContain(":5:37: no cast available")
errors.errors[0] shouldContain (":4:37: no cast available")
errors.errors[1] shouldContain (":5:37: no cast available")
}
test("const evaluation of signed bitwise operations") {
val src="""
%import textio
val src = """
main {
sub start() {
byte @shared a = -1
@@ -417,26 +417,16 @@ main {
const byte cb = -15
const ubyte cub = 2
txt.print_ub( a & b )
txt.spc()
txt.print_ub( a | b )
txt.spc()
txt.print_ub( a ^ b )
txt.spc()
txt.print_b( a << ub )
txt.spc()
txt.print_b( a >> ub )
txt.nl()
txt.print_ub( ca & cb )
txt.spc()
txt.print_ub( ca | cb )
txt.spc()
txt.print_ub( ca ^ cb )
txt.spc()
txt.print_b( ca << cub )
txt.spc()
txt.print_b( ca >> cub )
txt.nl()
print_ub( a & b )
print_ub( a | b )
print_ub( a ^ b )
print_b( a << ub )
print_b( a >> ub )
print_ub( ca & cb )
print_ub( ca | cb )
print_ub( ca ^ cb )
print_b( ca << cub )
print_b( ca >> cub )
word @shared aw = -1
word @shared bw = -15
@@ -445,30 +435,28 @@ main {
const word cbw = -15
const uword cuw = 2
txt.print_uw( aw & bw )
txt.spc()
txt.print_uw( aw | bw )
txt.spc()
txt.print_uw( aw ^ bw )
txt.nl()
txt.print_uw( caw & cbw )
txt.spc()
txt.print_uw( caw | cbw )
txt.spc()
txt.print_uw( caw ^ cbw )
txt.nl()
txt.print_w( cbw << cuw )
txt.spc()
txt.print_w( cbw >> cuw )
txt.nl()
print_uw( aw & bw )
print_uw( aw | bw )
print_uw( aw ^ bw )
print_uw( caw & cbw )
print_uw( caw | cbw )
print_uw( caw ^ cbw )
print_w( cbw << cuw )
print_w( cbw >> cuw )
}
sub print_ub(ubyte v) {}
sub print_b(byte v) {}
sub print_uw(uword v) {}
sub print_w(word v) {}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("const long with small values") {
val src="""
val src = """
main {
sub start() {
const long notkaputt = 42
@@ -477,4 +465,34 @@ main {
}"""
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("const long with large unsigned long values should be converted to signed longs") {
val src = $$"""
main {
sub start() {
long @shared l1 = $e1fa84c6
long @shared l2 = -1
long @shared l3 = $ffffffff
long @shared l4 = $7fffffff
l1 ^= -1
l2 ^= $ffffffff
l3 ^= $7fffffff
}
}"""
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 12
val a = st.filterIsInstance<Assignment>()
(a[0].value as NumericLiteral).number shouldBe -503675706.0
(a[1].value as NumericLiteral).number shouldBe -1.0
(a[2].value as NumericLiteral).number shouldBe -1.0
(a[3].value as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
((a[4].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
((a[5].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
((a[6].value as BinaryExpression).right as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
}
})
+5 -10
View File
@@ -271,14 +271,14 @@ class TestProg8Parser: FunSpec( {
val srcText = "bad * { }\n"
val e = shouldThrow<ParseError> { parseModule(SourceCode.Text(srcText)) }
assertPosition(e.position, Regex("^string:[0-9a-f\\-]+$"), 1, 4, 4)
assertPosition(e.position, Regex("^string:[0-9a-f\\-]+$"), 1, 5, 5)
}
test("in ParseError from bad file source code") {
val path = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
val e = shouldThrow<ParseError> { parseModule(ImportFileSystem.getFile(path)) }
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 4)
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 5)
}
test("of Module parsed from a string") {
@@ -327,7 +327,7 @@ class TestProg8Parser: FunSpec( {
val mpf = module.position.file
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
assertPositionOf(targetDirective, mpf, 1, 1, 8)
assertPositionOf(targetDirective, mpf, 1, 1, 9)
val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 2, 1, 4)
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
@@ -970,9 +970,6 @@ class TestProg8Parser: FunSpec( {
test("line comment in array literal is ok") {
val src="""
%import textio
%zeropage basicsafe
main {
sub start() {
byte[] foo = [ 1, 2, ; this comment is ok
@@ -991,13 +988,11 @@ main {
test("various alternative curly brace styles are ok") {
val src="""
%zeropage dontuse
main {
; curly braces without newline
sub start () { foo() derp() other() }
sub foo() { cx16.r0++ }
asmsub derp() { %asm {{ nop rts }} %ir {{ load.b r0,1 return }} }
asmsub derp() { %asm {{ nop rts }} %ir {{ load.b r0,#1 return }} }
; curly braces on next line
sub other()
@@ -1021,7 +1016,7 @@ main {
{
%ir
{{
load.b r0,1
load.b r0,#1
return
}}
}
+4 -9
View File
@@ -118,10 +118,10 @@ class TestProgram: FunSpec({
}
test("block sort order") {
val src="""
val src= """
%import textio
main ${'$'}0a00 {
main $0a00 {
sub start() {
otherblock1.foo()
otherblock2.foo()
@@ -132,16 +132,12 @@ otherblock1 {
sub foo() {
txt.print("main.start: ")
txt.print_uwhex(&main.start, true)
txt.nl()
txt.print("datablock1 array: ")
txt.print_uwhex(&datablock1.array1, true)
txt.nl()
txt.print("datablock2 array: ")
txt.print_uwhex(&datablock2.array2, true)
txt.nl()
txt.print("otherblock1.foo: ")
txt.print_uwhex(&foo, true)
txt.nl()
}
}
@@ -149,15 +145,14 @@ otherblock2 {
sub foo() {
txt.print("otherblock2.foo: ")
txt.print_uwhex(&otherblock2.foo, true)
txt.nl()
}
}
datablock1 ${'$'}9000 {
datablock1 $9000 {
ubyte[5] @shared array1 = [1,2,3,4,5]
}
datablock2 ${'$'}8000 {
datablock2 $8000 {
ubyte[5] @shared array2 = [1,2,3,4,5]
}
"""
+122 -18
View File
@@ -126,6 +126,24 @@ txt {
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("infinite alias loop detected") {
val src="""
main {
sub start() {
alias vv = vv
alias xx = xx.yy
alias zz = mm
alias mm = zz
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "references itself"
errors.errors[1] shouldContain "undefined symbol: xx.yy"
errors.errors[2] shouldContain "references itself"
}
test("wrong alias gives correct error") {
val src="""
main {
@@ -207,20 +225,21 @@ main {
sub start() {
str @shared name = "part1" + "part2"
str @shared rept = "rep"*4
const ubyte times = 3
name = "xx1" + "xx2"
rept = "xyz" * (times+1)
test("xx1" + "xx2")
test("xyz" * 4)
}
sub test(str message) {
}
}"""
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 6
stmts.size shouldBe 5
val name1 = stmts[0] as VarDecl
val rept1 = stmts[1] as VarDecl
(name1.value as StringLiteral).value shouldBe "part1part2"
(rept1.value as StringLiteral).value shouldBe "reprepreprep"
val name2strcopy = stmts[3] as IFunctionCall
val rept2strcopy = stmts[4] as IFunctionCall
val name2strcopy = stmts[2] as IFunctionCall
val rept2strcopy = stmts[3] as IFunctionCall
val name2 = (name2strcopy.args.first() as AddressOf).identifier!!
val rept2 = (rept2strcopy.args.first() as AddressOf).identifier!!
(name2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xx1xx2"
@@ -241,6 +260,48 @@ main {
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.single() shouldContain "cannot use byte value"
}
test("string indexing bounds checks") {
val src="""
main {
sub start() {
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
name[5] = '!' ; don't do this in real code...
name[5] = 0
name[6] = 99 ; out of bounds
name[-1] = 99 ; ok
name[-5] = 99 ; ok
cx16.r0L = name[5]
cx16.r1L = name[6] ; out of bounds
cx16.r1L = name[-1] ; ok
cx16.r1L = name[-5] ; ok
array[5] = '!'
array[5] = 0
array[6] = 99 ; out of bounds
array[-1] = 99 ; ok
array[-5] = 99 ; ok
array[-6] = 99 ; ok
cx16.r0L = array[5]
cx16.r1L = array[6] ; out of bounds
cx16.r1L = array[-1] ; ok
cx16.r1L = array[-5] ; ok
cx16.r1L = array[-6] ; ok
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":9:13: index out of bounds"
errors.errors[1] shouldContain ":14:24: index out of bounds"
errors.errors[2] shouldContain ":20:14: index out of bounds"
errors.errors[3] shouldContain ":26:25: index out of bounds"
}
}
context("return value") {
@@ -1091,7 +1152,7 @@ main {
compileText(C128Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("on..goto") {
test("on..goto and on..call") {
val src="""
main {
sub start() {
@@ -1108,6 +1169,23 @@ main {
}
on cx16.r13L+1 goto (thing.func1, thing.func2, thing.func3)
test2()
}
sub test2() {
ubyte @shared thing
on thing call (lblA, lblB, lblC)
on thing goto (lblA, lblB, lblC)
lblA:
cx16.r0++
goto lblDone
lblB:
cx16.r1++
goto lblDone
lblC:
cx16.r2++
lblDone:
}
}
@@ -1129,30 +1207,26 @@ thing {
}
}
test("pointer arrays are always split") {
test("pointer arrays are split by default") {
val src="""
%option enable_floats
main {
sub start() {
^^bool[10] @split @shared barray
^^word[10] @split @shared warray
^^float[10] @split @shared farray
^^bool[10] @shared barrayns
^^word[10] @shared warrayns
^^float[10] @shared farrayns
^^bool[10] @shared barray
^^word[10] @shared warray
^^float[10] @shared farray
}
}"""
val result = compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 7
st.size shouldBe 4
val decls = st.filterIsInstance<VarDecl>()
decls.size shouldBe 6
decls.size shouldBe 3
decls.all { it.datatype.sub!=null } shouldBe true
decls.all { it.datatype.isPointerArray } shouldBe true
decls.all { it.datatype.isPointerArray } shouldBe true
decls.all { it.datatype.isSplitWordArray } shouldBe true
}
test("on..call in nested scope compiles correctly with temp variable introduced") {
@@ -1196,5 +1270,35 @@ main {
errors.errors.size shouldBe 3
errors.errors[1] shouldContain "12:10: undefined symbol: breakpoint"
}
test("struct names cannot be keywords") {
val src="""
main {
sub start() {
struct on {
bool flag
}
struct step {
bool flag
}
struct inline {
bool flag
}
struct call {
bool flag
}
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), optimize=false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "struct name cannot be a keyword"
errors.errors[1] shouldContain "struct name cannot be a keyword"
errors.errors[2] shouldContain "struct name cannot be a keyword"
errors.errors[3] shouldContain "builtin function cannot be redefined"
}
})
+19 -31
View File
@@ -251,32 +251,26 @@ main {
val src="""
main {
sub start() {
ubyte[10] array
array[-11] = 0
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "out of bounds"
}
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
test("bounds checking on strings invalid cases") {
val src="""
main {
sub start() {
str name = "1234567890"
name[10] = 0
name[-1] = 0
name[-11] = 0
name[-5] = 99 ; ok
name[-6] = 99 ; out of bounds
cx16.r1L = name[-5] ; ok
cx16.r1L = name[-6] ; out of bounds
array[-6] = 99 ; ok
array[-7] = 99 ; out of bounds
cx16.r1L = array[-6] ; ok
cx16.r1L = array[-7] ; out of bounds
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "out of bounds"
errors.errors[1] shouldContain "out of bounds"
errors.errors[2] shouldContain "out of bounds"
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":8:9: index out of bounds"
errors.errors[1] shouldContain ":10:20: index out of bounds"
errors.errors[2] shouldContain ":12:9: index out of bounds"
errors.errors[3] shouldContain ":14:20: index out of bounds"
}
test("array and string initializer with multiplication") {
@@ -413,8 +407,6 @@ main {
test("various array indexed assignment scenarios") {
val src="""
%import floats
%import textio
%zeropage basicsafe
main {
bool[10] barray
@@ -456,14 +448,10 @@ main {
dump()
sub dump() {
txt.print_bool(barray[2])
txt.spc()
txt.print_uw(warrayns[2])
txt.spc()
txt.print_uw(warray[2])
txt.spc()
txt.print_f(farray[2])
txt.nl()
bool @shared bb = barray[2]
uword @shared ww = warrayns[2]
uword @shared ww2 = warray[2]
float @shared ff = farray[2]
}
}
}"""
@@ -197,8 +197,6 @@ main {
"identifiers can have the names of cpu instructions" {
val text="""
%import textio
nop {
sub lda(ubyte sec) -> ubyte {
asl:
@@ -223,13 +221,15 @@ main {
sub start() {
ubyte col = 10
ubyte row = 20
txt.print_ub(nop.lda(42))
txt.nl()
txt.print_uw(nop.lda.asl)
print_ub(nop.lda(42))
print_uw(nop.lda.asl)
void ffalse(99)
void ftrue(99)
}
sub print_ub(ubyte v) {}
sub print_uw(uword v) {}
}
"""
val result = compileText(C64Target(), false, text, outputDir, writeAssembly = true)
+9 -15
View File
@@ -19,14 +19,11 @@ class TestLibrary: FunSpec({
val outputDir = tempdir().toPath()
test("library compilation (x16)") {
val src="""
%address ${'$'}A050
%memtop ${'$'}C000
val src= $$"""
%address $A050
%memtop $C000
%output library
%import textio
main {
; Create a jump table as first thing in the library.
uword[] @shared @nosplit jumptable = [
@@ -105,23 +102,20 @@ library {
}
test("library compilation (c64)") {
val src="""
%address ${'$'}8050
%memtop ${'$'}a000
val src= $$"""
%address $8050
%memtop $a000
%output library
%import textio
main {
; Create a jump table as first thing in the library.
uword[] @shared @nosplit jumptable = [
; NOTE: the compiler has inserted a single JMP instruction at the start of the 'main' block, that jumps to the start() routine.
; This is convenient because the rest of the jump table simply follows it,
; making the first jump neatly be the required initialization routine for the library (initializing variables and BSS region).
; btw, ${'$'}4c = opcode for JMP.
${'$'}4c00, &library.func1,
${'$'}4c00, &library.func2,
; btw, $4c = opcode for JMP.
$4c00, &library.func1,
$4c00, &library.func2,
]
sub start() {
@@ -8,6 +8,7 @@ import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldStartWith
import io.kotest.matchers.types.instanceOf
import prog8.code.StNodeType
import prog8.code.ast.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
@@ -81,6 +82,12 @@ main {
val assign = start.children[2] as PtAssignment
assign.target.identifier!!.name shouldBe "cx16.r0"
assign.value shouldBe instanceOf<PtBinaryExpression>()
val st = result.codegenSymboltable!!
st.flat.size shouldBeGreaterThan 100
st.flat["cbm.CHROUT"]?.type shouldBe StNodeType.EXTSUB
st.lookup("cbm.CHROUT")?.type shouldBe StNodeType.EXTSUB
st.lookupUnscoped("sizeof")?.type shouldBe StNodeType.BUILTINFUNC
}
test("peek and poke argument types") {
Loaded 100 of 154 files, more files were not shown because too many files have changed in this diff. Show more