Compare commits

...
91 Commits
Author SHA1 Message Date
Irmen de Jong 4030a1b9e5 cleanup, animals example removed because it's superseded by the pointers/animalgame example now 2025-12-16 16:48:34 +01:00
Irmen de Jong 0217d05c68 IR: loadfield/storefield now accept offsets up to 65535 2025-12-16 14:56:11 +01:00
Irmen de Jong 8553f3c9f1 IR: loadfield is a better name 2025-12-16 12:57:45 +01:00
Irmen de Jong d157d03ea0 IR: optimize load+add+load into single loadm with offset (also store)
update gradle wrapper to 9.2.0
2025-12-16 12:17:52 +01:00
Irmen de Jong 1799b5f00c updated bundled zsmkit version to 2.8 2025-12-15 18:41:28 +01:00
Irmen de Jong 2ddb5e11b7 updated symboldumps 2025-12-13 02:12:00 +01:00
Irmen de Jong 3aa8b9fd12 turn prefix - numbers into negative numbers asap to avoid type problems later 2025-12-13 01:13:52 +01:00
Irmen de Jong 51300196e8 fix more long comparisons with arrays, fixes #196 2025-12-12 21:01:51 +01:00
Irmen de Jong f7b2f19cba remove restriction about long comparison with expression 2025-12-11 21:43:53 +01:00
Irmen de Jong 014a82a1ee always put all struct types as .struct in asm code to make them all accessible for size and offsets 2025-12-10 21:39:44 +01:00
Irmen de Jong 745cf3d958 IR: removed loadix/storeix instructions 2025-12-09 22:55:53 +01:00
Irmen de Jong c4aa681d58 IR: better name for loadfield/storefield instructions 2025-12-09 00:40:26 +01:00
Irmen de Jong feb0774bbd in benchmark-c routines, use same CIA timer as the original C code did so the program size is better comparable 2025-12-08 01:08:33 +01:00
Irmen de Jong 2ccfb2ac90 fix double evaluation (sign extension) of word to long assignment. Fixes #201 2025-12-07 22:23:59 +01:00
Irmen de Jong 2191c4b7d8 remove redundant expression assignment before multiplying certain words 2025-12-07 14:06:59 +01:00
Irmen de Jong 39c1cf0c3c fix diskio lf_start_list_dirs() and lf_start_list_files() not processing the pattern argument 2025-12-07 05:11:08 +01:00
Irmen de Jong c693be0dd6 fix byte subtract from long
fix IR register check error for %ir segments
2025-12-06 20:40:42 +01:00
Irmen de Jong 0f433055a7 fix pointer dereference division and modulus , fixes #199 2025-12-05 22:19:54 +01:00
Irmen de Jong 76c95ba6fa some more obscure inplace modifications implemented 2025-12-05 21:00:14 +01:00
Irmen de Jong bd272a8567 optimize some specialized cases of long << 2025-12-05 01:08:19 +01:00
Irmen de Jong b38fff76e9 hack to work around struct name prefix inconsistency
see github issue 198
2025-12-03 21:07:08 +01:00
Irmen de Jong 07b7639ff2 better error msg 2025-12-02 22:56:40 +01:00
Irmen de Jong 6871561c09 fix parent node error in sizeof() 2025-12-01 23:21:14 +01:00
Irmen de Jong 77a71138ad fix typo r8r0sl -> r8r9sl 2025-11-30 22:05:57 +01:00
Irmen de Jong 165f94bc93 fix long expression <0 / >0 comparison 2025-11-30 16:57:05 +01:00
Irmen de Jong b75090f928 fix some register clobbering with long operations 2025-11-30 16:44:36 +01:00
Irmen de Jong 62b0c82f93 implement long <=0 and long >=0 comparisons
make sure the adpcm benchmark doesn't use random memory contents
2025-11-30 16:25:58 +01:00
Irmen de Jong b1ef863c7f make $8000000 a valid long integer (-2147483648) 2025-11-26 23:10:59 +01:00
Irmen de Jong e78345410f fix long-- high word wrap bug 2025-11-26 20:13:17 +01:00
Irmen de Jong 8b86f97aaa Implement struct field long and signed word bitshifts. Fixes #194 2025-11-25 22:28:28 +01:00
Irmen de Jong 93135774e6 fix IR signed bitshift right 2025-11-25 22:16:28 +01:00
Irmen de Jong a64f27c6b0 add cx16.r0r1sl, cx16.r2r3sl, ... that memory-map signed longs on the virtual registers 2025-11-25 21:13:03 +01:00
Irmen de Jong 69ef63c96d tweaks 2025-11-24 22:23:40 +01:00
Irmen de Jong df1a2a1611 also optimize BRA+RTS into just BRA
release 12.0
2025-11-23 15:18:05 +01:00
Irmen de Jong d19a3af9ed change some single use float global constants to their asm proc 2025-11-21 21:35:51 +01:00
Irmen de Jong 352c11ad9f optimize float<>0 into sgn(float)<>0 2025-11-21 00:57:43 +01:00
Irmen de Jong 9504711fc7 IR/VM: SGN sets status bits 2025-11-19 21:37:04 +01:00
Irmen de Jong 590feda903 IR: fix sgn(float) register type error 2025-11-19 21:11:31 +01:00
Irmen de Jong 6e7e2922bf moved the float <> long cast routines to float.asm where the other cast routines are too 2025-11-19 18:45:32 +01:00
Irmen de Jong 01df1f0083 way more efficient implementation of internal_cast_as_long now using QINT 2025-11-19 18:32:40 +01:00
Irmen de Jong 397299bd1d implement 6502 codegen for casting float to long (super slow...) 2025-11-19 00:05:18 +01:00
Irmen de Jong c275aacd38 implement 6502 codegen for casting long to float 2025-11-18 23:26:12 +01:00
Irmen de Jong 6a6e18773e C64: key repeat is now enabled at program startup, to fall in line with the default key repeat behavior on the C128 and X16. 2025-11-18 21:09:13 +01:00
Irmen de Jong 99e037489b IR: added float<>long casts, cx16: blink_cursor extapi tested in test.p8 2025-11-17 23:13:56 +01:00
Irmen de Jong fb5290e17b cx16: added diskio.f_fatlba() 2025-11-15 20:17:26 +01:00
Irmen de Jong 818774ab84 added conv.hex2long(), added more new X16 extapi calls (rom 49+) 2025-11-15 17:52:34 +01:00
Irmen de Jong d667312acc Merge branch 'refs/heads/12.1-SNAPSHOT'
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-11-15 15:15:06 +01:00
Irmen de Jong 64d0cd87a8 diskio.f_seek() and f_tell() now use longs instead of separate words
also fix bug when returning multiple long values
2025-11-15 15:13:26 +01:00
Irmen de Jong 2fffbdde89 todo 2025-11-14 20:05:46 +01:00
Irmen de Jong dc8ea31c49 fix codegen error for a word array equality test 2025-11-13 21:18:04 +01:00
Irmen de Jong f4ead66e91 fix param name breakage in PET txt.setcc for ignored charcolor parameter 2025-11-12 22:00:30 +01:00
Irmen de Jong 314e7f5691 doc updates 2025-11-10 23:42:29 +01:00
Irmen de Jong da31465b7f added the missing smallstack to buffers module 2025-11-10 00:21:13 +01:00
Irmen de Jong 397a907088 fix handling and errors of using long addresses in memread/memwrite 2025-11-09 18:17:57 +01:00
Irmen de Jong 50c6962e1f fix symbol lookup crash 2025-11-09 15:10:10 +01:00
Irmen de Jong b7d1fb1342 translate address-of into + expression for non-const address ptrs too,
also IR optimization to use immediate arithmetic opcodes if possible
2025-11-09 01:36:34 +01:00
Irmen de Jong afb458a7da fix crash in long address-of 2025-11-08 22:24:37 +01:00
Irmen de Jong da3c7f267f improved identifier const value handling if types differ 2025-11-08 20:34:51 +01:00
Irmen de Jong b7ba7c50b1 handle &ptr[offset] for non-uword ptr 2025-11-08 20:31:28 +01:00
Irmen de Jong bdbfe7048b updated library symboldumps 2025-11-08 00:21:53 +01:00
Irmen de Jong 79039b66d4 beta6 2025-11-07 23:40:03 +01:00
Irmen de Jong 833e463525 String indexing bound check now includes the terminating 0 character. Also fix negative indexes on strings.
fixes #190
2025-11-07 23:20:44 +01:00
Irmen de Jong 6611e4e092 improve error message 2025-11-07 01:22:41 +01:00
Irmen de Jong 1f31cb18e4 IR: fix missing source lines in p8ir file 2025-11-06 00:39:31 +01:00
Irmen de Jong bc8ba252a5 made Position line, col, endcol all 1-based
added a precise (but slow) back face culling routine to the cobra-mk3 example
2025-11-05 00:25:43 +01:00
Irmen de Jong c353dd40bf put on..call jumplist in correct scope, fixes #197 2025-11-03 23:46:49 +01:00
Irmen de Jong 928ef6bbaa fix on..call parse problem, also struct name cannot be a keyword 2025-11-03 21:21:07 +01:00
Irmen de Jong 45dde856c6 upgrade to kotlin 2.2.21 2025-11-02 20:08:59 +01:00
Irmen de Jong e1ccef4e89 sort the library routines better, updated result of crc32 benchmark 2025-11-02 16:58:49 +01:00
Irmen de Jong 4d3f0ec223 sizeof(string) is now defined 2025-11-01 21:19:30 +01:00
Irmen de Jong b73c958c4a ir immediate syntax fixes 2025-11-01 16:55:00 +01:00
Irmen de Jong 2eac457e1c clarify string assignment (or rather, the lack thereof) 2025-11-01 00:40:52 +01:00
Irmen de Jong 815ef7e654 add check for not yet implement long expression comparisons 2025-11-01 00:26:36 +01:00
Irmen de Jong 0cd8c4f87e add strings.ncopy and nappend (length limited) 2025-10-31 21:22:15 +01:00
Irmen de Jong b02a8ed954 strings can no longer be assigned by-value. Use strings.copy() instead. Fixes #189 2025-10-31 19:22:40 +01:00
Irmen de Jong b1e07f3fdb better error when trying to use a const pointer (which is not supported yet) 2025-10-30 20:58:21 +01:00
Irmen de Jong fc8727f81e allow pointer to pointer assignment in cast codegen, fixes #191 2025-10-30 19:35:44 +01:00
Irmen de Jong ec728bad52 fix certain float array assignment, fixes #193 2025-10-30 01:45:25 +01:00
Irmen de Jong a5e827e40f implement some more long array operations 2025-10-30 01:20:59 +01:00
Irmen de Jong 34061c5a63 implement inplace bitwise operations on long struct fields 2025-10-28 23:44:34 +01:00
Irmen de Jong 9c2bcab4a5 fix more ptr/long issues 2025-10-28 23:08:12 +01:00
Irmen de Jong 2f07f41664 detect self-referencing aliases 2025-10-23 21:24:14 +02:00
Irmen de Jong 7027304597 put some upper bounds on more internal compiler loops 2025-10-23 20:37:44 +02:00
Irmen de Jong ee75333891 implement more long assignments through pointers 2025-10-21 22:55:46 +02:00
Irmen de Jong 6a70fb0480 deal with invalid sqrt results (negative argument), allow sqrt of longs (like floats) 2025-10-21 21:01:03 +02:00
Irmen de Jong ebc738b132 'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts 2025-10-20 00:33:40 +02:00
Irmen de Jong e5939be0bd some more long operations implemented 2025-10-19 23:03:17 +02:00
Irmen de Jong 0c0affd1bf cx16 diskio.load_size() now returns full long size. Fixes #184 2025-10-19 21:18:46 +02:00
Irmen de Jong c7158fd968 implement more long operations on struct fields 2025-10-17 01:55:11 +02:00
Irmen de Jong de2f3f0d0d tweak TODO messages for longs 2025-10-16 23:20:34 +02:00
Irmen de Jong c0286e3349 added txt.size() to return text screen width and height in 1 call. Also fixed txt.width/height on the C128 (where SCREEN reports 1 less for them...) 2025-10-16 00:33:14 +02:00
154 changed files with 4405 additions and 1902 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>
+7 -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:*
+14 -1
View File
@@ -2,15 +2,28 @@
Several benchmarks of https://thred.github.io/c-bench-64/
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
* Prog8 program size is consistently the smallest by a fair bit.
* Prog8 program size is consistently one of the smallest.
* Prog8 execution speed places it more or less in the middle of the stack.
Measured with Prog8 V12.0 .
Benchmarks ran on a PAL C64 emulated through VICE.
textual results:
| benchmark | size | time |
|-----------|------|----------|
| crc8 | 1568 | 2.0 sec |
| crc16 | 1608 | 2.5 sec |
| crc32 | 1812 | 4.2 sec |
| pow | 2152 | 11.7 sec |
| sieve | 1772 | 22.1 sec |
| sieve_bit | 1918 | 40.1 sec |
![crc8](result-crc8.png)
+98
View File
@@ -0,0 +1,98 @@
%import textio
cia {
ubyte freq
const ubyte CNT = 200
sub calibrate() {
txt.print("calibrating frequency: ")
tod_init(0)
txt.print_ub(tod_freq())
txt.print(" hz\n")
tod_reset()
}
sub print_time() {
uword t = tod_get10()
txt.print("(cia) time: ")
txt.print_uw(t / 10)
txt.chrout('.')
txt.print_uw(t % 10)
txt.print(" sec.\n")
}
sub tod_reset() {
; set the tod to 0
c64.CIA2TODHR = 0
c64.CIA2TODMIN = 0
c64.CIA2TODSEC = 0
c64.CIA2TOD10 = 0
}
sub tod_get10() -> uword {
ubyte h, m, s, t
uword time
h = c64.CIA2TODHR
m = c64.CIA2TODMIN
s = c64.CIA2TODSEC
t = c64.CIA2TOD10
time = t
time += (s & $0f) * (10 as uword)
time += (s >> 4) * (100 as uword)
time += (m & $0f) * 600
time += (m >> 4) * 6000
return time
}
sub tod_init(ubyte f) {
if (f == 0)
freq = tod_detect_freq()
else
freq = f
if (freq == 50)
c64.CIA2CRA |= $80
else
c64.CIA2CRA &= $7f
}
sub tod_freq() -> ubyte {
return freq
}
sub tod_detect_freq() -> ubyte {
uword cbl
c64.CIA2CRB = $40 ; stop timer
c64.CIA2CRA = $80 ; stop timer
; set ta to overflow every 10000 count (~= 10ms)
c64.CIA2TAL = $10
c64.CIA2TAH = $27
c64.CIA2TBL = CNT
c64.CIA2TBH = 0
tod_reset()
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
while c64.CIA2TODSEC == 0 {
; wait for tod to count 1s
}
; cal=CIA2.ta_lo;
; cah=CIA2.ta_hi;
cbl = c64.CIA2TBL
; cbh=CIA2.tb_hi;
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
; printf("elapsed ~= %d0ms\n",CNT-cbl);
if CNT - cbl > 90
return 50
else
return 60
}
}
+5 -5
View File
@@ -1,5 +1,5 @@
%import textio
%import floats
%import ciatimer
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
@@ -7,12 +7,11 @@
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -40,12 +39,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uwhex(EXPECTED, true)
txt.nl()
return true
}
+5 -5
View File
@@ -1,5 +1,5 @@
%import textio
%import floats
%import ciatimer
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
@@ -7,12 +7,11 @@
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -40,12 +39,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ulhex(EXPECTED, true)
txt.nl()
return true
}
+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -36,12 +35,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ubhex(EXPECTED, true)
txt.nl()
return true
}
+3 -3
View File
@@ -1,16 +1,16 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -46,12 +45,13 @@ test {
if prime_count == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -46,12 +45,13 @@ test {
if prime_count == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
+1 -1
View File
@@ -7,7 +7,7 @@ adpcm {
while cbm.RDTIM16()<max_time {
adpcm.init(0,0)
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
repeat 252/2 {
unroll 2 {
ubyte @zp nibble = @(nibbles_ptr)
+5 -5
View File
@@ -472,7 +472,7 @@ elite_galaxy {
generate_next_planet()
textelite.num_commands++
}
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
init_market_for_planet()
}
@@ -494,7 +494,7 @@ elite_galaxy {
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
if elite_util.prefix_matches(nameptr, elite_planet.name) {
ubyte distance = elite_planet.distance(x, y)
if distance < current_distance {
@@ -532,7 +532,7 @@ elite_galaxy {
; else
; txt.chrout('-')
; txt.spc()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.display(true, distance)
}
pn++
@@ -548,7 +548,7 @@ elite_galaxy {
str current_name = " " ; 8 max
ubyte pn = 0
current_name = elite_planet.name
void strings.copy(elite_planet.name, current_name)
init(number)
; txt.clear_screen()
; txt.print("Galaxy #")
@@ -569,7 +569,7 @@ elite_galaxy {
generate_next_planet()
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
+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 {
@@ -112,6 +112,7 @@ 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)),
+4 -1
View File
@@ -14,8 +14,11 @@ fun Number.toHex(): String {
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toLong()
if(integer<0)
if(integer<0) {
if(integer==-2147483648L)
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
return '-' + abs(integer).toHex()
}
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
+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,7 +878,7 @@ 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("""
@@ -882,7 +894,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLong(target.pointer!!, value.number.toInt())
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtIdentifier && value.type.isLong) {
@@ -905,7 +917,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLongVar(target.pointer!!, asmSymbolName(value))
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtTypeCast && value.type.isLong) {
@@ -920,7 +932,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value.value.type.isWord) {
@@ -945,19 +957,19 @@ class AsmGen6502Internal (
sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base)
}
TargetStorageKind.POINTER -> {
throw AssemblyError("assign typecasted long into pointer ${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.kind} at ${target.position} - use simple expressions and temporary variables for now")
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
} else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else {
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
}
target.datatype.isFloat -> {
@@ -1426,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("""
@@ -1988,9 +2003,9 @@ $repeatLabel""")
lda #<$leftName
ldy #>$leftName""")
if(lessOrEquals)
out("jsr floats.vars_lesseq_f")
out(" jsr floats.vars_lesseq_f")
else
out("jsr floats.vars_less_f")
out(" jsr floats.vars_less_f")
}
fun lessf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
@@ -2109,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")
@@ -2245,7 +2276,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #$offset
@@ -2312,7 +2343,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #0
@@ -2423,12 +2454,13 @@ $repeatLabel""")
}
internal fun pushLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
// may clobber P8ZP_SCRATCH_B1, X, Y.
if(numberOfLongs==0) return
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
sta P8ZP_SCRATCH_REG
tax
lda cx16.$startregName
pha
lda cx16.$startregName+1
@@ -2437,12 +2469,12 @@ $repeatLabel""")
pha
lda cx16.$startregName+3
pha
lda P8ZP_SCRATCH_REG""")
txa""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
sta P8ZP_SCRATCH_REG
tax
sty P8ZP_SCRATCH_B1
ldy #0
- lda cx16.$startregName,y
@@ -2451,7 +2483,7 @@ $repeatLabel""")
cpy #$numbytes
bne -
ldy P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_REG""")
txa""")
}
internal fun popLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
@@ -2460,7 +2492,7 @@ $repeatLabel""")
val startregName = startreg.startregname()
if(numberOfLongs==1) {
out("""
sta P8ZP_SCRATCH_REG
tax
pla
sta cx16.$startregName+3
pla
@@ -2469,12 +2501,12 @@ $repeatLabel""")
sta cx16.$startregName+1
pla
sta cx16.$startregName
lda P8ZP_SCRATCH_REG""")
txa""")
return
}
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
out("""
sta P8ZP_SCRATCH_REG
tax
sty P8ZP_SCRATCH_B1
ldy #$numbytes-1
- pla
@@ -2482,7 +2514,7 @@ $repeatLabel""")
dey
bpl -
ldy P8ZP_SCRATCH_B1
lda P8ZP_SCRATCH_REG""")
txa""")
}
}
@@ -2497,4 +2529,8 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
}
internal fun Double.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
internal fun Int.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
@@ -544,7 +544,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
val third = lines[2].value
if(!haslabel(second)) {
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
if ((" jmp" in first || "\tjmp" in first || " bra" in first || "\tbra" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null)
}
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
@@ -40,7 +40,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"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)
@@ -416,7 +416,7 @@ 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
@@ -538,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 -> {
@@ -557,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 -> {
@@ -577,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")
@@ -620,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 -> {
@@ -641,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 -> {
@@ -652,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")
@@ -684,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 -> {
@@ -703,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 -> {
@@ -721,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")
@@ -763,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 -> {
@@ -784,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 -> {
@@ -795,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")
@@ -279,7 +279,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
// pass argument via a register parameter
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
throw AssemblyError("argument type incompatible")
throw AssemblyError("argument type incompatible ${value.type} vs param ${parameter.value.type} at ${parameter.value.position}")
val paramRegister: RegisterOrStatusflag = when(sub) {
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
@@ -362,12 +362,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType)
return true
if(argType.isBool && paramType.isBool)
return true
// signed/unsigned args vs parameters are considered compatible
if(argType.isByte && paramType.isByte)
return true
if(argType.isWord && paramType.isWord)
return true
// only signed longs and floats are available so these were already covered
// if(argType.isLong && paramType.isLong)
// return true
// if(argType.isFloat && paramType.isFloat)
// return true
// we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa
@@ -942,7 +942,7 @@ _jump jmp (${target.asmLabel})
asmgen.out(" lda $varname+1")
}
is PtAddressOf -> {
require(!long)
require(!long) {"addresses must still be words not longs"}
if(value.isFromArrayElement) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
@@ -955,9 +955,14 @@ _jump jmp (${target.asmLabel})
}
}
else -> {
require(!long)
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
if(long) {
// note: clobbers R14+R15
asmgen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, true)
asmgen.out(" lda cx16.r14+3")
} else {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0")
}
}
}
}
@@ -1294,7 +1299,11 @@ _jump jmp (${target.asmLabel})
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long <= 0 ${value.position}")
loadAndCheck0OrSignIntoCarry(value, true)
if (jump != null)
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1306,7 +1315,11 @@ _jump jmp (${target.asmLabel})
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
if(lessEquals) {
TODO("long >= 0 ${value.position}")
loadAndCheck0OrSignIntoCarry(value, false)
if (jump != null)
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
} else {
loadAndCmp0MSB(value, true)
if (jump != null)
@@ -1316,6 +1329,28 @@ _jump jmp (${target.asmLabel})
}
}
private fun loadAndCheck0OrSignIntoCarry(value: PtExpression, negative: Boolean) {
if(value is PtIdentifier) {
val varname = asmgen.asmVariableName(value)
asmgen.out("""
lda #<$varname
ldy #>$varname
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
asmgen.out(if(negative) " sec" else " clc")
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
} else {
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
asmgen.out("""
lda #<cx16.r14
ldy #>cx16.r14
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
asmgen.out(if(negative) " sec" else " clc")
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
}
}
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
// special case for (u)word == 0
@@ -1406,7 +1441,7 @@ _jump jmp (${target.asmLabel})
if(left is PtIdentifier) {
val leftvar = asmgen.asmVariableName(left)
if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda $leftvar
cmp #$${hex.substring(6,8)}
@@ -1437,7 +1472,7 @@ _jump jmp (${target.asmLabel})
// 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.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6,8)}
@@ -1492,6 +1527,7 @@ _jump jmp (${target.asmLabel})
stmt: PtIfElse
) {
// this comparison is not part of an expression but part of an if statement, there's no need to save the previous values of the temp registers
if(operator=="<" || operator ==">=") {
assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R14R15_32, left.type.isSigned)
assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R12R13_32, left.type.isSigned)
@@ -1933,7 +1969,7 @@ _jump jmp (${target.asmLabel})
if(notEquals)
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
else
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
translateAYEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
}
}
}
@@ -2113,4 +2149,5 @@ _jump jmp (${target.asmLabel})
else -> throw AssemblyError("expected comparison operator")
}
}
}
}
@@ -368,7 +368,7 @@ 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("""
@@ -386,7 +386,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// 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)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6, 8)}
@@ -424,7 +424,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// 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)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp $varname2
@@ -493,7 +493,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
@@ -518,7 +518,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
@@ -412,10 +412,9 @@ internal class ProgramAndVarsGen(
asmgen.out("; struct types")
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
symboltable.allStructTypes().distinctBy { it.name }.forEach { structtype ->
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
asmgen.out("${it.structName} .struct $structargs\n")
asmgen.out("${structtype.scopedNameString} .struct $structargs\n")
structtype.fields.withIndex().forEach { (index, field) ->
val dt = field.first
val varname = "f${index}"
@@ -425,7 +424,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .endstruct\n")
}
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
val (instancesNoInit, instances) = symboltable.allStructInstances().partition { it.initialValues.isEmpty() }
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
asmgen.out(" .section BSS\n")
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
@@ -938,7 +937,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.sortedBy { it.name }.forEach {
if(it.dt==BaseDataType.FLOAT)
if(it.dt.isFloat)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.name} = ${it.value.toHex()}")
@@ -1044,7 +1043,7 @@ internal class ProgramAndVarsGen(
}
dt.isLongArray -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
val hexnum = number.absoluteValue.toLongHex()
if(number>=0)
"$$hexnum"
else
@@ -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
@@ -201,7 +202,7 @@ internal class AssignmentAsmGen(
assignRegisterByte(tgt, CpuRegister.A, false, false)
}
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
// simple case: assign a constant boolean (0 or 1)
@@ -294,6 +295,9 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
elementDt.isLong -> {
assignVariableLong(assign.target, "$arrayVarName+$indexValue", DataType.LONG)
}
elementDt.isFloat -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
assignFloatFromAY(assign.target)
@@ -313,6 +317,9 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
}
elementDt.isLong -> {
assignVariableLongIndexed(assign.target, arrayVarName, value)
}
elementDt.isFloat -> {
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
asmgen.out("""
@@ -638,21 +645,8 @@ internal class AssignmentAsmGen(
returnDt?.isByteOrBool==true -> assignRegisterByte(target, CpuRegister.A, returnDt.isSigned, false) // function's byte result is in A
returnDt?.isWord==true -> assignRegisterpairWord(target, RegisterOrPair.AY) // function's word result is in AY
returnDt==BaseDataType.STR -> {
val targetDt = target.datatype
when {
targetDt.isString -> {
asmgen.out("""
tax
lda #<${target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${target.asmVarname}
sta P8ZP_SCRATCH_W1+1
txa
jsr prog8_lib.strcpy""")
}
targetDt.isUnsignedWord -> assignRegisterpairWord(target, RegisterOrPair.AY)
else -> throw AssemblyError("str return value type mismatch with target")
}
if (target.datatype.isUnsignedWord) assignRegisterpairWord(target, RegisterOrPair.AY)
else throw AssemblyError("str return value type mismatch with target")
}
returnDt== BaseDataType.LONG -> {
// longs are in R14:R15 (r14=lsw, r15=msw)
@@ -787,8 +781,12 @@ internal class AssignmentAsmGen(
return if(translatedOk)
true
else
else {
if(expr.type.isLong && expr.operator in "*/%")
TODO("long multiplication or division ${expr.position}")
anyExprGen.assignAnyExpressionUsingStack(expr, assign)
}
}
private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
@@ -1180,7 +1178,6 @@ internal class AssignmentAsmGen(
}
}
else {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
if(expr.definingBlock()!!.options.veraFxMuls){
// cx16 verafx hardware mul
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
@@ -1741,49 +1738,9 @@ internal class AssignmentAsmGen(
val left = expr.left
val right = expr.right
when(right) {
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
TODO("add/subtract long into ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
}
}
is PtNumber -> {
asmgen.assignExpressionTo(left, target)
val hex = right.number.toInt().toString(16).padStart(8, '0')
val hex = right.number.toLongHex()
if (target.kind == TargetStorageKind.VARIABLE) {
if (expr.operator == "+") {
asmgen.out("""
@@ -1851,8 +1808,88 @@ internal class AssignmentAsmGen(
sta cx16.$startreg+3""")
}
return true
} else if(target.kind==TargetStorageKind.POINTER) {
// not an expression, no need to save R14/R15
assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, target.datatype.isSigned)
pointergen.assignLongVar(target.pointer!!, "cx16.r14")
return true
} else {
TODO("add/subtract long const into ${target.kind} ${target.position} - use simple expressions and temporary variables for now")
TODO("add/subtract long const into ${target.kind} ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
}
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
// isn't an expression, so no need to preserve R12-R15
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R12R13_32, left.type.isSigned)
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R14R15_32, left.type.isSigned)
if(expr.operator=="+") {
asmgen.out("""
clc
lda cx16.r12
adc cx16.r14
sta cx16.r12
lda cx16.r12+1
adc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
adc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
adc cx16.r14+3
sta cx16.r12+3""")
} else {
asmgen.out("""
sec
lda cx16.r12
sbc cx16.r14
sta cx16.r12
lda cx16.r12+1
sbc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
sbc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
sbc cx16.r14+3
sta cx16.r12+3""")
}
asmgen.assignRegister(RegisterOrPair.R12R13_32, target)
return true
}
}
else -> {
@@ -2617,7 +2654,7 @@ $endLabel""")
}
else -> {}
}
} else if(valueDt.isUnsignedWord) {
} else if(valueDt.isWord) {
when(target.register) {
RegisterOrPair.A,
RegisterOrPair.X,
@@ -2636,7 +2673,38 @@ $endLabel""")
// 'cast' uword into a 16 bits register, just assign it
return assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
}
in combinedLongRegisters -> TODO("assign wprd to long reg ${value.position}")
RegisterOrPair.R0R1_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R0, false)
return asmgen.signExtendLongVariable("cx16.r0", valueDt.base)
}
RegisterOrPair.R2R3_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R2, false)
return asmgen.signExtendLongVariable("cx16.r2", valueDt.base)
}
RegisterOrPair.R4R5_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R4, false)
return asmgen.signExtendLongVariable("cx16.r4", valueDt.base)
}
RegisterOrPair.R6R7_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R6, false)
return asmgen.signExtendLongVariable("cx16.r6", valueDt.base)
}
RegisterOrPair.R8R9_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R8, false)
return asmgen.signExtendLongVariable("cx16.r8", valueDt.base)
}
RegisterOrPair.R10R11_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R10, false)
return asmgen.signExtendLongVariable("cx16.r10", valueDt.base)
}
RegisterOrPair.R12R13_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R12, false)
return asmgen.signExtendLongVariable("cx16.r12", valueDt.base)
}
RegisterOrPair.R14R15_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R14, false)
return asmgen.signExtendLongVariable("cx16.r14", valueDt.base)
}
else -> {}
}
}
@@ -2671,8 +2739,11 @@ $endLabel""")
} else if(valueDt.isLong && targetDt.isWord) {
// long to word, just take the lsw
assignCastViaLswFunc(value, target)
} else if(valueDt.isPointer && targetDt.isPointer) {
// pointer type A to pointer type B, just assign
assignExpressionToRegister(value, target.register!!, valueDt.isSigned)
} else
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker ${value.position}")
}
}
}
@@ -2812,7 +2883,8 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
BaseDataType.LONG -> TODO("cast float to long")
else -> throw AssemblyError("weird type $targetDt")
}
}
@@ -2842,6 +2914,7 @@ $endLabel""")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2878,6 +2951,7 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
@@ -2911,6 +2985,7 @@ $endLabel""")
BaseDataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2950,6 +3025,7 @@ $endLabel""")
BaseDataType.UWORD, BaseDataType.POINTER -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2979,6 +3055,14 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
BaseDataType.LONG -> {
asmgen.out("""
sta cx16.r0L
sty cx16.r0H
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
jsr floats.cast_as_long""")
}
else -> throw AssemblyError("weird type")
}
}
@@ -2988,6 +3072,15 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
} else if(targetDt.isWord || targetDt.isPointer) {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
} else if(targetDt.isFloat) {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta cx16.r0L
sty cx16.r0H
lda #<$sourceAsmVarName
ldy #>$sourceAsmVarName
jsr floats.cast_from_long""")
} else
throw AssemblyError("weird type")
}
@@ -3026,6 +3119,7 @@ $endLabel""")
else
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
st${regs.toString().lowercase()} $targetAsmVarName
@@ -3073,6 +3167,7 @@ $endLabel""")
asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
asmgen.signExtendLongVariable(targetAsmVarName, sourceDt)
@@ -3108,6 +3203,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3146,6 +3242,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3351,31 +3448,16 @@ $endLabel""")
}
private fun assignVariableString(target: AsmAssignTarget, varName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
when {
target.datatype.isUnsignedWord -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
}
target.datatype.isString || target.datatype.isUnsignedByteArray || target.datatype.isByteArray -> {
asmgen.out("""
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$varName
ldy #>$varName
jsr prog8_lib.strcpy""")
}
else -> throw AssemblyError("assign string to incompatible variable type")
}
}
else -> throw AssemblyError("string-assign to weird target")
if (target.kind == TargetStorageKind.VARIABLE) {
if (target.datatype.isUnsignedWord) {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
} else throw AssemblyError("assign string to incompatible variable type ${target.position}")
}
else throw AssemblyError("string-assign to weird target ${target.position}")
}
private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
@@ -3429,7 +3511,6 @@ $endLabel""")
lda $varName+3
sta ${target.asmVarname}+3,y""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname()
@@ -3479,7 +3560,60 @@ $endLabel""")
else -> throw AssemblyError("wrong dt ${target.position}")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLongVar(target.pointer!!, varName)
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
private fun assignVariableLongIndexed(target: AsmAssignTarget, arrayVarName: String, index: PtArrayIndexer) {
asmgen.loadScaledArrayIndexIntoRegister(index, CpuRegister.Y)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $arrayVarName,y
sta ${target.asmVarname}
lda $arrayVarName+1,y
sta ${target.asmVarname}+1
lda $arrayVarName+2,y
sta ${target.asmVarname}+2
lda $arrayVarName+3,y
sta ${target.asmVarname}+3""")
}
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname()
asmgen.out("""
lda $arrayVarName,y
sta cx16.$regstart
lda $arrayVarName+1,y
sta cx16.$regstart+1
lda $arrayVarName+2,y
sta cx16.$regstart+2
lda $arrayVarName+3,y
sta cx16.$regstart+3""")
}
TargetStorageKind.ARRAY -> {
val deref = target.array!!.pointerderef
if(deref!=null) {
pointergen.assignLongVar(IndexedPtrTarget(target), arrayVarName)
return
}
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.X)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out("""
lda $arrayVarName,y
sta ${target.asmVarname},x
lda $arrayVarName+1,y
sta ${target.asmVarname}+1,x
lda $arrayVarName+2,y
sta ${target.asmVarname}+2,x
lda $arrayVarName+3,y
sta ${target.asmVarname}+3,x""")
}
TargetStorageKind.POINTER -> pointergen.assignIndexedPointer(target, arrayVarName, index, DataType.arrayFor(BaseDataType.LONG))
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -3662,11 +3796,11 @@ $endLabel""")
pointergen.assignFloatAY(IndexedPtrTarget(target))
return
}
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.A)
asmgen.assignExpressionToVariable(target.array.index, "P8ZP_SCRATCH_REG", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla")
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -3674,6 +3808,7 @@ $endLabel""")
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_REG
jsr floats.set_array_float""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
@@ -3984,7 +4119,18 @@ $endLabel""")
else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
}
TargetStorageKind.ARRAY -> {
TODO("assign 32 bits int into array ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
val arrayVarName = asmgen.asmSymbolName(target.array.variable!!)
val startreg = pairedRegisters.startregname()
asmgen.out("""
lda cx16.$startreg
sta $arrayVarName,y
lda cx16.$startreg+1
sta $arrayVarName+1,y
lda cx16.$startreg+2
sta $arrayVarName+2,y
lda cx16.$startreg+3
sta $arrayVarName+3,y""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> {
@@ -4004,7 +4150,10 @@ $endLabel""")
sta cx16.$targetStartReg+3""")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> {
val startreg = pairedRegisters.startregname()
pointergen.assignLongVar(target.pointer!!, "cx16.$startreg")
}
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -4499,7 +4648,7 @@ $endLabel""")
stz $startreg+2
stz $startreg+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, 0)
TargetStorageKind.VOID -> { /* do nothing */ }
}
return
@@ -4514,7 +4663,7 @@ $endLabel""")
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset")
}
}
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
store(hex.substring(6,8), 0)
store(hex.substring(4,6), 1)
store(hex.substring(2,4), 2)
@@ -4527,7 +4676,7 @@ $endLabel""")
return
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta ${target.asmVarname},y
@@ -4542,7 +4691,7 @@ $endLabel""")
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname()
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta cx16.$regstart
@@ -4553,7 +4702,7 @@ $endLabel""")
lda #$${hex.take(2)}
sta cx16.$regstart+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, long)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5223,7 +5372,7 @@ $endLabel""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), ignoreDatatype, scope)
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5284,7 +5433,7 @@ $endLabel""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), ignoreDatatype, scope)
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5309,7 +5458,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.inplaceLongNegate(PtrTarget(target), scope)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -4,6 +4,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
@@ -112,9 +113,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
SourceStorageKind.EXPRESSION -> inplacemodificationLongWithExpression(target.asmVarname, operator, value.expression!!)
SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(target.asmVarname, operator, regName(value))
SourceStorageKind.ARRAY -> TODO("inplace modify long with array value ${target.position}")
SourceStorageKind.MEMORY -> TODO("inplace modify long with memread value ${target.position}")
}
}
target.datatype.isFloat -> {
@@ -123,7 +124,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -163,18 +164,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
is PtIdentifier -> {
val pointer = memory.address as PtIdentifier
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into pointer ${target.position}")
SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
SourceStorageKind.LITERALBOOLEAN -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
SourceStorageKind.REGISTER -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
SourceStorageKind.MEMORY -> ptrgen. inplacemodificationBytePointerWithValue(pointer, operator, value.memory!!)
SourceStorageKind.ARRAY -> ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
} else {
inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
}
}
}
@@ -308,14 +309,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(targetVarName, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(targetVarName, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("inplace long modifiication ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace long modifiication ${target.position}")
SourceStorageKind.MEMORY -> TODO("inplace long modification with memread value ${target.position}")
SourceStorageKind.ARRAY -> TODO("inplace long modification with array value ${target.position}")
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
TODO("inplace long modifiication ${target.position}")
TODO("inplace long modification ${target.position}")
} else {
TODO("inplace long modifiication ${target.position}")
TODO("inplace long modification ${target.position}")
}
}
}
@@ -327,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float array ${target.position}")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(targetVarName, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(targetVarName, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -518,7 +519,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(tempvar, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(tempvar, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
@@ -554,7 +555,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
// it's not an expression so no need to preserve R14:R15
// 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")
}
@@ -710,13 +711,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $variable
sta $variable+1
lda #0
sta $variable
sta $variable""")
if(shift==1) {
asmgen.out("""
asl $variable+1
rol $variable+2
rol $variable+3""")
} else {
asmgen.out("""
ldy #$shift
- asl $variable+1
rol $variable+2
rol $variable+3
dey
bne -""")
}
}
value == 16 -> {
asmgen.out("""
@@ -737,12 +746,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+2
lda #0
sta $variable
sta $variable+1
sta $variable+1""")
if(shift==1) {
asmgen.out(" asl $variable+2 | rol $variable+3")
} else {
asmgen.out("""
ldy #$shift
- asl $variable+2
rol $variable+3
dey
bne -""")
}
}
value == 24 -> {
asmgen.out("""
@@ -755,12 +769,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
value <= 31 -> {
val shift = value-24
asmgen.out("""
if(shift==1) {
asmgen.out(" lda $variable | asl a")
} else {
asmgen.out("""
lda $variable
ldy #$shift
- asl a
dey
bne -
bne -""")
}
asmgen.out("""
sta $variable+3
lda #0
sta $variable
@@ -859,7 +878,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
inc $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
clc
lda $variable
@@ -884,44 +903,34 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
1 -> {
asmgen.out("""
lda $variable
bne +
dec $variable+1
bne +
dec $variable+2
bne +++
lda $variable+1
bne ++
lda $variable+2
bne +
dec $variable+3
+ dec $variable+2
+ dec $variable+1
+ dec $variable""")
}
else -> {
if(value in 1..255) {
asmgen.out("""
lda $variable
sec
lda $variable
sbc #$value
sta $variable
bcs +
dec $variable+1
bne +
dec $variable+2
bne +
dec $variable+3
+""")
} else if(value in 1..65535) {
asmgen.out("""
lda $variable
sec
sbc #<$value
sta $variable
bcs +++
lda $variable+1
sbc #>$value
sta $variable+1
bcs +
dec $variable+2
bne ++
lda $variable+2
bne +
dec $variable+3
+ dec $variable+2
+ dec $variable+1
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
sec
lda $variable
@@ -943,7 +952,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<<" -> if (value > 0) inplaceLongShiftLeft()
">>" -> if (value > 0) inplaceLongShiftRight()
"|" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
ora #$${hex.substring(6,8)}
@@ -959,7 +968,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"&" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
and #$${hex.substring(6,8)}
@@ -975,7 +984,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"^" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
eor #$${hex.substring(6,8)}
@@ -1385,188 +1394,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return false
}
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
}
private fun inplacemodificationBytePointerWithVariable(pointervar: PtIdentifier, operator: String, otherName: String) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
"+" -> asmgen.out(" clc | adc $otherName")
"-" -> asmgen.out(" sec | sbc $otherName")
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
"<<" -> {
asmgen.out("""
ldy $otherName
beq +
- asl a
dey
bne -
+""")
}
">>" -> {
asmgen.out("""
ldy $otherName
beq +
- lsr a
dey
bne -
+""")
}
"&" -> asmgen.out(" and $otherName")
"|" -> asmgen.out(" ora $otherName")
"^" -> asmgen.out(" eor $otherName")
"==" -> {
asmgen.out("""
cmp $otherName
beq +
lda #0
beq ++
+ lda #1
+""")
}
"!=" -> {
asmgen.out("""
cmp $otherName
bne +
lda #0
beq ++
+ lda #1
+""")
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
private fun inplacemodificationBytePointerWithLiteralval(pointervar: PtIdentifier, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
when (operator) {
"+" -> {
if(value==1) {
if(asmgen.options.romable) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" inc a")
else
asmgen.out(" clc | adc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out($$"+\tinc $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" clc | adc #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"-" -> {
if(value==1) {
if(asmgen.options.romable) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" dec a")
else
asmgen.out(" sec | sbc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out($$"+\tdec $ffff\t; modified")
}
} else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" sec | sbc #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"*" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value in asmgen.optimizedByteMultiplications)
asmgen.out(" jsr prog8_math.mul_byte_${value}")
else
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"/" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value==0)
throw AssemblyError("division by zero")
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm | tya")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"%" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value==0)
throw AssemblyError("division by zero")
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"<<" -> {
if (value > 0) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
repeat(value) { asmgen.out(" asl a") }
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
">>" -> {
if (value > 0) {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
repeat(value) { asmgen.out(" lsr a") }
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
}
"&" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"|"-> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"^" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"==" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("""
cmp #$value
beq +
lda #0
beq ++
+ lda #1
+""")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
"!=" -> {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("""
cmp #$value
bne +
lda #0
beq ++
+ lda #1
+""")
asmgen.storeAIntoZpPointerVar(sourceName, false)
}
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
private fun inplacemodificationByteWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
require(dt.isByteOrBool)
if(!value.isSimple()) {
File diff suppressed because it is too large. Load diff
@@ -123,6 +123,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
if(augAssign.target.type.isString)
throw AssemblyError("cannot assign to str type ${augAssign.position}")
// augmented assignment always has just a single target
if (augAssign.target.children.single() is PtIrRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@@ -188,7 +191,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> {
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
addInstr(inplaceInstrs, IRInstruction(opc, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
}
"or=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
@@ -467,7 +473,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
if(assignment.target.type.isString) {
// assigning array and string values is done via an explicit memcopy/stringcopy function calls.
throw AssemblyError("cannot assign to str type ${assignment.position}")
}
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
@@ -550,7 +560,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
if(constOffset in 0..65535) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
@@ -560,18 +570,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
}
val tr = expressionEval.translateExpression(memory!!.address)
@@ -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
@@ -406,7 +406,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(ptrWithOffset!=null) {
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
if(constOffset in 0..255) {
if(constOffset in 0..65535) {
val ptrName = (ptrWithOffset.left as PtIdentifier).name
val pointerReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also {
@@ -416,18 +416,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
}
val tr = translateExpression(mem.address)
@@ -819,6 +807,10 @@ 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)
}
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
addInstr(result, IRInstruction(Opcode.FTOSL, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
}
}
@@ -837,6 +829,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
BaseDataType.LONG -> {
addInstr(result, IRInstruction(Opcode.FFROMSL, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
}
}
@@ -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)
@@ -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 -> {
@@ -55,6 +55,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|| cleanupPushPop(chunk1, indexedInstructions)
|| simplifyConstantReturns(chunk1, indexedInstructions)
|| removeNeedlessLoads(chunk1, indexedInstructions)
|| useArrayIndexingInsteadOfAdds(chunk1, indexedInstructions)
|| removeNops(chunk1, indexedInstructions) // last time, in case one of the optimizers replaced something with a nop
} while (changed)
}
}
@@ -440,6 +442,54 @@ jump p8_label_gen_2
}
else -> {}
}
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
// look if the given register gets an immediate value 1 or 2 istructions back
// returns (index of load instruction, immediate value) or null.
if(idx>=1) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-1 to previous.immediate!!
}
if(idx>=2) {
val previous = indexedInstructions[idx-2].value
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
return idx-2 to previous.immediate!!
}
return null
}
if(ins.reg1!=null) {
val immediate1 = getImmediateLoad(ins.reg1!!)
if(immediate1!=null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
chunk.instructions.removeAt(immediate1.first)
changed=true
} else {
val immediate2 = getImmediateLoad(ins.reg2!!)
if (immediate2 != null) {
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
chunk.instructions.removeAt(immediate2.first)
changed=true
}
}
}
}
// try to use immediate arithmetic instruction if possible
when(ins.opcode) {
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
// Opcode.SUBR -> TODO("ir peephole Subr")
// Opcode.DIVR -> TODO("ir peephole Divr")
// Opcode.DIVSR -> TODO("ir peephole Divsr")
// Opcode.MODR -> TODO("ir peephole Modr")
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
else -> {}
}
}
return changed
}
@@ -505,4 +555,40 @@ jump p8_label_gen_2
}
return changed
}
private fun useArrayIndexingInsteadOfAdds(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if (ins.opcode == Opcode.ADD && ins.immediate!=null && idx>0) {
val load = indexedInstructions[idx-1].value
if((load.opcode==Opcode.LOAD) && load.labelSymbol!=null) {
val lastInstruction = indexedInstructions[idx+1].value
if(lastInstruction.opcode==Opcode.LOADI) {
val targetRegister = lastInstruction.reg1!!
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
val loadm = IRInstruction(Opcode.LOADM, lastInstruction.type, reg1=targetRegister, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
chunk.instructions[idx-1] = loadm
chunk.instructions.removeAt(idx+1)
chunk.instructions.removeAt(idx)
changed = true
}
} else if(lastInstruction.opcode==Opcode.STOREI) {
val targetRegister = lastInstruction.reg1!!
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
val valueLoad = indexedInstructions[idx-2].value
if(valueLoad.opcode==Opcode.LOAD && valueLoad.reg1==targetRegister) {
val storem = IRInstruction(Opcode.STOREM, lastInstruction.type, reg1=valueLoad.reg1, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
chunk.instructions[idx-1] = storem
chunk.instructions.removeAt(idx+1)
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
}
}
return changed
}
}
@@ -122,7 +122,6 @@ private fun convert(variable: StMemVar): IRStMemVar {
private fun convert(constant: StConstant): IRStConstant {
val dt = DataType.forDt(constant.dt)
val scopedName = if('.' in constant.name) {
constant.name
} else {
@@ -132,7 +131,7 @@ private fun convert(constant: StConstant): IRStConstant {
constant.name
}
}
return IRStConstant(scopedName, dt, constant.value)
return IRStConstant(scopedName, constant.dt, constant.value)
}
@@ -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
}
}
@@ -81,7 +81,7 @@ class UnusedCodeRemover(private val program: Program,
private fun reportUnreachable(stmt: Statement) {
when(val next = stmt.nextSibling()) {
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is Break, is Jump -> {}
else -> errors.warn("unreachable code", next.position)
}
}
+59 -1
View File
@@ -70,7 +70,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
+11 -1
View File
@@ -358,7 +358,7 @@ extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without r
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
extsub $FF7D = PRIMM() ; print immediate string
extsub $FF7D = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; ---- C128 specific system utility routines: ----
@@ -1140,6 +1140,16 @@ cx16 {
&word r14s = $1bfc
&word r15s = $1bfe
; signed long versions
&long r0r1sl = $1be0
&long r2r3sl = $1be4
&long r4r5sl = $1be8
&long r6r7sl = $1bec
&long r8r9sl = $1bf0
&long r10r11sl = $1bf4
&long r12r13sl = $1bf8
&long r14r15sl = $1bfc
; ubyte versions (low and high bytes)
&ubyte r0L = $1be0
&ubyte r1L = $1be2
+12
View File
@@ -406,6 +406,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
jsr cbm.SCREEN
inx
txa
rts
}}
@@ -415,11 +416,22 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
jsr cbm.SCREEN
iny
tya
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr cbm.SCREEN
inx
iny
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+71 -1
View File
@@ -2,7 +2,8 @@
FL_ONE_const .byte 129 ; 1.0
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
; note: don't add too many constants here because they all end up in the resulting program
.section BSS
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
@@ -126,6 +127,75 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
.pend
cast_from_long .proc
; convert long pointed to by AY into a float pointed to by R0
; a bit slow algorithm implemented below: (msw(l) as word) as float * 65536.0 + (lsw(l) as float)
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #3
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVAYFAY
lda #<FL_65536_const
ldy #>FL_65536_const
jsr CONUPK
jsr FMULTT
jsr pushFAC1
ldy #1
lda (P8ZP_SCRATCH_W1),y
sta P8ZP_SCRATCH_REG
dey
lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jsr MOVEF
clc
jsr popFAC
jsr FADDT
ldx cx16.r0L
ldy cx16.r0H
jmp MOVMF
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
; !notreached!
.pend
cast_as_long .proc
; convert float pointed to by R0 into a long pointed to by AY
FACHO = FAC_ADDR + 1
; save the target variable pointer on the stack
pha
tya
pha
lda cx16.r0L
ldy cx16.r0H
jsr MOVFM
jsr QINT
; restore the target variable pointer from the stack, and put the result in it
pla
sta P8ZP_SCRATCH_PTR+1
pla
sta P8ZP_SCRATCH_PTR
ldx #3
ldy #0
- lda FACHO,x
sta (P8ZP_SCRATCH_PTR),y
iny
dex
bpl -
rts
.pend
copy_float .proc
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
+1
View File
@@ -11,6 +11,7 @@ floats {
; note: the fac1 and fac2 are working registers and take 6 bytes each,
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
const uword FAC_ADDR = $61
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
+4 -1
View File
@@ -2,8 +2,11 @@
func_sign_f_into_A .proc
; sign in A, also sets status flags
jsr MOVFM
jmp SIGN
jsr SIGN
cmp #0
rts
.pend
+14
View File
@@ -1150,6 +1150,16 @@ cx16 {
&word r14s = $cffc
&word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r9sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0
&ubyte r1L = $cfe2
@@ -1313,6 +1323,8 @@ asmsub init_system() {
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
lda #$80
sta 650 ; enable key repeat for normal keys too
lda #PROG8_C64_BANK_CONFIG ; apply bank config
sta $01
and #1
@@ -1349,6 +1361,8 @@ asmsub cleanup_at_exit() {
jsr cbm.MEMTOP ; adjust MEMTOP down again
jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch
lda #0
sta 650 ; disable keyrepeats
lda _exitcarry
lsr a
lda _exitcode
+8
View File
@@ -424,6 +424,14 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+79
View File
@@ -572,6 +572,85 @@ _try_iso
}}
}
asmsub hex2long(str string @AY) -> long @R0R1_32 {
; -- hexadecimal string (with or without '$') to long.
; string may be in petscii or c64-screencode encoding.
; stops parsing at the first character that's not a hex digit (except leading $)
; result in R0:R1, number of characters processed also remains in cx16.r15L if you want to use it!! (0 = error)
%asm {{
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
sty cx16.r0
sty cx16.r0+1
sty cx16.r0+2
sty cx16.r0+3
sty cx16.r15H
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #'$'
bne _loop
iny
_loop
lda #0
sta P8ZP_SCRATCH_B1
lda (P8ZP_SCRATCH_W2),y
beq _stop
cmp #7 ; screencode letters A-F are 1-6
bcc _add_letter
and #127
cmp #97
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
cmp #'g'
bcs _stop
cmp #'a'
bcs _add_letter
cmp #'0'
bcc _stop
cmp #'9'+1
bcs _stop
_calc
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
asl cx16.r0
rol cx16.r0+1
rol cx16.r0+2
rol cx16.r0+3
and #$0f
clc
adc P8ZP_SCRATCH_B1
ora cx16.r0
sta cx16.r0
iny
bne _loop
_stop
sty cx16.r15L
rts
_add_letter
pha
lda #9
sta P8ZP_SCRATCH_B1
pla
jmp _calc
_try_iso
cmp #103
bcs _stop
and #63
bne _add_letter
; !notreached!
}}
}
asmsub bin2uword(str string @AY) -> uword @AY {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
+61 -3
View File
@@ -68,7 +68,65 @@ smallringbuffer {
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
smallstack {
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
ubyte[256] buffer
ubyte sp = 255
sub init() {
sp = 255
}
sub size() -> ubyte {
return 255-sp
}
sub free() -> ubyte {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==255
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer[sp] = lsb(value)
sp--
buffer[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer[sp]
sp++
cx16.r0L = buffer[sp]
return cx16.r0
}
}
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
@@ -78,7 +136,7 @@ stack {
ubyte bank
sub init(ubyte rambank) {
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the stack, must be called before use. Supply the HIRAM bank to use as buffer space.
sp = $bfff
bank = rambank
}
@@ -157,7 +215,7 @@ ringbuffer {
ubyte bank = 255 ; set via init()
sub init(ubyte rambank) {
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
; -- initialize the ringbuffer, must be called before use. Supply the HIRAM bank to use as buffer space.
head = $a000
tail = $bfff
fill = 0
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
%import shared_compression
+69 -59
View File
@@ -244,6 +244,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=d")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -252,6 +253,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -566,7 +568,7 @@ return_status:
sub internal_f_open_w(str filename, bool open_for_seeks) -> bool {
f_close_w()
list_filename = filename
void strings.copy(filename, list_filename)
str modifier = ",s,?"
modifier[3] = 'w'
if open_for_seeks
@@ -624,7 +626,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
@@ -818,8 +820,13 @@ io_error:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> long {
&long banksize = &cx16.r12
banksize = cx16.getrambank() - startbank
banksize <<= 13 ; * 8192
banksize += endaddress
banksize -= startaddress
return banksize
}
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
@@ -994,15 +1001,12 @@ io_error:
return false
}
sub f_seek(uword pos_hiword, uword pos_loword) {
sub f_seek(long position) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
pokel(&command+2, position)
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1011,13 +1015,10 @@ io_error:
}
sub f_seek_w(uword pos_hiword, uword pos_loword) {
sub f_seek_w(long position) {
; -- seek in the output file opened with f_open_w_seek, to the given 32-bits position
diskio.f_seek.command[1] = WRITE_IO_CHANNEL ; f_open_w uses this secondary address
diskio.f_seek.command[2] = lsb(pos_loword)
diskio.f_seek.command[3] = msb(pos_loword)
diskio.f_seek.command[4] = lsb(pos_hiword)
diskio.f_seek.command[5] = msb(pos_hiword)
pokel(&diskio.f_seek.command+2, position)
cbm.SETNAM(sizeof(diskio.f_seek.command), &diskio.f_seek.command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
@@ -1025,54 +1026,63 @@ io_error:
reset_write_channel() ; back to the write io channel
}
asmsub f_tell() -> uword @R0, uword @R1, uword @R2, uword @R3 {
; -- Returns the current read position of the opened read file,
; in R0 and R1 (low + high words) and the file size in R2 and R3 (low + high words).
; Returns 0 as size if the command is not supported by the DOS implementation/version.
%asm {{
jmp internal_f_tell
}}
}
sub internal_f_tell() {
; gets the (32 bits) position + file size of the opened read file channel
ubyte[2] command = ['t',0]
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
sub f_tell() -> long, long {
; -- Returns the current read position of the opened read file, and the file size.
; Returns 0,0 if the command is not supported by the DOS implementation/version.
ubyte[3] command = ['?','?',0]
bool success=false
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
cx16.r1 = read4hex()
cx16.r0 = read4hex() ; position in R1:R0
void cbm.CHRIN() ; separator space
cx16.r3 = read4hex()
cx16.r2 = read4hex() ; filesize in R3:R2
success = true
long filesize, filepos
ubyte commandoffset
command[1] = 't'
commandoffset = 1
f_tell_internal()
return filepos, filesize
sub f_tell_internal() {
; this code is used for both the T (tell) and FL (fatlba) commands
command[2] = READ_IO_CHANNEL ; f_open uses this secondary address
cbm.SETNAM(sizeof(command)-commandoffset, &command+commandoffset)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
void cbm.CHKIN(15) ; use #15 as input channel
; valid response starts with "07," followed by hex notations of the position and filesize
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
filepos = read8hex()
void cbm.CHRIN() ; separator space
filesize = read8hex()
success = true
} else {
filesize = filepos = 0
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
}
while cbm.READST()==0 {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
}
cbm.CLOSE(15)
reset_read_channel() ; back to the read io channel
if success
return
cx16.r0 = cx16.r1 = cx16.r2 = cx16.r3 = 0
}
sub read4hex() -> uword {
str hex = "0000"
hex[0] = cbm.CHRIN()
hex[1] = cbm.CHRIN()
hex[2] = cbm.CHRIN()
hex[3] = cbm.CHRIN()
return conv.hex2uword(hex)
sub f_fatlba() -> long, long {
; -- Return the current LBA, cluster number (sector index and shift value are ignored for now)
diskio.f_tell.command[0] = 'f'
diskio.f_tell.command[1] = 'l'
diskio.f_tell.commandoffset = 0
diskio.f_tell.f_tell_internal()
return diskio.f_tell.filepos, diskio.f_tell.filesize
}
sub read8hex() -> long {
str hex = "00000000"
for cx16.r0L in 0 to 7 {
hex[cx16.r0L] = cbm.CHRIN()
}
return conv.hex2long(hex)
}
}
+2
View File
@@ -14,6 +14,8 @@ floats {
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
const uword FAC_ADDR = $c3
extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY)
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
+68 -19
View File
@@ -217,6 +217,16 @@ cx16 {
&word r14s = $001e
&word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r9sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes)
&ubyte r0L = $0002
&ubyte r1L = $0004
@@ -463,7 +473,7 @@ extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobb
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
extsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) ; note: The the zero page address containing the base address is passed in stavec ($03B2)
extsub $ff7d = PRIMM()
extsub $ff7d = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
; high level graphics & fonts
extsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
@@ -620,6 +630,10 @@ const ubyte EXTAPI_scnsiz = $0D
const ubyte EXTAPI_kbd_leds = $0E
const ubyte EXTAPI_memory_decompress_from_func = $0F
const ubyte EXTAPI_default_palette = $10
const ubyte EXTAPI_has_machine_property = $11
const ubyte EXTAPI_kbdbuf_get = $12
const ubyte EXTAPI_kbdbuf_clear = $13
const ubyte EXTAPI_blink_enable = $14
; extapi16 call numbers
const ubyte EXTAPI16_test = $00
@@ -676,6 +690,30 @@ sub mouse_present() -> bool {
return cx16.r0L != $fc ; $fc = BAT_FAIL
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
%asm {{
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
}}
}
; shims for the kernal routines called via the extapi call:
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
@@ -758,31 +796,42 @@ asmsub set_default_palette() {
}}
}
asmsub get_charset() -> ubyte @A {
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
asmsub has_machine_property(ubyte property @X) clobbers(A,X) -> bool @Pc {
; requires rom v49+
%asm {{
lda #EXTAPI_has_machine_property
jmp cx16.extapi
}}
}
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
lda KERNAL_MODE
beq _end
asmsub kbdbuf_get() clobbers(X,Y) -> ubyte @A {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_get
jmp cx16.extapi
}}
}
bit #$40 ;ISO mode flag
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
lda #1
bra _end
asmsub kbdbuf_clear() clobbers(A) {
; requires rom v49+
%asm {{
lda #EXTAPI_kbdbuf_clear
jmp cx16.extapi
}}
}
+ bit #1 ;PETSCII upper case/graphics
bne + ;usually KERNAL_MODE 2 or 4
lda #2
bra _end
+ lda #3 ;PETSCII upper/lower case
_end:
rts
asmsub blink_enable(bool enable @X) clobbers(A,X) {
; requires rom v49+
%asm {{
txa
and #1
eor #1
tax
lda #EXTAPI_blink_enable
jmp cx16.extapi
}}
}
; TODO : implement shims for the remaining extapi calls.
; ---- end of kernal routines ----
+7
View File
@@ -615,6 +615,13 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+10
View File
@@ -605,6 +605,16 @@ cx16 {
&word r14s = $7ffc
&word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r9sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0
&ubyte r1L = $7fe2
+13 -2
View File
@@ -182,8 +182,8 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
}
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
; ---- set char at the given position on the screen. charcolor is ignored on PET
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char at the given position on the screen. NOTE: charcolor is ignored on PET
%asm {{
lda row
asl a
@@ -236,6 +236,17 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr width
tax
jsr height
tay
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+47
View File
@@ -266,6 +266,25 @@ strcpy .proc
rts
.pend
strncpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; with maximum length to copy in X.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #$ff
- iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
beq +
dex
bne -
iny
lda #0
sta (P8ZP_SCRATCH_W1),y
+ rts
.pend
strcmp_expression .proc
; -- compare strings, result in A
lda _arg_s2
@@ -641,3 +660,31 @@ long_shiftrightX_inplace .proc
bne -
_end rts
.pend
compare_long_0_and_sign .proc
; pointer to 32 bits signed long in P8ZP_SCRATCH_W1, carry Set=check NEGATIVE (<=0), carry Clear=check POSITIVE (>=0)
; returns carry Set if comparison succeeded otherwise carry Clear
ldy #0
lda (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
beq _success
lda (P8ZP_SCRATCH_W1),y
bcc _checkpositive
bmi _success
bpl _fail
_checkpositive bpl _success
_fail clc
rts
_success sec
rts
.pend
+3 -1
View File
@@ -222,6 +222,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=c") ; on C64 (1581 diskdrive) the type for directories is CBM
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -230,6 +231,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -550,7 +552,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
+1 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
; This file contains the shared routines that work on all targets.
compression {
@@ -160,6 +160,9 @@ sub log2(float value) -> float {
ldy #>FL_LOG2_const
jsr MOVFM
jmp FDIVT
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
; !notreached!
}}
}
+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.
+20
View File
@@ -279,6 +279,26 @@ sub hex2uword(str string) -> uword {
}
}
sub hex2long(str string) -> long {
; -- hexadecimal string (with or without '$') to long.
; stops parsing at the first character that's not a hex digit (except leading $)
long result
ubyte char
if @(string)=='$'
string++
repeat {
char = @(string)
if char==0
return result
result <<= 4
if char>='0' and char<='9'
result |= char-'0'
else
result |= char-'a'+10
string++
}
}
sub bin2uword(str string) -> uword {
; -- binary string (with or without '%') to uword.
; stops parsing at the first character that's not a 0 or 1. (except leading %)
+3 -3
View File
@@ -206,7 +206,7 @@ diskio {
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,0
load.b r99100,#0
loadm.w r99000,diskio.save.filenameptr
loadm.w r99001,diskio.save.start_address
loadm.w r99002,diskio.save.savesize
@@ -218,7 +218,7 @@ diskio {
; like save() but omits the 2 byte prg header.
sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,1
load.b r99100,#1
loadm.w r99000,diskio.save_raw.filenameptr
loadm.w r99001,diskio.save_raw.start_address
loadm.w r99002,diskio.save_raw.savesize
@@ -284,7 +284,7 @@ diskio {
; get the load adress from a PRG file (usually $0801 but it can be different)
if f_open(filename) {
uword address
f_read(&address, 2)
void f_read(&address, 2)
f_close()
return address
}
+24 -3
View File
@@ -106,12 +106,23 @@ strings {
sub copy(str source, str target) -> ubyte {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you don’t have to call this explicitly and can just write string1 = string2
; but this function is useful if you’re dealing with addresses for instance.
%ir {{
loadm.w r99000,strings.copy.source
loadm.w r99001,strings.copy.target
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
sub ncopy(str source, str target, ubyte maxlength) -> ubyte {
; Copy a string to another, overwriting that one, but limited to the given length.
; Returns the length of the string that was copied.
%ir {{
loadm.w r99000,strings.ncopy.source
loadm.w r99001,strings.ncopy.target
loadm.b r99100,strings.ncopy.maxlength
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
@@ -123,6 +134,16 @@ strings {
return copy(suffix, target+cx16.r0L) + cx16.r0L
}
sub nappend(str target, str suffix, ubyte maxlength) -> ubyte {
; Append the suffix string to the target, up to the given maximum length of the combined string.
; Returns the length of the resulting string.
cx16.r0L = length(target)
if maxlength<cx16.r0L
return cx16.r0L
maxlength -= cx16.r0L
return ncopy(suffix, target+cx16.r0L, maxlength) + cx16.r0L
}
sub compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
+12 -1
View File
@@ -53,7 +53,8 @@ sys {
%ir {{
loadm.w r99000,sys.internal_stringcopy.source
loadm.w r99001,sys.internal_stringcopy.tgt
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
}}
}
@@ -306,6 +307,16 @@ cx16 {
&word r14s = $ff1e
&word r15s = $ff20
; signed long versions
&long r0r1sl = $ff02
&long r2r3sl = $ff06
&long r4r5sl = $ff0a
&long r6r7sl = $ff0e
&long r8r9sl = $ff12
&long r10r11sl = $ff16
&long r12r13sl = $ff1a
&long r14r15sl = $ff1e
; ubyte versions (low and high bytes)
&ubyte r0L = $ff02
&ubyte r1L = $ff04
+6 -1
View File
@@ -21,6 +21,11 @@ sub height() -> ubyte {
}}
}
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
}}
+3 -3
View File
@@ -60,13 +60,13 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
@@ -145,7 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
System.err.println("On the Commander X16, HIRAM bank 0 is used by the kernal and can't be used.")
return false
}
@@ -20,7 +20,19 @@ 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() } },
@@ -71,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 {
@@ -84,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 {
@@ -138,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 {
@@ -50,8 +50,8 @@ class ModuleImporter(private val program: Program,
fun importImplicitLibraryModule(name: String): Module? {
val import = Directive("%import", listOf(
DirectiveArg(name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
), Position("<<<implicit-import>>>", 0, 0, 0))
DirectiveArg(name, 42u, position = Position("~implicit-import~", 0, 0, 0))
), Position("~implicit-import~", 0, 0, 0))
return executeImportDirective(import, null)
}
@@ -497,8 +497,8 @@ internal class AstChecker(private val program: Program,
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
}
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && param.first.type!=DataType.pointer(BaseDataType.UBYTE)) {
err("parameter '${param.first.name}' should be (u)word, str or ^^ubyte")
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && !param.first.type.isPointer) {
err("parameter '${param.first.name}' should be (u)word, str or a pointer")
}
}
else if(param.second.statusflag!=null) {
@@ -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?
@@ -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
}
}
@@ -1950,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)
}
@@ -2081,7 +2093,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memread: DirectMemoryRead) {
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
if(memread.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memread.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
val pointervar = memread.addressExpression as? IdentifierReference
@@ -2114,7 +2128,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
if(memwrite.addressExpression.inferType(program).isLong)
errors.err("long address not yet supported", memwrite.addressExpression.position)
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
val pointervar = memwrite.addressExpression as? IdentifierReference
@@ -2359,7 +2375,7 @@ internal class AstChecker(private val program: Program,
if(value.type==BaseDataType.FLOAT)
err("integer value expected instead of float; possible loss of precision")
val number=value.number
if (number < -2147483647.0 || number > 2147483647.0)
if (number < -2147483648.0 || number > 2147483647.0)
return err("value '$number' out of range for long")
}
targetDt.isArray -> {
@@ -34,8 +34,14 @@ internal fun Program.reorderStatements(options: CompilationOptions, errors: IErr
internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && changer.applyModifications() > 0)
changer.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("changeNotExpressionAndIfComparisonExpr() is looping endlessly")
}
}
@@ -93,8 +99,15 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
val desugar = CodeDesugarer(this, options.compTarget, errors)
desugar.visit(this)
while(errors.noErrors() && desugar.applyModifications()>0)
desugar.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && desugar.applyModifications() > 0)
desugar.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("desugaring() is looping endlessly")
}
}
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
@@ -121,16 +134,30 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0)
lit2decl.visit(this)
for(numCycles in 0..1000) {
if(errors.noErrors() && lit2decl.applyModifications() > 0)
lit2decl.visit(this)
else
break
if(numCycles==1000) {
throw InternalCompilerException("checkIdentifiers() is looping endlessly - check for circular aliases")
}
}
}
}
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options)
process.visit(this)
while(errors.noErrors() && process.applyModifications()>0) {
process.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && process.applyModifications() > 0)
process.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("variousCleanups() is looping endlessly")
}
}
@@ -44,6 +44,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
if(alias.alias == alias.target.nameInSource.first())
errors.err("alias references itself", alias.position)
}
override fun visit(block: Block) {
@@ -96,6 +99,50 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(decl)
}
private val keywords = setOf(
"void",
"on",
"in",
"to",
"if",
"downto",
"else",
"step",
"call",
"goto",
"inline",
"struct",
"then",
"alias",
"defer",
"const",
"sizeof",
"as",
"return",
"break",
"continue",
"true",
"false",
"sub",
"asmsub",
"extsub",
"clobbers",
"while",
"do",
"until",
"repeat",
"unroll",
"when"
)
override fun visit(struct: StructDecl) {
if(struct.name in BuiltinFunctions)
errors.err("builtin function cannot be redefined", struct.position)
else if(struct.name in keywords)
errors.err("struct name cannot be a keyword", struct.position)
super.visit(struct)
}
override fun visit(subroutine: Subroutine) {
if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
@@ -69,6 +69,13 @@ class AstPreprocessor(val program: Program,
return super.before(string, parent)
}
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
if (parent is RangeExpression)
return noModifications
val constValue = expr.constValue(program) ?: return noModifications
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
}
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
val modifications = mutableListOf<IAstModification>()
@@ -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)
}
}
@@ -621,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)
)
}
@@ -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 }
@@ -132,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 if(leftDt.isWords) 65536+rightCv.number else 0xffffffffL+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),
@@ -339,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memread.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -354,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
if(dt.isLong) {
val constAddr = memwrite.addressExpression.constValue(program)?.number
if(constAddr==null || constAddr>=65536)
return noModifications
}
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
return noModifications
@@ -27,45 +27,46 @@ 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
@@ -431,6 +432,16 @@ 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 ${arrayIndexedExpression.position}")
@@ -469,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
if(addressOf.arrayIndex!=null) {
val tgt = addressOf.identifier?.constValue(program)
if (tgt != null && tgt.type.isWord) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(
IAstModification.ReplaceNode(addressOf, add, parent)
)
val tgt = addressOf.identifier
if(tgt!=null) {
val constAddress = tgt.constValue(program)
if (constAddress != null && constAddress.type.isInteger) {
// &constant[idx] --> constant + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
indexExpr
else
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
} else {
val decl = tgt.targetVarDecl()
if(decl!=null && decl.datatype.isInteger) {
// &addressvar[idx] --> addressvar + idx
val indexExpr = addressOf.arrayIndex!!.indexExpr
val right = if (indexExpr.inferType(program) istype decl.datatype)
indexExpr
else
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
val add = BinaryExpression(tgt, "+", right, addressOf.position)
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
}
}
}
}
return noModifications
+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 }
+6 -6
View File
@@ -38,9 +38,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharLitAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
chrout('\n')
}
@@ -61,9 +61,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharVarAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
ubyte ch = '\n'
chrout(ch)
@@ -96,9 +96,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharConstAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
const ubyte ch = '\n'
chrout(ch)
-1
View File
@@ -203,7 +203,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
val bothCx16AndC64 = cartesianProduct(
listOf(
"animals",
"balls",
"cube3d",
"cube3d-float",
+21 -4
View File
@@ -16,10 +16,7 @@ import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.C128Target
import prog8.code.target.C64Target
import prog8.code.target.PETTarget
import prog8.code.target.VMTarget
import prog8.code.target.*
import prog8tests.helpers.*
@@ -438,4 +435,24 @@ main {
errors.errors[0] shouldContain "8:9: cannot assign to an array or string that is located in ROM"
errors.errors[1] shouldContain "9:9: cannot assign to an array or string that is located in ROM"
}
test("long addresses not yet supported in memread and memwrite") {
val src = """
main {
sub start() {
const long bufferll = 888888
long @shared addrlv = 777777
cx16.r0L = bufferll[2]
cx16.r1L = @(999999)
cx16.r2L = @(addrlv)
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "long address not yet supported"
errors.errors[1] shouldContain "long address not yet supported"
errors.errors[2] shouldContain "long address not yet supported"
}
})
+34
View File
@@ -7,10 +7,13 @@ import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtNumber
import prog8.code.core.InternalCompilerException
import prog8.code.core.toHex
import prog8.code.target.C64Target
import prog8.code.target.Mflpt5
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
@@ -225,4 +228,35 @@ class TestNumbers: FunSpec({
errors.errors[4] shouldContain "out of range"
errors.errors[5] shouldContain "byte doesn't match"
}
test("biggest long numbers") {
val src= $$"""
main {
sub start() {
long @shared l1, l2
l1 = $7fffffff
l2 = $80000000
l1 = -2147483648
l2 = -2147483649 ; will be truncated
l1 = $abcd1234
l2 = -$7fffffff
l1 = $80000001
l2 = -$80
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir)!!
val st = result.codegenAst!!.entrypoint()!!
st.children.size shouldBe 13
((st.children[4] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[5] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[6] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[7] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[8] as PtAssignment).value as PtNumber).number shouldBe -1412623820
((st.children[9] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[10] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[11] as PtAssignment).value as PtNumber).number shouldBe -128
}
})
+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") {
+87 -5
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") {
@@ -2548,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))
+1 -1
View File
@@ -598,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:"
+33
View File
@@ -489,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"
}
})
+120 -37
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
@@ -305,30 +306,38 @@ main {
(waDeclV as NumericLiteral).number shouldBe 40960.0
}
test("address of a const uword pointer array expression") {
val src= """
test("address of a const integer pointer array expression") {
val src = """
main {
sub start() {
const uword buffer = 2000
const long bufferl = 999999
uword @shared addr = &buffer[2]
const ubyte width = 100
ubyte @shared i
ubyte @shared j
uword @shared addr2 = &buffer[i * width + j]
long @shared addr2 = &bufferl[2]
const uword width = 100
uword @shared addr3 = &buffer[width]
long @shared addr4 = &bufferl[width]
}
}"""
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = true)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 11
val assignAddr = (st[2] as Assignment).value
(assignAddr as NumericLiteral).number shouldBe 2002.0
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
assignAddr2.operator shouldBe "+"
st.size shouldBe 12
val assignAddr = (st[3] as Assignment).value as NumericLiteral
assignAddr.number shouldBe 2002.0
assignAddr.type shouldBe BaseDataType.UWORD
val assignAddr2 = (st[5] as Assignment).value as NumericLiteral
assignAddr2.number shouldBe 1000001.0
assignAddr2.type shouldBe BaseDataType.LONG
val assignAddr3 = (st[8] as Assignment).value as NumericLiteral
assignAddr3.number shouldBe 2100
assignAddr3.type shouldBe BaseDataType.UWORD
val assignAddr4 = (st[10] as Assignment).value as NumericLiteral
assignAddr4.number shouldBe 1000099.0
assignAddr4.type shouldBe BaseDataType.LONG
}
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 +348,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 +357,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 +368,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 +377,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 +388,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,14 +408,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="""
val src = """
main {
sub start() {
byte @shared a = -1
@@ -455,7 +464,7 @@ main {
}
test("const long with small values") {
val src="""
val src = """
main {
sub start() {
const long notkaputt = 42
@@ -464,4 +473,78 @@ 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()
}
test("test const sizeof type casting") {
val src = """
main {
const uword LineSize = sizeof(uword)
const uword BufferSize = LineSize ; or 400*LineSize
sub start () {
}
}"""
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
test("const struct sizes and offsets, also available in assembly code") {
val src = """
main {
struct Node {
ubyte type
uword value
bool flag
}
sub start() {
const ubyte size = sizeof(Node)
const ubyte offset1 = offsetof(Node.type)
const ubyte offset2 = offsetof(Node.value)
const ubyte offset3 = offsetof(Node.flag)
%asm {{
lda p8c_size
lda p8c_offset1
lda p8c_offset2
lda p8c_offset3
lda #size(p8t_Node)
lda #p8t_Node.p8v_type
lda #p8t_Node.p8v_value
lda #p8t_Node.p8v_flag
}}
}
}"""
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})
+5 -5
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]
@@ -992,7 +992,7 @@ 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()
@@ -1016,7 +1016,7 @@ main {
{
%ir
{{
load.b r0,1
load.b r0,#1
return
}}
}
+115 -7
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:
}
}
@@ -1192,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"
}
})
+15 -21
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") {
@@ -637,4 +637,40 @@ main{
compileText(C64Target(), true, src, outputDir) shouldNotBe null
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
}
test("symbol prefixing inconsistency with struct types and merged blocks") {
// see https://github.com/irmen/prog8/issues/198
val src="""
plane {
struct Point {
ubyte x
ubyte y
}
}
mytxt {
%option no_symbol_prefixing
sub print_ub(ubyte value) {
cx16.r0L++
}
}
mytxt {
%option merge
sub print_pt(^^plane.Point p) {
mytxt.print_ub(p.x)
mytxt.print_ub(p.y)
}
}
main {
sub start() {
^^plane.Point origin = ^^plane.Point:[0,0]
mytxt.print_pt(origin)
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
}
})
+2 -2
View File
@@ -14,7 +14,7 @@ import prog8.code.source.SourceCode
object ParentSentinel : Node {
override val position = Position("<<sentinel>>", 0, 0, 0)
override val position = Position("~sentinel~", 0, 0, 0)
override var parent: Node = this
override fun linkParents(parent: Node) {}
override fun replaceChildNode(node: Node, replacement: Node) {
@@ -394,7 +394,7 @@ open class Module(final override val statements: MutableList<Statement>,
class GlobalNamespace(val modules: MutableList<Module>): Node, INameScope {
override val name = "<<<global>>>"
override val position = Position("<<<global>>>", 0, 0, 0)
override val position = Position("~global~", 0, 0, 0)
override val statements = mutableListOf<Statement>() // not used
override var parent: Node = ParentSentinel
@@ -113,7 +113,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitDirective(ctx: DirectiveContext): Directive {
val pos = ctx.toPosition()
val position = Position(pos.file, pos.line, pos.startCol, ctx.directivename().UNICODEDNAME().symbol.stopIndex)
val end = ctx.directivename().UNICODEDNAME().symbol.stopIndex - ctx.directivename().UNICODEDNAME().symbol.startIndex
val position = Position(pos.file, pos.line, pos.startCol, pos.startCol+end+1)
if(ctx.directivenamelist() != null) {
val namelist = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, it.position) }
@@ -346,7 +347,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
in -128..127 -> BaseDataType.BYTE
in 0..65535 -> BaseDataType.UWORD
in -32768..32767 -> BaseDataType.WORD
in -2147483647..2147483647 -> BaseDataType.LONG
in -2147483648L..0xffffffffL -> BaseDataType.LONG // TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast (max value)
else -> BaseDataType.FLOAT
}
}
@@ -385,6 +386,13 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
else -> throw FatalAstException(terminal.text)
}
// TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
if(integer.second.isLong && integer.first > Integer.MAX_VALUE && integer.first <= 0xffffffff) {
val signedLong = integer.first.toLong().toInt()
return NumericLiteral(integer.second, signedLong.toDouble(), ctx.toPosition())
}
return NumericLiteral(integer.second, integer.first, ctx.toPosition())
}
@@ -498,7 +506,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
if(statusregister!=null) {
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine, pctx.register.charPositionInLine+1))
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine+1, pctx.register.charPositionInLine+1))
}
return SubroutineParameter(identifiername, datatype, zp, registerorpair, pctx.toPosition())
}
@@ -706,7 +714,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
pathString
}
// note: beware of TAB characters in the source text, they count as 1 column...
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
val endOffset = if(start.startIndex<0 || start.stopIndex<0) 0 else start.stopIndex - start.startIndex
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + endOffset)
}
private fun getZpOption(tags: List<String>): ZeropageWish = when {
@@ -747,7 +756,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> {
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine+1, registerTok.charPositionInLine+1))
}
}
}
@@ -572,14 +572,15 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
val index = arrayIndex?.constIndex()
if (index != null) {
address += when {
target.datatype.isUnsignedWord -> index
target.datatype.isInteger -> index
target.datatype.isArray -> program.memsizer.memorySize(targetVar.datatype, index)
else -> throw FatalAstException("need array or uword ptr")
else -> throw FatalAstException("need array or ptr")
}
} else
return null
}
return NumericLiteral(BaseDataType.UWORD, address, position)
val addressType = if(targetVar.datatype.isLong) BaseDataType.LONG else BaseDataType.UWORD
return NumericLiteral(addressType, address, position)
}
}
}
@@ -686,8 +687,12 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
in -128..127 -> NumericLiteral(BaseDataType.BYTE, dvalue, position)
in 0..65535 -> NumericLiteral(BaseDataType.UWORD, dvalue, position)
in -32768..32767 -> NumericLiteral(BaseDataType.WORD, dvalue, position)
in -2147483647..2147483647 -> NumericLiteral(BaseDataType.LONG, dvalue, position)
else -> NumericLiteral(BaseDataType.FLOAT, dvalue, position)
else -> {
if(dvalue in -2147483648.0 .. 2147483647.0)
NumericLiteral(BaseDataType.LONG, dvalue, position)
else
NumericLiteral(BaseDataType.FLOAT, dvalue, position)
}
}
}
}
@@ -946,7 +951,8 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
throw FatalAstException("type cast of weird type $type")
}
}
return ValueAfterCast(false, "no cast available from $type to $targettype number=$number", null)
val implicit = if(implicit) "implicit " else ""
return ValueAfterCast(false, "no ${implicit}cast available from $type to $targettype for value $number", null)
}
fun convertTypeKeepValue(targetDt: BaseDataType): ValueAfterCast {
@@ -1415,14 +1421,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
// the value of a variable can (temporarily) be a different type as the vardecl itself.
// don't return the value if the types don't match yet!
val value = vardecl.value?.constValue(program)
if(value==null || value.type==vardecl.datatype.base)
return value
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
if(optimal.type==vardecl.datatype.base)
return optimal
return null
val casted = value.cast(vardecl.datatype.base, true)
return if(casted.isValid)
casted.valueOrZero()
else
null
}
override fun toString(): String {
@@ -1536,6 +1542,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
return null
val resultValue: NumericLiteral? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
resultValue?.parent=this.parent
if(withDatatypeCheck) {
val resultDt = this.inferType(program)
if(resultValue==null || resultDt istype DataType.forDt(resultValue.type))
@@ -1765,6 +1772,8 @@ class PtrDereference(
return resultType(target.datatype)
if(target is StructFieldRef)
return resultType(target.type)
if(target is Alias)
return target.target.inferType(program)
TODO("infertype $chain -> $target")
}
@@ -1845,7 +1854,9 @@ class ArrayIndexedPtrDereference(
InferredTypes.knownFor(fieldDt)
}
} else {
require(indexPosition==this.chain.size-1) {"when indexing primitive pointer type there cannot be a field after it"}
require(indexPosition==this.chain.size-1) {
"when indexing primitive pointer type there cannot be a field after it $position"
}
return if(derefLast)
InferredTypes.knownFor(target.datatype.dereference())
else
@@ -1030,9 +1030,9 @@ class Subroutine(override val name: String,
replacement.parent = this
}
is NumericLiteral -> {
if(node===asmAddress?.address) {
if(node===asmAddress?.address || node==asmAddress?.address) {
asmAddress.address = replacement
} else if(node===asmAddress?.varbank) {
} else if(node==asmAddress?.varbank) {
asmAddress.constbank = replacement.number.toInt().toUByte()
asmAddress.varbank = null
} else throw FatalAstException("can't replace")
Loaded 100 of 154 files, more files were not shown because too many files have changed in this diff. Show more