mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
144
Commits
v12.0-beta4
...
v12.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4030a1b9e5 | ||
|
|
0217d05c68 | ||
|
|
8553f3c9f1 | ||
|
|
d157d03ea0 | ||
|
|
1799b5f00c | ||
|
|
2ddb5e11b7 | ||
|
|
3aa8b9fd12 | ||
|
|
51300196e8 | ||
|
|
f7b2f19cba | ||
|
|
014a82a1ee | ||
|
|
745cf3d958 | ||
|
|
c4aa681d58 | ||
|
|
feb0774bbd | ||
|
|
2ccfb2ac90 | ||
|
|
2191c4b7d8 | ||
|
|
39c1cf0c3c | ||
|
|
c693be0dd6 | ||
|
|
0f433055a7 | ||
|
|
76c95ba6fa | ||
|
|
bd272a8567 | ||
|
|
b38fff76e9 | ||
|
|
07b7639ff2 | ||
|
|
6871561c09 | ||
|
|
77a71138ad | ||
|
|
165f94bc93 | ||
|
|
b75090f928 | ||
|
|
62b0c82f93 | ||
|
|
b1ef863c7f | ||
|
|
e78345410f | ||
|
|
8b86f97aaa | ||
|
|
93135774e6 | ||
|
|
a64f27c6b0 | ||
|
|
69ef63c96d | ||
|
|
df1a2a1611 | ||
|
|
d19a3af9ed | ||
|
|
352c11ad9f | ||
|
|
9504711fc7 | ||
|
|
590feda903 | ||
|
|
6e7e2922bf | ||
|
|
01df1f0083 | ||
|
|
397299bd1d | ||
|
|
c275aacd38 | ||
|
|
6a6e18773e | ||
|
|
99e037489b | ||
|
|
fb5290e17b | ||
|
|
818774ab84 | ||
|
|
d667312acc | ||
|
|
64d0cd87a8 | ||
|
|
2fffbdde89 | ||
|
|
dc8ea31c49 | ||
|
|
f4ead66e91 | ||
|
|
314e7f5691 | ||
|
|
da31465b7f | ||
|
|
397a907088 | ||
|
|
50c6962e1f | ||
|
|
b7d1fb1342 | ||
|
|
afb458a7da | ||
|
|
da3c7f267f | ||
|
|
b7ba7c50b1 | ||
|
|
bdbfe7048b | ||
|
|
79039b66d4 | ||
|
|
833e463525 | ||
|
|
6611e4e092 | ||
|
|
1f31cb18e4 | ||
|
|
bc8ba252a5 | ||
|
|
c353dd40bf | ||
|
|
928ef6bbaa | ||
|
|
45dde856c6 | ||
|
|
e1ccef4e89 | ||
|
|
4d3f0ec223 | ||
|
|
b73c958c4a | ||
|
|
2eac457e1c | ||
|
|
815ef7e654 | ||
|
|
0cd8c4f87e | ||
|
|
b02a8ed954 | ||
|
|
b1e07f3fdb | ||
|
|
fc8727f81e | ||
|
|
ec728bad52 | ||
|
|
a5e827e40f | ||
|
|
34061c5a63 | ||
|
|
9c2bcab4a5 | ||
|
|
2f07f41664 | ||
|
|
7027304597 | ||
|
|
ee75333891 | ||
|
|
6a70fb0480 | ||
|
|
ebc738b132 | ||
|
|
e5939be0bd | ||
|
|
0c0affd1bf | ||
|
|
c7158fd968 | ||
|
|
de2f3f0d0d | ||
|
|
c0286e3349 | ||
|
|
513ee25432 | ||
|
|
be74290ddc | ||
|
|
a36501a9ed | ||
|
|
ee5d33a230 | ||
|
|
a8fd66cfdb | ||
|
|
f5ce744748 | ||
|
|
68066acdec | ||
|
|
6286035d89 | ||
|
|
9a37eb9146 | ||
|
|
307796f115 | ||
|
|
e962431139 | ||
|
|
b7620abc9a | ||
|
|
d2719d23f5 | ||
|
|
d69eead6d8 | ||
|
|
6db3611d93 | ||
|
|
a84320c7e2 | ||
|
|
dfc720557c | ||
|
|
32149d073a | ||
|
|
fc38be6376 | ||
|
|
13f6efc1d1 | ||
|
|
8296002887 | ||
|
|
424b89f357 | ||
|
|
4f5590fbff | ||
|
|
598e70c49a | ||
|
|
a6d051b496 | ||
|
|
796d3242e1 | ||
|
|
1e8ff6f82a | ||
|
|
07bb5c36bd | ||
|
|
7e26ecb0b6 | ||
|
|
0c59ad70d4 | ||
|
|
53ce688cc5 | ||
|
|
bb8b44d9d0 | ||
|
|
51ae32d23e | ||
|
|
c873fac0dc | ||
|
|
f0a67fff8a | ||
|
|
f85ccd837d | ||
|
|
396fcbc927 | ||
|
|
0f564b301d | ||
|
|
f4f34fc2ed | ||
|
|
f7639cb78f | ||
|
|
f5f5aef722 | ||
|
|
d6e30d8468 | ||
|
|
37afdf5a18 | ||
|
|
5eb7074172 | ||
|
|
9aff280d10 | ||
|
|
44741a8e32 | ||
|
|
37535f2913 | ||
|
|
ec9475c308 | ||
|
|
b961ce97d6 | ||
|
|
4ed92d71a7 | ||
|
|
3e1386a987 | ||
|
|
4195e3968a | ||
|
|
961095bfec |
No files matched your search
Generated
+10
-10
@@ -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>
|
||||
@@ -34,8 +34,11 @@ How to get it/build it
|
||||
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
|
||||
Note that if you are not using *gradle* to build it, you might have to perform some manual
|
||||
tasks once to make it compile fully. These are explained in the linked instructions.
|
||||
- Alternatively, you can also install the compiler as a package on some linux distros:
|
||||
- Alternatively, you can also install the compiler as a package on some linux distros (which will take care of dependencies automatically):
|
||||
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
|
||||
- Finally there's a Homebrew recipe for Mac OS (but also for Linux, and WSL2 on Windows, this also takes care of dependencies automatically):
|
||||
``brew install prog8``
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
@@ -58,9 +61,9 @@ What does Prog8 provide?
|
||||
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because it's compiled to native machine code
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- compiled code is very small; much smaller than equivalent C code compiled with CC65, and usually runs faster as well
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- various data types other than just bytes (16-bit words, long integers, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
@@ -82,7 +85,7 @@ What does Prog8 provide?
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings
|
||||
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
|
||||
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
|
||||
- 50 Kb of available program RAM size on the C64 by default (41 Kb on the C128) because Basic ROM is banked out by default
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
@@ -193,3 +196,12 @@ For instance here's a well known space ship animated in 3D with hidden line remo
|
||||
in the CommanderX16 emulator:
|
||||
|
||||

|
||||
|
||||
|
||||
Performance comparison with various C compilers
|
||||
-----------------------------------------------
|
||||
|
||||
Here is a performance comparison with various C compilers for the 6502/C64 so you can get
|
||||
an idea of how Prog8 stacks up.
|
||||
|
||||
[comparison](benchmark-c/benchmark.md)
|
||||
@@ -0,0 +1,39 @@
|
||||
# C-Bench-64 comparison
|
||||
|
||||
Several benchmarks of https://thred.github.io/c-bench-64/
|
||||
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
|
||||
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
|
||||
|
||||
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
|
||||
|
||||
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
|
||||
|
||||
* Prog8 program size is consistently one of the smallest.
|
||||
* Prog8 execution speed places it more or less in the middle of the stack.
|
||||
|
||||
Measured with Prog8 V12.0 .
|
||||
Benchmarks ran on a PAL C64 emulated through VICE.
|
||||
|
||||
textual results:
|
||||
|
||||
| benchmark | size | time |
|
||||
|-----------|------|----------|
|
||||
| crc8 | 1568 | 2.0 sec |
|
||||
| crc16 | 1608 | 2.5 sec |
|
||||
| crc32 | 1812 | 4.2 sec |
|
||||
| pow | 2152 | 11.7 sec |
|
||||
| sieve | 1772 | 22.1 sec |
|
||||
| sieve_bit | 1918 | 40.1 sec |
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

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