mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
3ea8ca59b7 | ||
|
|
71ffbe2ba7 | ||
|
|
e63921009c | ||
|
|
db1aa3f257 | ||
|
|
8abdb837b2 | ||
|
|
cdeabd4b66 | ||
|
|
efff74c0f1 | ||
|
|
0e177cb531 | ||
|
|
35b921b062 | ||
|
|
a7d98e43b8 | ||
|
|
845ee2dd83 | ||
|
|
f9b0bfe31b | ||
|
|
f303d15628 | ||
|
|
67f7ffa52d | ||
|
|
88c5d9783a | ||
|
|
e7fc0360ad | ||
|
|
cd1862dd9f | ||
|
|
045c4909a8 | ||
|
|
f1bfe619b2 | ||
|
|
e0107bacbd | ||
|
|
b3bd2a6a09 | ||
|
|
ff1f58e022 | ||
|
|
557b12668d | ||
|
|
b058f1c7c2 | ||
|
|
3e07b6ca70 | ||
|
|
d66dc664de | ||
|
|
a2b9d78cf3 | ||
|
|
44f70da113 | ||
|
|
c87eaf576d | ||
|
|
c09b1395f6 | ||
|
|
d6a8201291 | ||
|
|
4187f97f7a | ||
|
|
9d3b2f12fd | ||
|
|
87c1bbbf40 | ||
|
|
a7ad6abdb9 | ||
|
|
a611406020 | ||
|
|
75da38224d | ||
|
|
8d6f3301c8 | ||
|
|
86b52a1c5e | ||
|
|
2c8b1c2022 | ||
|
|
ab1f065752 | ||
|
|
2c7256a443 | ||
|
|
97420b28e5 | ||
|
|
1467c7039d | ||
|
|
d319badc6c | ||
|
|
65d6c1c438 | ||
|
|
abeefb5655 | ||
|
|
50fecbcebe | ||
|
|
4fe8b72d42 | ||
|
|
f3b060df51 | ||
|
|
09d1cb6925 | ||
|
|
54fa72fa98 | ||
|
|
fd62fe7511 | ||
|
|
bfb34dff62 | ||
|
|
817b623596 | ||
|
|
f6dbeb1f63 | ||
|
|
c4b9bdd33f | ||
|
|
68e0d5f1b5 | ||
|
|
4939e3df55 | ||
|
|
19f19f3880 | ||
|
|
ad0c767ea8 | ||
|
|
ed5f4d5855 | ||
|
|
c2f5d37486 | ||
|
|
231b50dacb | ||
|
|
a71895cbe8 | ||
|
|
a8bede17b2 | ||
|
|
f6c8e693a5 | ||
|
|
9461e4088c | ||
|
|
efd73fd10d | ||
|
|
ddf6e84a1a | ||
|
|
633d6c34e2 | ||
|
|
6e7fbc6683 | ||
|
|
124ea1230b | ||
|
|
8b48a295b6 | ||
|
|
d285d37fdb | ||
|
|
8bb927b483 | ||
|
|
1af4cd0d63 | ||
|
|
db2f28c4cd |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# An .aiignore file follows the same syntax as a .gitignore file.
|
||||
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
||||
|
||||
# you can ignore files
|
||||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
*.bin
|
||||
*.prg
|
||||
|
||||
# or folders
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
output/
|
||||
.gradle/
|
||||
.kotlin/
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
make -j4
|
||||
sudo make install
|
||||
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Build and test with Gradle
|
||||
|
||||
Generated
+1
@@ -15,6 +15,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
|
||||
@@ -193,3 +193,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,26 @@
|
||||
# C-Bench-64 comparison
|
||||
|
||||
Several benchmarks of https://thred.github.io/c-bench-64/
|
||||
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
|
||||
|
||||
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
|
||||
|
||||
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
|
||||
|
||||
* Prog8 program size is consistently the smallest by a fair bit.
|
||||
* Prog8 execution speed places it more or less in the middle of the stack.
|
||||
|
||||
Measured with Prog8 V12.0 .
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
@@ -0,0 +1,71 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const uword EXPECTED = $ffd0
|
||||
uword crc_result
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc16.p8\n")
|
||||
txt.print("Calculates the CRC16 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC16($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_uwhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uwhex(EXPECTED, true)
|
||||
return true
|
||||
}
|
||||
|
||||
sub CRC16(^^ubyte data, uword length) -> uword {
|
||||
; CRC-16/XMODEM
|
||||
uword crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= mkword(@(data),0) ; currently there's no easy way to affect only the MSB of the variable
|
||||
|
||||
repeat 8
|
||||
{
|
||||
crc <<=1
|
||||
if_cs
|
||||
crc ^= $1021
|
||||
}
|
||||
data++
|
||||
}
|
||||
return crc
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const long EXPECTED = $e1fa84c6
|
||||
long crc_result
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc32.p8\n")
|
||||
txt.print("Calculates the CRC32 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC32($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_ulhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ulhex(EXPECTED, true)
|
||||
return true
|
||||
}
|
||||
|
||||
sub CRC32(^^ubyte data, uword length) -> long {
|
||||
; CRC-32/CKSUM
|
||||
long crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= (@(data) as long)<<24 ; currently there's no easy way to affect only the MSB of the variable
|
||||
|
||||
repeat 8
|
||||
{
|
||||
crc <<=1
|
||||
if_cs
|
||||
crc ^= $04c11db7
|
||||
}
|
||||
data++
|
||||
}
|
||||
crc ^= $ffffffff
|
||||
return crc
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("crc8.p8\n")
|
||||
txt.print("Calculates the CRC8 of the C64 Kernal\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
crc_result = CRC8($e000, $2000)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("CRC=")
|
||||
txt.print_ubhex(crc_result, true)
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ubhex(EXPECTED, true)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
const ubyte EXPECTED = $a2
|
||||
ubyte crc_result
|
||||
|
||||
sub CRC8(^^ubyte data, uword length) -> ubyte
|
||||
{
|
||||
; CRC-8/GSM-A
|
||||
ubyte crc
|
||||
|
||||
repeat length
|
||||
{
|
||||
crc ^= @(data)
|
||||
|
||||
repeat 8
|
||||
{
|
||||
if (crc & $80) != 0
|
||||
crc = (crc << 1) ^ $1d
|
||||
else
|
||||
crc <<= 1
|
||||
}
|
||||
data++
|
||||
}
|
||||
return crc
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
|
||||
const ubyte N_ITER = 10
|
||||
const ubyte SIZE = 32
|
||||
float[SIZE] array
|
||||
|
||||
const float expected = 3614007361536.000000
|
||||
const float epsilon = 10000000
|
||||
float res
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("pow.p8\n")
|
||||
txt.print("Calculates floating point exponential (")
|
||||
txt.print_uw(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
ubyte i,j
|
||||
res = 0
|
||||
|
||||
for j in 0 to SIZE-1 {
|
||||
array[j]=0
|
||||
}
|
||||
|
||||
for i in 0 to N_ITER-1 {
|
||||
for j in 0 to SIZE-1 {
|
||||
array[j] += testpow(2.5 / ((i + 1) as float), j)
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0 to SIZE-1 {
|
||||
res += array[j]
|
||||
}
|
||||
}
|
||||
|
||||
sub testpow(float x, ubyte y) -> float
|
||||
{
|
||||
float tmp = x
|
||||
|
||||
if y == 0
|
||||
return 1
|
||||
|
||||
repeat y-1
|
||||
tmp *= x
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("res = ")
|
||||
txt.print_f(res)
|
||||
float diff = expected - res;
|
||||
txt.print("\nexpected = ")
|
||||
txt.print_f(expected)
|
||||
txt.print("\nepsilon = ")
|
||||
txt.print_f(epsilon)
|
||||
txt.print("\ndiff = ")
|
||||
txt.print_f(diff)
|
||||
|
||||
if (diff < epsilon and diff > -epsilon)
|
||||
{
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print("[FAIL]\n")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,85 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
const ubyte N_ITER = 10
|
||||
const uword SIZE = 8191
|
||||
const uword EXPECTED = 1900
|
||||
uword prime_count
|
||||
^^bool @zp flags = memory("flags", SIZE, 0)
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("sieve.c\n")
|
||||
txt.print("Calculates the primes from 1 to ")
|
||||
txt.print_uw(SIZE * 2 + 2)
|
||||
txt.print(" (")
|
||||
txt.print_ub(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
repeat N_ITER
|
||||
prime_count = sieve(SIZE)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("count=")
|
||||
txt.print_uw(prime_count)
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
sub sieve(uword size) -> uword
|
||||
{
|
||||
uword i, prime, k
|
||||
uword count = 1
|
||||
|
||||
for i in 0 to size-1
|
||||
flags[i] = true
|
||||
|
||||
for i in 0 to size-1
|
||||
{
|
||||
if flags[i]
|
||||
{
|
||||
prime = i + i + 3
|
||||
k = i + prime
|
||||
while k < size
|
||||
{
|
||||
flags[k] = false
|
||||
k += prime
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
%import textio
|
||||
%import floats
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test {
|
||||
const ubyte N_ITER = 4
|
||||
const uword SIZE = 16000
|
||||
const uword EXPECTED = 3432
|
||||
uword prime_count
|
||||
^^ubyte @zp flags = memory("flags", SIZE/8+1, 0)
|
||||
|
||||
sub benchmark_name()
|
||||
{
|
||||
txt.print("sieve_bit.p8\n")
|
||||
txt.print("Calculates the primes from 1 to ")
|
||||
txt.print_uw(SIZE * 2 + 2)
|
||||
txt.print(" (")
|
||||
txt.print_ub(N_ITER)
|
||||
txt.print(" iterations)\n")
|
||||
}
|
||||
|
||||
sub benchmark()
|
||||
{
|
||||
repeat N_ITER
|
||||
prime_count = sieve(SIZE)
|
||||
}
|
||||
|
||||
sub benchmark_check() -> bool
|
||||
{
|
||||
txt.print("count=")
|
||||
txt.print_uw(prime_count)
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
return true
|
||||
}
|
||||
|
||||
ubyte[] bitv = [
|
||||
$01,
|
||||
$02,
|
||||
$04,
|
||||
$08,
|
||||
$10,
|
||||
$20,
|
||||
$40,
|
||||
$80
|
||||
]
|
||||
|
||||
sub check_flag(uword idx) -> bool
|
||||
{
|
||||
return flags[idx / 8] & bitv[lsb(idx) & 7] != 0
|
||||
}
|
||||
|
||||
sub clear_flag(uword idx)
|
||||
{
|
||||
flags[idx / 8] &= ~(bitv[lsb(idx) & 7])
|
||||
}
|
||||
|
||||
|
||||
sub sieve(uword size) -> uword
|
||||
{
|
||||
uword i, prime, k
|
||||
uword count=1
|
||||
|
||||
for i in 0 to (size / 8)
|
||||
flags[i] = $ff
|
||||
|
||||
for i in 0 to size-1
|
||||
{
|
||||
if check_flag(i)
|
||||
{
|
||||
prime = i + i + 3
|
||||
k = i + prime;
|
||||
while k < size
|
||||
{
|
||||
clear_flag(k)
|
||||
k += prime
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
; Binary Search Tree.
|
||||
; It's a simple implementation for test/demonstration purposes of the pointer support;
|
||||
; no balancing is done and memory is not freed when elements are removed.
|
||||
|
||||
%import textio
|
||||
|
||||
btree {
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
uword score
|
||||
while cbm.RDTIM16() < max_time {
|
||||
bench_operations()
|
||||
txt.chrout('.')
|
||||
score++
|
||||
}
|
||||
txt.nl()
|
||||
return score
|
||||
}
|
||||
|
||||
sub bench_operations() {
|
||||
arena.freeall()
|
||||
btree.root = 0
|
||||
|
||||
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
|
||||
btree.add(cx16.r0)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
|
||||
void btree.contains(203)
|
||||
void btree.contains(204)
|
||||
void btree.contains(605)
|
||||
void btree.contains(606)
|
||||
|
||||
btree.remove(9999)
|
||||
btree.remove(97)
|
||||
btree.remove(187)
|
||||
btree.remove(203)
|
||||
btree.remove(275)
|
||||
btree.remove(321)
|
||||
btree.remove(520)
|
||||
btree.remove(562)
|
||||
btree.remove(606)
|
||||
btree.remove(719)
|
||||
btree.remove(794)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
}
|
||||
|
||||
struct Node {
|
||||
^^Node left
|
||||
^^Node right
|
||||
uword value
|
||||
}
|
||||
|
||||
^^Node root = 0
|
||||
|
||||
sub add(uword value) {
|
||||
^^Node node = arena.alloc(sizeof(Node))
|
||||
node.value = value
|
||||
node.left = node.right = 0
|
||||
|
||||
if root==0
|
||||
root=node
|
||||
else {
|
||||
^^Node parent = root
|
||||
repeat {
|
||||
if parent.value >= value {
|
||||
if parent.left!=0
|
||||
parent = parent.left
|
||||
else {
|
||||
parent.left = node
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if parent.right!=0
|
||||
parent = parent.right
|
||||
else {
|
||||
parent.right = node
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub contains(uword value) -> bool {
|
||||
^^Node r = root
|
||||
while r!=0 {
|
||||
if r.value==value
|
||||
return true
|
||||
if r.value>value
|
||||
r = r.left
|
||||
else
|
||||
r = r.right
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
ubyte count
|
||||
|
||||
if root!=0
|
||||
count_node(root)
|
||||
|
||||
return count
|
||||
|
||||
sub count_node(^^Node r) {
|
||||
count++
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub remove(uword value) {
|
||||
; note: we don't deallocate the memory from the node, for simplicity sake
|
||||
^^Node n = root
|
||||
^^Node parent = 0
|
||||
while n!=0 {
|
||||
if n.value==value {
|
||||
if n.left==0
|
||||
replacechild(parent, n, n.right)
|
||||
else if n.right==0
|
||||
replacechild(parent, n, n.left)
|
||||
else {
|
||||
; Both left & right subtrees are present.
|
||||
; N = node to delete.
|
||||
; Find N's successor S. (N's right subtree's minimum element)
|
||||
; Attach N's left subtree to S.left (S doesn't have a left child)
|
||||
; Attach N's right subtree to Parent in place of N.
|
||||
^^Node successor = find_successor(n)
|
||||
successor.left = n.left
|
||||
replacechild(parent, n, n.right)
|
||||
}
|
||||
return
|
||||
}
|
||||
parent = n
|
||||
if n.value>value
|
||||
n = n.left
|
||||
else
|
||||
n = n.right
|
||||
}
|
||||
|
||||
sub find_successor(^^Node p) -> ^^Node {
|
||||
^^Node succ = p
|
||||
p = p.right
|
||||
while p!=0 {
|
||||
succ = p
|
||||
p = p.left
|
||||
}
|
||||
return succ
|
||||
}
|
||||
|
||||
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
|
||||
if p.left==child
|
||||
p.left = newchild
|
||||
else
|
||||
p.right = newchild
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_inorder() {
|
||||
if root!=0
|
||||
process_tree(root)
|
||||
|
||||
sub process_tree(^^Node r) {
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
cx16.r0 = r.value
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_preorder() {
|
||||
if root!=0
|
||||
process_tree(root,0)
|
||||
|
||||
sub process_tree(^^Node r, ubyte depth) {
|
||||
cx16.r0 = r.value
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.left, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.right, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
arena {
|
||||
; extremely trivial arena allocator (that never frees)
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
uword next = buffer
|
||||
|
||||
sub alloc(ubyte size) -> uword {
|
||||
defer next += size
|
||||
return next
|
||||
}
|
||||
|
||||
sub freeall() {
|
||||
next = buffer
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
%import b_btree
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
@@ -34,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
|
||||
@@ -64,13 +65,9 @@ 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("circles with kernal")
|
||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||
; benchmark_number++
|
||||
|
||||
announce_benchmark("text-elite")
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
@@ -79,28 +76,32 @@ main {
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("btree-struct-pointers")
|
||||
benchmark_score[benchmark_number] = btree.benchmark(200)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
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])
|
||||
txt.column(6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
final_score += benchmark_score[benchmark_number]
|
||||
total_score += benchmark_score[benchmark_number]
|
||||
txt.nl()
|
||||
benchmark_number++
|
||||
} until benchmark_names[benchmark_number]==0
|
||||
|
||||
txt.print("\n\nfinal score : ")
|
||||
txt.print_uw(final_score)
|
||||
txt.nl()
|
||||
txt.print("\n\ntotal score : ")
|
||||
txt.print_uw(total_score)
|
||||
txt.print(" (higher=better)\n")
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
|
||||
@@ -5,9 +5,10 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// the automatically generated module where all string literals are interned to:
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME) // option to add more if needed one day
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
@@ -37,6 +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.R14R15_32)
|
||||
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
||||
null -> ReturnConvention(null, null)
|
||||
@@ -45,6 +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.R14R15_32)
|
||||
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
else -> ReturnConvention(paramType, null)
|
||||
@@ -61,6 +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.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)
|
||||
@@ -88,11 +91,11 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"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
|
||||
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"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 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)),
|
||||
@@ -100,9 +103,11 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__long" to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
@@ -112,34 +117,41 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"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)),
|
||||
"mklong2" to FSignature(true, BaseDataType.LONG, FParam("msw", BaseDataType.UWORD), FParam("lsw", BaseDataType.UWORD)),
|
||||
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||
"clamp__long" to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG), FParam("minimum", BaseDataType.LONG), FParam("maximum", BaseDataType.LONG)),
|
||||
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"min__long" to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"max__long" to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
|
||||
"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)),
|
||||
|
||||
@@ -13,6 +13,7 @@ class CompilationOptions(val output: OutputType,
|
||||
val noSysInit: Boolean,
|
||||
val romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
val compilerVersion: String,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var memtopAddress: UInt,
|
||||
@@ -28,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,7 +13,7 @@ fun Number.toHex(): String {
|
||||
// 256..65536 -> "$0100".."$ffff"
|
||||
// larger -> "$12345678"
|
||||
// negative values are prefixed with '-'.
|
||||
val integer = this.toInt()
|
||||
val integer = this.toLong()
|
||||
if(integer<0)
|
||||
return '-' + abs(integer).toHex()
|
||||
return when (integer) {
|
||||
|
||||
@@ -50,8 +50,10 @@ enum class BaseDataType {
|
||||
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
|
||||
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
|
||||
val BaseDataType.isLong get() = this == BaseDataType.LONG
|
||||
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
|
||||
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
|
||||
val BaseDataType.isWordOrByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.BOOL)
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
@@ -143,7 +145,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
if(actualElementDt.isNumericOrBool)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
@@ -222,6 +224,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
BaseDataType.LONG -> "long[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
@@ -284,6 +287,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.LONG -> "long["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
@@ -344,6 +348,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isUnsignedWord = base == BaseDataType.UWORD
|
||||
val isSignedWord = base == BaseDataType.WORD
|
||||
val isInteger = base.isInteger
|
||||
val isWordOrByteOrBool = base.isWordOrByteOrBool
|
||||
val isIntegerOrBool = base.isIntegerOrBool
|
||||
val isNumeric = base.isNumeric
|
||||
val isNumericOrBool = base.isNumericOrBool
|
||||
@@ -362,6 +367,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isLongArray = base.isArray && sub == BaseDataType.LONG
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
@@ -394,7 +400,9 @@ enum class RegisterOrPair {
|
||||
FAC2,
|
||||
// cx16 virtual registers:
|
||||
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
R8, R9, R10, R11, R12, R13, R14, R15,
|
||||
// combined virtual registers to store 32 bits longs:
|
||||
R0R1_32, R2R3_32, R4R5_32, R6R7_32, R8R9_32, R10R11_32, R12R13_32, R14R15_32;
|
||||
|
||||
companion object {
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
@@ -407,6 +415,18 @@ enum class RegisterOrPair {
|
||||
}
|
||||
}
|
||||
|
||||
fun startregname() = when(this) {
|
||||
R0R1_32 -> "r0"
|
||||
R2R3_32 -> "r2"
|
||||
R4R5_32 -> "r4"
|
||||
R6R7_32 -> "r6"
|
||||
R8R9_32 -> "r8"
|
||||
R10R11_32 -> "r10"
|
||||
R12R13_32 -> "r12"
|
||||
R14R15_32 -> "r14"
|
||||
else -> throw IllegalArgumentException("must be a combined virtual register $this")
|
||||
}
|
||||
|
||||
fun asCpuRegister(): CpuRegister = when(this) {
|
||||
A -> CpuRegister.A
|
||||
X -> CpuRegister.X
|
||||
@@ -420,13 +440,14 @@ enum class RegisterOrPair {
|
||||
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
BaseDataType.UWORD, BaseDataType.POINTER, null -> ""
|
||||
else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
fun isLong() = this in combinedLongRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
@@ -463,6 +484,17 @@ val Cx16VirtualRegisters = arrayOf(
|
||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||
)
|
||||
|
||||
val combinedLongRegisters = arrayOf(
|
||||
RegisterOrPair.R0R1_32,
|
||||
RegisterOrPair.R2R3_32,
|
||||
RegisterOrPair.R4R5_32,
|
||||
RegisterOrPair.R6R7_32,
|
||||
RegisterOrPair.R8R9_32,
|
||||
RegisterOrPair.R10R11_32,
|
||||
RegisterOrPair.R12R13_32,
|
||||
RegisterOrPair.R14R15_32
|
||||
)
|
||||
|
||||
val CpuRegisters = arrayOf(
|
||||
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||
@@ -498,6 +530,5 @@ enum class ZeropageWish {
|
||||
|
||||
enum class SplitWish {
|
||||
DONTCARE,
|
||||
SPLIT,
|
||||
NOSPLIT
|
||||
}
|
||||
@@ -14,6 +14,7 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.LONG -> numElements * 4
|
||||
BaseDataType.FLOAT-> numElements * floatsize
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
|
||||
@@ -252,6 +252,14 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
newValue.add(newAddr)
|
||||
}
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
// could be a struct instance or memory slab "allocation"
|
||||
if (elt.name != "prog8_lib_structalloc" && elt.name != "memory")
|
||||
throw AssemblyError("weird array value element $elt")
|
||||
else {
|
||||
newValue.add(elt)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird array value element $elt")
|
||||
}
|
||||
}
|
||||
@@ -550,7 +558,7 @@ class AsmGen6502Internal (
|
||||
} else {
|
||||
return if (allocator.isZpVar((target as PtNamedNode).scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
loadAFromZpPointerVar(sourceName, true)
|
||||
loadAFromZpPointerVar(sourceName)
|
||||
sourceName
|
||||
} else {
|
||||
out("""
|
||||
@@ -609,7 +617,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadAFromZpPointerVar(zpPointerVar: String, keepY: Boolean) {
|
||||
internal fun loadAFromZpPointerVar(zpPointerVar: String, keepY: Boolean=false) {
|
||||
if (isTargetCpu(CpuType.CPU65C02))
|
||||
out(" lda ($zpPointerVar)")
|
||||
else {
|
||||
@@ -798,6 +806,7 @@ class AsmGen6502Internal (
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> assignmentAsmGen.assignRegisterpairWord(target, reg)
|
||||
in Cx16VirtualRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
|
||||
in combinedLongRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
|
||||
RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target)
|
||||
RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target)
|
||||
else -> throw AssemblyError("invalid register")
|
||||
@@ -805,6 +814,9 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
|
||||
|
||||
// this is basically the fallback assignment routine, it is RARELY called
|
||||
|
||||
when {
|
||||
target.datatype.isByteOrBool -> {
|
||||
if (value.asConstInteger()==0) {
|
||||
@@ -852,6 +864,102 @@ class AsmGen6502Internal (
|
||||
), value.definingISub()
|
||||
)
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
if(value is PtNumber) {
|
||||
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0')
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out("""
|
||||
lda #$${hex.substring(6,8)}
|
||||
sta ${target.asmVarname}
|
||||
lda #$${hex.substring(4, 6)}
|
||||
sta ${target.asmVarname}+1
|
||||
lda #$${hex.substring(2, 4)}
|
||||
sta ${target.asmVarname}+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 -> assignExpressionToRegister(value, target.register!!, true)
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value is PtIdentifier && value.type.isLong) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
val valuesym = asmSymbolName(value)
|
||||
out(
|
||||
"""
|
||||
lda $valuesym
|
||||
sta ${target.asmVarname}
|
||||
lda $valuesym+1
|
||||
sta ${target.asmVarname}+1
|
||||
lda $valuesym+2
|
||||
sta ${target.asmVarname}+2
|
||||
lda $valuesym+3
|
||||
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 -> assignExpressionToRegister(value, target.register!!, true)
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value is PtTypeCast && value.type.isLong) {
|
||||
if(value.value is PtIdentifier) {
|
||||
val valuesym = asmSymbolName((value.value as PtIdentifier).name)
|
||||
if(value.value.type.isByte) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out(" lda $valuesym | sta ${target.asmVarname}")
|
||||
signExtendLongVariable(target.asmVarname, value.value.type.base)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value.value.type.isWord) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out("""
|
||||
lda $valuesym
|
||||
sta ${target.asmVarname}
|
||||
lda $valuesym+1
|
||||
sta ${target.asmVarname}+1""")
|
||||
signExtendLongVariable(target.asmVarname, value.value.type.base)
|
||||
}
|
||||
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!!.startregname()
|
||||
out("""
|
||||
lda $valuesym
|
||||
sta cx16.$startreg
|
||||
lda $valuesym+1
|
||||
sta cx16.$startreg+1""")
|
||||
signExtendLongVariable("cx16.$startreg", value.value.type.base)
|
||||
}
|
||||
TargetStorageKind.POINTER -> {
|
||||
throw AssemblyError("assign typecasted long into pointer ${target.position}")
|
||||
}
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else throw AssemblyError("weird casted type")
|
||||
} else {
|
||||
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
} else if(target.kind == TargetStorageKind.REGISTER) {
|
||||
assignExpressionToRegister(value, target.register!!, true)
|
||||
} else {
|
||||
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||
assignRegister(RegisterOrPair.FAC1, target)
|
||||
@@ -1157,12 +1265,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
|
||||
@@ -1182,7 +1290,7 @@ $repeatLabel""")
|
||||
if(returnvalue!=null) {
|
||||
val returnDt = sub.signature.returns.single()
|
||||
if (returnDt.isNumericOrBool || returnDt.isPointer) {
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!, returnDt.isSigned)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
@@ -1203,7 +1311,7 @@ $repeatLabel""")
|
||||
assignExpressionTo(it.first as PtExpression, tgt)
|
||||
}
|
||||
assigns.first().also {
|
||||
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!)
|
||||
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!, (it.first as PtExpression).type.isSigned)
|
||||
}
|
||||
}
|
||||
out(" rts")
|
||||
@@ -1289,6 +1397,50 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendLongVariable(asmvar: String, valueDt: BaseDataType) {
|
||||
// sign extend signed word in a var to a full long in that variable
|
||||
when(valueDt) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
out("""
|
||||
stz $asmvar+1
|
||||
stz $asmvar+2
|
||||
stz $asmvar+3""")
|
||||
}
|
||||
else {
|
||||
out("""
|
||||
lda #0
|
||||
sta $asmvar+1
|
||||
sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
out("""
|
||||
lda $asmvar
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta $asmvar+1
|
||||
sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
out("""
|
||||
lda $asmvar+1
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
else -> throw AssemblyError("need byte or word type")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
|
||||
|
||||
internal fun jmp(asmLabel: String, indirect: Boolean=false, indexedX: Boolean=false) {
|
||||
@@ -1364,7 +1516,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 {
|
||||
@@ -1406,9 +1558,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
|
||||
@@ -1456,7 +1608,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 {
|
||||
@@ -1489,9 +1641,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
|
||||
@@ -2269,6 +2421,69 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun pushLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
|
||||
if(numberOfLongs==0) return
|
||||
|
||||
val startregName = startreg.startregname()
|
||||
if(numberOfLongs==1) {
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda cx16.$startregName
|
||||
pha
|
||||
lda cx16.$startregName+1
|
||||
pha
|
||||
lda cx16.$startregName+2
|
||||
pha
|
||||
lda cx16.$startregName+3
|
||||
pha
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
return
|
||||
}
|
||||
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
- lda cx16.$startregName,y
|
||||
pha
|
||||
iny
|
||||
cpy #$numbytes
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
}
|
||||
|
||||
internal fun popLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
|
||||
if(numberOfLongs==0) return
|
||||
|
||||
val startregName = startreg.startregname()
|
||||
if(numberOfLongs==1) {
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
pla
|
||||
sta cx16.$startregName+3
|
||||
pla
|
||||
sta cx16.$startregName+2
|
||||
pla
|
||||
sta cx16.$startregName+1
|
||||
pla
|
||||
sta cx16.$startregName
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
return
|
||||
}
|
||||
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ldy #$numbytes-1
|
||||
- pla
|
||||
sta cx16.$startregName,y
|
||||
dey
|
||||
bpl -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -463,15 +463,19 @@ private fun optimizeStoreLoadSame(
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated IF NOT IO ADDRESS
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
|
||||
) {
|
||||
val firstVal = first.substring(4).trimStart()
|
||||
val thirdVal = third.substring(4).trimStart()
|
||||
if (firstVal == thirdVal)
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
if (firstVal == thirdVal) {
|
||||
val address = getAddressArg(third, symbolTable)
|
||||
if (address != null && !machine.isIOAddress(address)) {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mods
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
@@ -25,15 +27,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val sscope = fcall.definingISub()
|
||||
|
||||
when (fcall.name) {
|
||||
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
|
||||
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
|
||||
"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)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
|
||||
"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)
|
||||
"divmod__ubyte" -> funcDivmod(fcall)
|
||||
@@ -46,10 +51,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekl" -> funcPeekL(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peekbool" -> funcPeekBool(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokel" -> funcPokeL(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
"pokemon" -> {
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
@@ -344,38 +351,112 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
} else {
|
||||
// arg1 is a word
|
||||
} 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
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
} else if(arg1.type.isLong) {
|
||||
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')
|
||||
asmgen.out("""
|
||||
sec
|
||||
lda $var1
|
||||
sbc #$${hex.substring(6, 8)}
|
||||
lda $var1+1
|
||||
sbc #$${hex.substring(4, 6)}
|
||||
lda $var1+2
|
||||
sbc #$${hex.substring(2, 4)}
|
||||
lda $var1+3
|
||||
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
|
||||
sbc $var2
|
||||
lda $var1+1
|
||||
sbc $var2+1
|
||||
lda $var1+2
|
||||
sbc $var2+2
|
||||
lda $var1+3
|
||||
sbc $var2+3""")
|
||||
} else {
|
||||
// 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("""
|
||||
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""")
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +466,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||
val slabname = PtIdentifier("$StMemorySlabBlockName.memory_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
@@ -399,9 +480,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val slabname = SymbolTable.labelnameForStructInstance(fcall)
|
||||
val prefix = if(fcall.args.isEmpty()) "${StStructInstanceBlockName}_bss" else StStructInstanceBlockName
|
||||
val labelname = PtIdentifier("$prefix.${SymbolTable.labelnameForStructInstance(fcall)}", fcall.type, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(PtIdentifier(slabname, fcall.type, fcall.position))
|
||||
addressOf.add(labelname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
@@ -421,6 +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 ${fcall.position}")
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
@@ -477,6 +560,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("ror2 long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out("""
|
||||
lsr $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable
|
||||
bcc +
|
||||
lda $variable+3
|
||||
ora #$80
|
||||
sta $variable+3
|
||||
+""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -541,6 +644,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("ror long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
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 type")
|
||||
}
|
||||
}
|
||||
@@ -592,6 +706,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("rol2 long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out("""
|
||||
asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
bcc +
|
||||
inc $variable
|
||||
+""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -655,6 +787,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("rol long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
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 type")
|
||||
}
|
||||
}
|
||||
@@ -726,6 +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 -> 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")
|
||||
}
|
||||
@@ -744,6 +888,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
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")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
@@ -821,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 */ }
|
||||
@@ -871,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 */ }
|
||||
@@ -881,6 +1056,18 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
private fun funcPokeL(fcall: PtBuiltinFunctionCall) {
|
||||
// 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?) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
@@ -915,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()
|
||||
@@ -960,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()
|
||||
@@ -977,6 +1168,36 @@ 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?) {
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type.isSigned
|
||||
when {
|
||||
@@ -996,6 +1217,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("clamp long ${fcall.position}")
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1276,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("min long ${fcall.position}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("min float not supported")
|
||||
}
|
||||
@@ -1060,55 +1287,109 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type.isSigned
|
||||
if(fcall.type.isByte) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
} else if(fcall.type.isWord) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if(signed) {
|
||||
when {
|
||||
fcall.type.isByte -> {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if (signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+"""
|
||||
)
|
||||
val targetReg = AsmAssignTarget.fromRegisters(
|
||||
resultRegister ?: RegisterOrPair.A,
|
||||
signed,
|
||||
fcall.position,
|
||||
fcall.definingISub(),
|
||||
asmgen
|
||||
)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
fcall.type.isWord -> {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if (signed) {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(
|
||||
resultRegister ?: RegisterOrPair.AY,
|
||||
signed,
|
||||
fcall.position,
|
||||
fcall.definingISub(),
|
||||
asmgen
|
||||
)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("max long ${fcall.position}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("max float not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMklong(fcall: PtBuiltinFunctionCall) {
|
||||
// result long in R14:R15 (r14=lsw, r15=msw)
|
||||
|
||||
fun isArgRegister(expression: PtExpression, reg: RegisterOrPair): Boolean {
|
||||
if(expression !is PtIdentifier)
|
||||
return false
|
||||
return expression.name.startsWith("cx16.${reg.name.lowercase()}")
|
||||
}
|
||||
|
||||
if(fcall.args.size==2) {
|
||||
// mklong2(msw, lsw)
|
||||
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 {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r15", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r14", DataType.UWORD)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
} else {
|
||||
throw AssemblyError("max float not supported")
|
||||
// mklong(msb, b2, b1, lsb)
|
||||
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.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,10 +1460,43 @@ 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)
|
||||
throw AssemblyError("msb required word argument")
|
||||
throw AssemblyError("msb requires word argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
@@ -1201,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 {
|
||||
@@ -1277,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")
|
||||
|
||||
@@ -1305,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 {
|
||||
@@ -1363,11 +1689,87 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("msw requires long argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msw(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultRegister) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+2 | ldx $sourceName+3")
|
||||
null, RegisterOrPair.AY -> asmgen.out(" lda $sourceName+2 | ldy $sourceName+3")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+2 | ldy $sourceName+3")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
asmgen.out(" lda $sourceName+2 | sta cx16.$regname | lda $sourceName+3 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("lsw requires long argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("lsw(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultRegister) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||
null, RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda $sourceName+1 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
|
||||
val signature = BuiltinFunctions.getValue(call.name)
|
||||
val callConv = signature.callConvention(call.args.map {
|
||||
@@ -1410,6 +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 ${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)
|
||||
@@ -1428,6 +1831,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.reg != null -> {
|
||||
val src = when {
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt==BaseDataType.LONG -> AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
|
||||
@@ -1439,7 +1843,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, conv.dt.isSigned, value.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign, scope)
|
||||
}
|
||||
|
||||
@@ -505,137 +505,158 @@ $endLabel""")
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
|
||||
fun iterateStrings() {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$indexVar .byte 0
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
}
|
||||
|
||||
fun iterateBytes() {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
fun iterateSplitWords() {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda ${iterableName}_lsb,y
|
||||
sta $loopvarName
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
fun iterateWords(actuallyLongs: Boolean = false) {
|
||||
val indexVar = if(asmgen.options.romable)
|
||||
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
else
|
||||
asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta $loopvarName
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=127u) {
|
||||
if(actuallyLongs) {
|
||||
asmgen.out("""
|
||||
lda $iterableName+2,y
|
||||
sta $loopvarName+2
|
||||
lda $iterableName+3,y
|
||||
sta $loopvarName+3""")
|
||||
}
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=127u) {
|
||||
if(actuallyLongs) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
cpy #${numElements*4u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -643,6 +664,19 @@ $loopLabel sty $indexVar
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
}
|
||||
} else {
|
||||
if(actuallyLongs) {
|
||||
// array size is 64 longs, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
|
||||
} else {
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
@@ -652,23 +686,29 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
if(!asmgen.options.romable) {
|
||||
if(numElements>=16u) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isFloatArray -> {
|
||||
throw AssemblyError("for loop with floating point variables is not supported")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
when {
|
||||
iterableDt.isString -> iterateStrings()
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> iterateBytes()
|
||||
iterableDt.isSplitWordArray -> iterateSplitWords()
|
||||
iterableDt.isWordArray -> iterateWords()
|
||||
iterableDt.isLongArray -> iterateWords(true)
|
||||
iterableDt.isFloatArray -> throw AssemblyError("for loop with floating point variables is not supported")
|
||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||
}
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
|
||||
@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||
// Just load them in whatever the register spec says.
|
||||
return when (params.size) {
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
1 -> params[0].register == null && (params[0].type.isWordOrByteOrBool || params[0].type.isPointer)
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||
else -> false
|
||||
}
|
||||
@@ -206,8 +206,11 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0])
|
||||
if (arg.name == "mkword")
|
||||
if (arg.name == "mkword" || arg.name == "mklong2")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
|
||||
if (arg.name == "mklong")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) ||
|
||||
usesOtherRegistersWhileEvaluating(arg.args[2]) || usesOtherRegistersWhileEvaluating(arg.args[3])
|
||||
return !arg.isSimple()
|
||||
}
|
||||
is PtAddressOf -> false
|
||||
|
||||
@@ -46,6 +46,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isLong -> translateIfLong(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -575,6 +576,29 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfLong(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val constValue = condition.right.asConstInteger()
|
||||
if(constValue==0) {
|
||||
// optimized comparisons with zero
|
||||
return when (condition.operator) {
|
||||
"==" -> longEqualsZero(condition.left, false, jumpAfterIf, stmt)
|
||||
"!=" -> longEqualsZero(condition.left, true, jumpAfterIf, stmt)
|
||||
"<" -> longLessZero(condition.left, false, jumpAfterIf, stmt)
|
||||
"<=" -> longLessZero(condition.left, true, jumpAfterIf, stmt)
|
||||
">" -> longGreaterZero(condition.left, false, jumpAfterIf, stmt)
|
||||
">=" -> longGreaterZero(condition.left, true, jumpAfterIf, stmt)
|
||||
else -> throw AssemblyError("expected comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
return when (condition.operator) {
|
||||
"==" -> longEqualsValue(condition.left, condition.right, false, jumpAfterIf, stmt)
|
||||
"!=" -> longEqualsValue(condition.left, condition.right, true, jumpAfterIf, stmt)
|
||||
"<", "<=", ">", ">=" -> compareLongValues(condition.left, condition.operator, condition.right, jumpAfterIf, stmt)
|
||||
else -> throw AssemblyError("expected comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
@@ -606,7 +630,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun wordGreaterEqualsZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
// special case for word>=0
|
||||
if(signed) {
|
||||
loadAndCmp0MSB(value)
|
||||
loadAndCmp0MSB(value, false)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
|
||||
else
|
||||
@@ -619,7 +643,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun wordLessZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
// special case for word<0
|
||||
if(signed) {
|
||||
loadAndCmp0MSB(value)
|
||||
loadAndCmp0MSB(value, false)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
|
||||
else
|
||||
@@ -894,23 +918,31 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
|
||||
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
private fun loadAndCmp0MSB(value: PtExpression, long: Boolean) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
if(value.splitWords) {
|
||||
require(!long)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
}
|
||||
else if(long)
|
||||
asmgen.out(" lda $varname+3,y")
|
||||
else
|
||||
asmgen.out(" lda $varname+1,y")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(value)
|
||||
asmgen.out(" lda $varname+1")
|
||||
if(long)
|
||||
asmgen.out(" lda $varname+3")
|
||||
else
|
||||
asmgen.out(" lda $varname+1")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
require(!long)
|
||||
if(value.isFromArrayElement) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
@@ -923,6 +955,7 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
require(!long)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
}
|
||||
@@ -1226,6 +1259,63 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
|
||||
private fun longEqualsZero(value: PtExpression, notEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
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)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
} else {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bne", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long <= 0 ${value.position}")
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long >= 0 ${value.position}")
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
|
||||
// special case for (u)word == 0
|
||||
@@ -1303,6 +1393,140 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
|
||||
private fun longEqualsValue(
|
||||
left: PtExpression,
|
||||
right: PtExpression,
|
||||
notEquals: Boolean,
|
||||
jump: PtJump?,
|
||||
stmt: PtIfElse
|
||||
) {
|
||||
val constRight = right.asConstInteger()
|
||||
val variableRight = (right as? PtIdentifier)?.name
|
||||
|
||||
if(left is PtIdentifier) {
|
||||
val leftvar = asmgen.asmVariableName(left)
|
||||
if(constRight!=null) {
|
||||
val hex = constRight.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
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
|
||||
{
|
||||
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
|
||||
if(constRight!=null) {
|
||||
val hex = constRight.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
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.r14+1
|
||||
cmp $variableRight+1
|
||||
bne +
|
||||
lda cx16.r14+2
|
||||
cmp $variableRight+2
|
||||
bne +
|
||||
lda cx16.r14+3
|
||||
cmp $variableRight+3
|
||||
+""")
|
||||
} else {
|
||||
TODO("long == value expression ${right.position}")
|
||||
}
|
||||
}
|
||||
|
||||
if(notEquals) {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
} else {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bne", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -15,32 +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.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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +109,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isLong -> translateIfExpressionLongConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -200,6 +196,36 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
|
||||
private fun translateIfExpressionLongConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
|
||||
// TODO can we reuse this whole thing from IfElse ?
|
||||
val constValue = condition.right.asConstInteger()
|
||||
if(constValue!=null) {
|
||||
if (constValue == 0) {
|
||||
when (condition.operator) {
|
||||
"==" -> return translateLongExprIsZero(condition.left, falseLabel)
|
||||
"!=" -> return translateLongExprIsNotZero(condition.left, falseLabel)
|
||||
}
|
||||
}
|
||||
if (constValue != 0) {
|
||||
when (condition.operator) {
|
||||
"==" -> return translateLongExprEqualsNumber(condition.left, constValue, falseLabel)
|
||||
"!=" -> return translateLongExprNotEqualsNumber(condition.left, constValue, falseLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
val variable = condition.right as? PtIdentifier
|
||||
if(variable!=null) {
|
||||
when (condition.operator) {
|
||||
"==" -> return translateLongExprEqualsVariable(condition.left, variable, falseLabel)
|
||||
"!=" -> return translateLongExprNotEqualsVariable(condition.left, variable, falseLabel)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
|
||||
private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
|
||||
val constValue = (condition.right as? PtNumber)?.number
|
||||
if(constValue==0.0) {
|
||||
@@ -339,6 +365,91 @@ 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')
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #$${hex.substring(6, 8)}
|
||||
bne $falseLabel
|
||||
lda $varname+1
|
||||
cmp #$${hex.substring(4, 6)}
|
||||
bne $falseLabel
|
||||
lda $varname+2
|
||||
cmp #$${hex.substring(2, 4)}
|
||||
bne $falseLabel
|
||||
lda $varname+3
|
||||
cmp #$${hex.take(2)}
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r14
|
||||
cmp #$${hex.substring(6, 8)}
|
||||
bne $falseLabel
|
||||
lda cx16.r14+1
|
||||
cmp #$${hex.substring(4, 6)}
|
||||
bne $falseLabel
|
||||
lda cx16.r14+2
|
||||
cmp #$${hex.substring(2, 4)}
|
||||
bne $falseLabel
|
||||
lda cx16.r14+3
|
||||
cmp #$${hex.take(2)}
|
||||
bne $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||
// if L==variable
|
||||
// TODO reuse code from ifElse?
|
||||
val varname2 = asmgen.asmVariableName(variable)
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp $varname2
|
||||
bne $falseLabel
|
||||
lda $varname+1
|
||||
cmp $varname2+1
|
||||
bne $falseLabel
|
||||
lda $varname+2
|
||||
cmp $varname2+2
|
||||
bne $falseLabel
|
||||
lda $varname+3
|
||||
cmp $varname2+3
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r14
|
||||
cmp $varname2
|
||||
bne $falseLabel
|
||||
lda cx16.r14+1
|
||||
cmp $varname2+1
|
||||
bne $falseLabel
|
||||
lda cx16.r14+2
|
||||
cmp $varname2+2
|
||||
bne $falseLabel
|
||||
lda cx16.r14+3
|
||||
cmp $varname2+3
|
||||
bne $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
|
||||
TODO("if expression LONG != number ${expr.position}")
|
||||
}
|
||||
|
||||
private fun translateLongExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||
// if L!=variable
|
||||
TODO("if expression LONG != variable ${expr.position}")
|
||||
}
|
||||
|
||||
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
|
||||
// if w!=0
|
||||
// TODO reuse code from ifElse?
|
||||
@@ -369,6 +480,56 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprIsZero(expr: PtExpression, falseLabel: String) {
|
||||
// if L==0
|
||||
// TODO reuse code from ifElse?
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ora $varname+1
|
||||
ora $varname+2
|
||||
ora $varname+3
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
|
||||
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 | bne $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprIsNotZero(expr: PtExpression, falseLabel: String) {
|
||||
// if L!=0
|
||||
// TODO reuse code from ifElse?
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ora $varname+1
|
||||
ora $varname+2
|
||||
ora $varname+3
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
|
||||
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 | beq $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
||||
// optimized code for byte comparisons with 0
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ internal class ProgramAndVarsGen(
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
asmgen.out("$StMemorySlabBlockName\t.block")
|
||||
for (slab in symboltable.allMemorySlabs) {
|
||||
if (slab.align > 1u)
|
||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||
@@ -377,6 +377,7 @@ internal class ProgramAndVarsGen(
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isLong -> ".dint"
|
||||
dt.isFloat -> ".byte"
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -418,14 +419,7 @@ internal class ProgramAndVarsGen(
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
val type = when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
val type = asmTypeString(dt)
|
||||
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
|
||||
}
|
||||
asmgen.out(" .endstruct\n")
|
||||
@@ -434,6 +428,7 @@ internal class ProgramAndVarsGen(
|
||||
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")
|
||||
instancesNoInit.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val zerovalues = structtype.fields.map { field ->
|
||||
@@ -445,11 +440,17 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send BSS\n")
|
||||
|
||||
asmgen.out("; struct instances with initialization values\n")
|
||||
asmgen.out(" .section STRUCTINSTANCES\n")
|
||||
instances.forEach { asmgen.out("${it.name} .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n") }
|
||||
asmgen.out("$StStructInstanceBlockName .block\n")
|
||||
instances.forEach {
|
||||
val instancename = it.name.substringAfter('.')
|
||||
asmgen.out("$instancename .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send STRUCTINSTANCES\n")
|
||||
}
|
||||
|
||||
@@ -564,6 +565,7 @@ internal class ProgramAndVarsGen(
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.LONG -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
@@ -774,6 +776,7 @@ internal class ProgramAndVarsGen(
|
||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
||||
dt.isLong -> asmgen.out("${variable.name}\t.dint ?")
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
@@ -788,7 +791,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
TODO("pointers are not supported yet for uninitialized array ${variable.astNode?.position}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -822,6 +825,7 @@ internal class ProgramAndVarsGen(
|
||||
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
// dt.isLong -> asmgen.out("${variable.name}\t.dint $initialValue")
|
||||
// dt.isFloat -> {
|
||||
// if(initialValue==0) {
|
||||
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||
@@ -866,7 +870,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
dt.isSplitWordArray -> {
|
||||
if(dt.elementType().isUnsignedWord) {
|
||||
if(dt.elementType().isUnsignedWord || dt.elementType().isPointer) {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||
@@ -898,6 +902,16 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .sint " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
dt.isLongArray -> {
|
||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.dint ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .dint " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
dt.isFloatArray -> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
val floatFills = array.map {
|
||||
@@ -914,7 +928,7 @@ internal class ProgramAndVarsGen(
|
||||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null, null))
|
||||
values.add(StArrayElement(0.0, null, null,null,null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -972,7 +986,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
if(it.number!=null) {
|
||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
@@ -984,8 +998,15 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
asmgen.asmSymbolName(addrOfSymbol)
|
||||
}
|
||||
else
|
||||
else if(it.structInstance!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}.${it.structInstance!!}")
|
||||
}
|
||||
else if(it.structInstanceUninitialized!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}_bss.${it.structInstanceUninitialized!!}")
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("weird array elt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
@@ -1013,7 +1034,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
dt.isArray && dt.elementType().isSignedWord -> array.map {
|
||||
dt.isSignedWordArray -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||
if(number>=0)
|
||||
@@ -1021,6 +1042,14 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isLongArray -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
|
||||
if(number>=0)
|
||||
"$$hexnum"
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
expr.type.isLong -> {
|
||||
require(expr.left.type.isLong && expr.right.type.isLong) {
|
||||
"both operands must be longs"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: long ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
require(expr.left.type.isFloat && expr.right.type.isFloat) {
|
||||
"both operands must be floats"
|
||||
@@ -179,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}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ internal enum class TargetStorageKind {
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
POINTER,
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -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!!)
|
||||
}
|
||||
}
|
||||
@@ -114,25 +114,17 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.FAC2 -> {
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
in Cx16VirtualRegisters -> {
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
val dt = if(signed) DataType.LONG
|
||||
else
|
||||
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
+491
-14
@@ -106,13 +106,24 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt())
|
||||
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}")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -156,7 +167,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into pointer")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into pointer ${target.position}")
|
||||
SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -291,13 +302,32 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
target.datatype.isLong -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.boolean!!.asInt())
|
||||
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.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
} else {
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.boolean!!.asInt().toDouble())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float array")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float array ${target.position}")
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(targetVarName, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -488,7 +518,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(tempvar, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -523,6 +553,449 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
|
||||
// it's not an expression so no need to preserve R14:R15
|
||||
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
|
||||
inplacemodificationLongWithVariable(targetVar, operator, "cx16.r14")
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $targetVar
|
||||
adc $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
adc $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
adc $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
adc $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
sec
|
||||
lda $targetVar
|
||||
sbc $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
sbc $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
sbc $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
sbc $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $sourceVar
|
||||
- asl $targetVar
|
||||
rol $targetVar+1
|
||||
rol $targetVar+2
|
||||
rol $targetVar+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
">>" -> {
|
||||
asmgen.out("""
|
||||
ldy $sourceVar
|
||||
- lda $targetVar+3
|
||||
asl a ; save sign bit
|
||||
ror $targetVar+3
|
||||
ror $targetVar+2
|
||||
ror $targetVar+1
|
||||
ror $targetVar
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
ora $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
ora $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
ora $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
ora $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"&" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
and $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
and $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
and $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
and $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
eor $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
eor $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
eor $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
eor $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
else -> {
|
||||
TODO("in-place modify LONG with variable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) {
|
||||
|
||||
fun inplaceLongShiftLeft() {
|
||||
when {
|
||||
value in 0..2 -> {
|
||||
repeat(value) {
|
||||
asmgen.out("""
|
||||
asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3""")
|
||||
}
|
||||
}
|
||||
value in 3..7 -> {
|
||||
asmgen.out("""
|
||||
ldy #$value
|
||||
- asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 8 -> {
|
||||
asmgen.out("""
|
||||
lda $variable+2
|
||||
sta $variable+3
|
||||
lda $variable+1
|
||||
sta $variable+2
|
||||
lda $variable
|
||||
sta $variable+1
|
||||
lda #0
|
||||
sta $variable""")
|
||||
}
|
||||
value in 9..15 -> {
|
||||
val shift = value - 8
|
||||
asmgen.out("""
|
||||
lda $variable+2
|
||||
sta $variable+3
|
||||
lda $variable+1
|
||||
sta $variable+2
|
||||
lda $variable
|
||||
sta $variable+1
|
||||
lda #0
|
||||
sta $variable
|
||||
ldy #$shift
|
||||
- asl $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 16 -> {
|
||||
asmgen.out("""
|
||||
lda $variable+1
|
||||
sta $variable+3
|
||||
lda $variable
|
||||
sta $variable+2
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1""")
|
||||
}
|
||||
value in 17..23 -> {
|
||||
val shift = value-16
|
||||
asmgen.out("""
|
||||
lda $variable+1
|
||||
sta $variable+3
|
||||
lda $variable
|
||||
sta $variable+2
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
ldy #$shift
|
||||
- asl $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 24 -> {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sta $variable+3
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2""")
|
||||
}
|
||||
value <= 31 -> {
|
||||
val shift = value-24
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
ldy #$shift
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
sta $variable+3
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun inplaceLongShiftRight() {
|
||||
when {
|
||||
value in 0..2 -> {
|
||||
repeat(value) {
|
||||
asmgen.out("""
|
||||
lda $variable+3
|
||||
asl a ; save sign bit
|
||||
ror $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable""")
|
||||
}
|
||||
}
|
||||
// TODO optimize for more cases 8, 16, 24 etc but don't forget to take the sign bit into account!
|
||||
value <= 31 -> {
|
||||
asmgen.out("""
|
||||
ldy #$value
|
||||
- lda $variable+3
|
||||
asl a ; save sign bit
|
||||
ror $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
when(value) {
|
||||
0 -> {}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
inc $variable
|
||||
bne +
|
||||
inc $variable+1
|
||||
bne +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
}
|
||||
else -> {
|
||||
if(value in 1..255) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #$value
|
||||
sta $variable
|
||||
bcc +
|
||||
inc $variable+1
|
||||
bne +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
} else if(value in 1..65535) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #<$value
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
adc #>$value
|
||||
sta $variable+1
|
||||
bcc +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
} else {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
adc #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
adc #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
adc #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
when(value) {
|
||||
0 -> {}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
bne +
|
||||
dec $variable+1
|
||||
bne +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+ dec $variable""")
|
||||
}
|
||||
else -> {
|
||||
if(value in 1..255) {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sec
|
||||
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
|
||||
lda $variable+1
|
||||
sbc #>$value
|
||||
sta $variable+1
|
||||
bcs +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+""")
|
||||
} else {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
sec
|
||||
lda $variable
|
||||
sbc #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
sbc #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
sbc #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
sbc #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"<<" -> if (value > 0) inplaceLongShiftLeft()
|
||||
">>" -> if (value > 0) inplaceLongShiftRight()
|
||||
"|" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
ora #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
ora #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
ora #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
ora #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
"&" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
and #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
and #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
and #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
and #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
"^" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
eor #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
eor #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
eor #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
eor #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
else -> {
|
||||
TODO("inplace long $operator $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
@@ -966,7 +1439,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
|
||||
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons")
|
||||
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
@@ -1089,7 +1562,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
|
||||
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons")
|
||||
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -1851,7 +2324,7 @@ $shortcutLabel:""")
|
||||
if(value in asmgen.optimizedWordMultiplications) {
|
||||
asmgen.out(" lda $lsb | ldy $msb | jsr prog8_math.mul_word_$value | sta $lsb | sty $msb")
|
||||
} else {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.out("""
|
||||
lda $lsb
|
||||
@@ -1862,9 +2335,10 @@ $shortcutLabel:""")
|
||||
ldy #>$value
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $lsb
|
||||
sty $msb""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $lsb
|
||||
@@ -2349,9 +2823,10 @@ $shortcutLabel:""")
|
||||
ldy $name+1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
|
||||
} else {
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.out(" lda $otherName | sta prog8_math.multiply_words.multiplier")
|
||||
@@ -2494,7 +2969,7 @@ $shortcutLabel:""")
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"*" -> {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -2505,9 +2980,10 @@ $shortcutLabel:""")
|
||||
ldy $otherName+1
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
@@ -2698,7 +3174,7 @@ $shortcutLabel:""")
|
||||
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
|
||||
require(dt.isWord)
|
||||
fun multiplyVarByWordInAX() {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
sta cx16.r1
|
||||
@@ -2707,9 +3183,10 @@ $shortcutLabel:""")
|
||||
ldx $name+1
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sta prog8_math.multiply_words.multiplier
|
||||
@@ -3003,7 +3480,7 @@ $shortcutLabel:""")
|
||||
if(value is PtNumber && value.number<=255) {
|
||||
TODO("shift a word var by ${value.number}")
|
||||
} else {
|
||||
throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||
throw AssemblyError("bit shift value can not be larger than a byte")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
|
||||
@@ -249,7 +249,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else if(value.type.isLong)
|
||||
TODO("load long")
|
||||
TODO("load long ${value.position}")
|
||||
else
|
||||
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}")
|
||||
}
|
||||
@@ -257,47 +257,65 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
internal fun inplaceModification(target: PtrTarget, operator: String, value: AsmAssignSource) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(target.dt.isByte) inplaceByteAdd(target, value)
|
||||
else if(target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
|
||||
val amount = value.number.number.toInt()
|
||||
inplaceByteInc(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteAdd(target, value)
|
||||
else if (target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
|
||||
else if (target.dt.isLong) inplaceLongAdd(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(target.dt.isByte) inplaceByteSub(target, value)
|
||||
else if(target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
|
||||
val amount = value.number.number.toInt()
|
||||
inplaceByteDec(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteSub(target, value)
|
||||
else if (target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
|
||||
else if (target.dt.isLong) inplaceLongSub(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
|
||||
else if(target.dt.isWord) inplaceWordMul(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FMULT", value)
|
||||
else if(target.dt.isLong) TODO("inplace long mul ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"/" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteDiv(target, value) ${target.position}")
|
||||
else if(target.dt.isWord) inplaceWordDiv(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FDIV", value)
|
||||
else if(target.dt.isLong) TODO("inplace long div ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"%" -> TODO("inplace ptr %")
|
||||
"%" -> TODO("inplace ptr % ${target.position}")
|
||||
"<<" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteShiftLeft(target, value) ${target.position}")
|
||||
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
">>" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteShiftRight(target, value) ${target.position}")
|
||||
if(target.dt.isByte) inplaceByteShiftRight(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr &")
|
||||
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
|
||||
else if(target.dt.isWord) inplaceWordAnd(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr |")
|
||||
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
|
||||
else if(target.dt.isWord) inplaceWordOr(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
@@ -305,12 +323,6 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
else if(target.dt.isWord) inplaceWordXor(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"==" -> TODO("inplace ptr ==")
|
||||
"!=" -> TODO("inplace ptr !=")
|
||||
"<" -> TODO("inplace ptr <")
|
||||
"<=" -> TODO("inplace ptr <=")
|
||||
">" -> TODO("inplace ptr >")
|
||||
">=" -> TODO("inplace ptr >=")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -398,6 +410,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("array ptr assign const word ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignLong(target: IndexedPtrTarget, long: Int) {
|
||||
TODO("array ptr assign const long ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignFloat(target: IndexedPtrTarget, float: Double) {
|
||||
TODO("array ptr assign const float ${target.position}")
|
||||
}
|
||||
@@ -434,6 +450,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("array ptr assign word var ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignLongVar(target: IndexedPtrTarget, varName: String) {
|
||||
TODO("array ptr assign long var ${target.position}")
|
||||
}
|
||||
|
||||
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero), and finally the datatype of the field
|
||||
@@ -451,7 +471,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
if(left.variable!=null) {
|
||||
asmgen.assignExpressionToRegister(left.variable!!, RegisterOrPair.AY)
|
||||
} else if(left.pointerderef!=null) {
|
||||
TODO("get pointer from deref $left")
|
||||
TODO("get pointer from deref $left ${left.position}")
|
||||
} else {
|
||||
throw AssemblyError("weird arrayindexer $left")
|
||||
}
|
||||
@@ -491,10 +511,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
|
||||
private fun addUnsignedByteOrWordToAY(value: PtExpression, scale: Int?) {
|
||||
// TODO generate more optimal code from this, a bit too much stack and temporary variable juggling seems to be done in some easy cases...
|
||||
|
||||
fun complicatedFallback() {
|
||||
// slow fallback routine that can deal with any expression for value
|
||||
// slow fallback routine that can deal with any expression for value (const number and variable have been dealt with already)
|
||||
|
||||
fun restoreAYandAddAsmVariable(varname: String, isByte: Boolean) {
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false) // msb
|
||||
@@ -693,7 +712,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed word shift rigth ${target.position} $value")
|
||||
TODO("signed word shift right ${target.position} $value")
|
||||
|
||||
fun shift1unsigned() {
|
||||
asmgen.out("""
|
||||
@@ -720,21 +739,92 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
TODO("<< expression")
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed byte shift right ${target.position} $value")
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
lsr a
|
||||
sta ($zpPtrVar),y""")
|
||||
} else if(number>1) {
|
||||
asmgen.out("""
|
||||
ldx #$number
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldx $varname
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
@@ -768,26 +858,113 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
TODO("<< expression")
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteShiftLeft(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
asl a
|
||||
sta ($zpPtrVar),y""")
|
||||
} else if(number>1) {
|
||||
asmgen.out("""
|
||||
ldx #$number
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldx $varname
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> TODO("inplace long add with ${value.number} ${target.position}")
|
||||
SourceStorageKind.VARIABLE -> TODO("inplace long add with ${value.asmVarname} ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> TODO("inplace long add with ${value.expression} ${target.position}")
|
||||
else -> TODO("inplace long add with ${value.kind} ${target.position}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> TODO("inplace long sub with ${value.number} ${target.position}")
|
||||
SourceStorageKind.VARIABLE -> TODO("inplace long sub with ${value.asmVarname} ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> TODO("inplace long sub with ${value.expression} ${target.position}")
|
||||
else -> TODO("inplace long sub with ${value.kind} ${target.position}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordAdd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
@@ -869,9 +1046,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> TODO("variable + * float")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression + * float")
|
||||
SourceStorageKind.REGISTER -> TODO("register + * float")
|
||||
SourceStorageKind.VARIABLE -> TODO("variable + * float ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression + * float ${target.position}")
|
||||
SourceStorageKind.REGISTER -> TODO("register + * float ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
@@ -920,7 +1097,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
sbc P8ZP_SCRATCH_W1+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register - word")
|
||||
SourceStorageKind.REGISTER -> TODO("register - word ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
@@ -972,7 +1149,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
asmgen.assignRegister(register, multiplyArg)
|
||||
multiply()
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr * expr (word)")
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr * expr (word) ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
@@ -1010,14 +1187,14 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
TODO("inplace variable word divide")
|
||||
TODO("inplace variable word divide ${target.position}")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
val register = value.register!!
|
||||
require(register.isWord())
|
||||
TODO("inplace register word divide")
|
||||
TODO("inplace register word divide ${target.position}")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word)")
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word) ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
@@ -1039,13 +1216,53 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
ldy $zpPtrVar+1
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> TODO("variable - / float")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression - / float")
|
||||
SourceStorageKind.REGISTER -> TODO("register - / float")
|
||||
SourceStorageKind.VARIABLE -> TODO("variable - / float ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> TODO("expression - / float ${target.position}")
|
||||
SourceStorageKind.REGISTER -> TODO("register - / float ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteInc(target: PtrTarget, amount: Int) {
|
||||
require(amount==1 || amount==2)
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda ($zpPtrVar)")
|
||||
repeat(amount) {
|
||||
asmgen.out(" inc a")
|
||||
}
|
||||
asmgen.out(" sta ($zpPtrVar)")
|
||||
}
|
||||
else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
clc
|
||||
adc #$amount
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteDec(target: PtrTarget, amount: Int) {
|
||||
require(amount==1 || amount==2)
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda ($zpPtrVar)")
|
||||
repeat(amount) {
|
||||
asmgen.out(" dec a")
|
||||
}
|
||||
asmgen.out(" sta ($zpPtrVar)")
|
||||
}
|
||||
else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
sec
|
||||
sbc #$amount
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteAdd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
@@ -1181,7 +1398,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
eor ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ byte")
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ byte ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
@@ -1225,51 +1442,225 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
eor ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ word")
|
||||
SourceStorageKind.REGISTER -> TODO("register ^ word ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteOr(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
ora #$number
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora #$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val varname = value.asmVarname
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
ora $varname
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register | byte ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordOr(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora #<$number
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
ora #>$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname
|
||||
sta ($zpPtrVar),y
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
txa
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register | word ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteAnd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
and #$number
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and #$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val varname = value.asmVarname
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
and $varname
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and $varname
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register & byte ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordAnd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and #<$number
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
and #>$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and $varname
|
||||
sta ($zpPtrVar),y
|
||||
lda ($zpPtrVar),y
|
||||
and $varname+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
txa
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register & word ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
|
||||
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
|
||||
val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
|
||||
assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
|
||||
when {
|
||||
target.datatype.isByteOrBool -> {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_PTR),y""")
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
}
|
||||
target.datatype.isWord || target.datatype.isPointer -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_PTR),y
|
||||
tax
|
||||
lda (P8ZP_SCRATCH_PTR)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_PTR),y
|
||||
tax
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_PTR),y""")
|
||||
asmgen.assignRegister(RegisterOrPair.AX, target)
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
TODO("assign long from pointer to $target ${target.position}")
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
// TODO optimize the float copying to avoid having to go through FAC1
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_PTR
|
||||
ldy P8ZP_SCRATCH_PTR+1
|
||||
jsr floats.MOVFM""")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt ${target.datatype}")
|
||||
}
|
||||
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
|
||||
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
|
||||
// when {
|
||||
// target.datatype.isByteOrBool -> {
|
||||
// asmgen.out("""
|
||||
// ldy #0
|
||||
// lda (P8ZP_SCRATCH_PTR),y""")
|
||||
// asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
// }
|
||||
// target.datatype.isWord || target.datatype.isPointer -> {
|
||||
// if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
// asmgen.out("""
|
||||
// ldy #1
|
||||
// lda (P8ZP_SCRATCH_PTR),y
|
||||
// tax
|
||||
// lda (P8ZP_SCRATCH_PTR)""")
|
||||
// else
|
||||
// asmgen.out("""
|
||||
// ldy #1
|
||||
// lda (P8ZP_SCRATCH_PTR),y
|
||||
// tax
|
||||
// dey
|
||||
// lda (P8ZP_SCRATCH_PTR),y""")
|
||||
// asmgen.assignRegister(RegisterOrPair.AX, target)
|
||||
// }
|
||||
// target.datatype.isLong -> {
|
||||
// TODO("assign long from pointer to $target ${target.position}")
|
||||
// }
|
||||
// target.datatype.isFloat -> {
|
||||
// // TODO optimize the float copying to avoid having to go through FAC1
|
||||
// asmgen.out("""
|
||||
// lda P8ZP_SCRATCH_PTR
|
||||
// ldy P8ZP_SCRATCH_PTR+1
|
||||
// jsr floats.MOVFM""")
|
||||
// asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
// }
|
||||
// else -> throw AssemblyError("weird dt ${target.datatype}")
|
||||
// }
|
||||
}
|
||||
|
||||
private fun saveOnStack(regs: RegisterOrPair) {
|
||||
@@ -1300,6 +1691,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
lda $regname+1
|
||||
pha""")
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
TODO("save on stack long register pair")
|
||||
}
|
||||
else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false)
|
||||
}
|
||||
}
|
||||
@@ -1332,6 +1726,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
pla
|
||||
sta $regname""")
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
TODO("restore from stack long register")
|
||||
}
|
||||
else -> asmgen.restoreRegisterStack(regs.asCpuRegister(), false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,14 @@ internal object DummyMemsizer : IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.LONG -> numElements*4
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class TestCodegen: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
@@ -76,6 +76,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||
in combinedLongRegisters -> {
|
||||
require(returns.type.isLong)
|
||||
val startreg = returns.register.registerOrPair!!.startregname()
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.LONG, reg1=regNum, labelSymbol = "cx16.${startreg}"), null)
|
||||
}
|
||||
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
null -> if(returns.register.statusflag!=null)
|
||||
@@ -137,63 +142,75 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
|
||||
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
|
||||
val oldvalueReg = codeGen.registers.next(targetDt)
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||
operandTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
|
||||
if((augAssign.operator=="+=" || augAssign.operator=="-=") && value.asConstInteger()==1 || value.asConstInteger()==2) {
|
||||
// INC/DEC optimization instead of ADD/SUB
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
val instr = if(augAssign.operator=="+=") Opcode.INC else Opcode.DEC
|
||||
repeat(value.asConstInteger()!!) {
|
||||
addInstr(inplaceInstrs, IRInstruction(instr, targetDt, reg1 = oldvalueReg), null)
|
||||
}
|
||||
|
||||
} else {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"&=" -> 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)
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||
operandTr = expressionEval.translateExpression(value)
|
||||
// note: the instructions to load the value will be placed after the LOADFIELD instruction so that later optimizations about what modification is actually done, are easier
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
}
|
||||
"and=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
|
||||
} else {
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"&=" -> 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)
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"and=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,6 +239,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun loadfield(
|
||||
inplaceInstrs: MutableList<IRCodeChunkBase>,
|
||||
addressReg: Int,
|
||||
fieldOffset: UByte,
|
||||
targetDt: IRDataType,
|
||||
oldvalueReg: Int
|
||||
) {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
if (fieldOffset > 0u)
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()),
|
||||
null
|
||||
)
|
||||
else
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg),
|
||||
null
|
||||
)
|
||||
} else {
|
||||
if (fieldOffset > 0u)
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()),
|
||||
null
|
||||
)
|
||||
else
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg),
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
@@ -415,10 +468,6 @@ 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.
|
||||
val targetIdent = assignment.target.identifier
|
||||
val targetMemory = assignment.target.memory
|
||||
val targetArray = assignment.target.array
|
||||
val targetPointerDeref = assignment.target.pointerDeref
|
||||
val valueDt = irType(assignment.value.type)
|
||||
val targetDt = irType(assignment.target.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -459,98 +508,104 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
}
|
||||
|
||||
if(targetIdent!=null) {
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||
} else {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
require(valueFpRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
else {
|
||||
require(valueRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
}
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
}
|
||||
else if(targetArray!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(targetArray.type, null)
|
||||
val variable = targetArray.variable
|
||||
if(variable==null)
|
||||
translateRegularAssignPointerIndexed(result, targetArray.pointerderef!!, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else if(variable.type.isPointer)
|
||||
assignToIndexedSimplePointer(result, variable, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
return result
|
||||
}
|
||||
else if(targetMemory!=null) {
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||
if(zero) {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val constAddress = targetMemory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return result
|
||||
with(assignment.target) {
|
||||
when {
|
||||
identifier != null -> {
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = identifier!!.name)
|
||||
} else {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
require(valueFpRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = identifier!!.name)
|
||||
}
|
||||
else {
|
||||
require(valueRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = identifier!!.name)
|
||||
}
|
||||
}
|
||||
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)
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
}
|
||||
memory != null -> {
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${memory!!.position}"}
|
||||
if(zero) {
|
||||
if(memory!!.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (memory!!.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory!!.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val constAddress = memory!!.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = memory!!.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
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)
|
||||
val addressReg = tr.resultReg
|
||||
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)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
array != null -> {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array!!.type, null)
|
||||
val variable = array!!.variable
|
||||
if(variable==null)
|
||||
translateRegularAssignPointerIndexed(result, array!!.pointerderef!!, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else if(variable.type.isPointer)
|
||||
assignToIndexedSimplePointer(result, variable, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
return result
|
||||
}
|
||||
pointerDeref != null -> {
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, pointerDeref!!)
|
||||
val actualValueReg = if(pointerDeref!!.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, pointerDeref!!.type, zero, actualValueReg)
|
||||
return result
|
||||
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
else if(targetPointerDeref!=null) {
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
|
||||
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
|
||||
return result
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
|
||||
private fun assignToIndexedSimplePointer(
|
||||
@@ -647,9 +702,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
|
||||
@@ -662,9 +717,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
|
||||
@@ -914,7 +969,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 /"")
|
||||
@@ -1054,14 +1109,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place -"")
|
||||
|
||||
val constValue = (operand as? PtNumber)?.number
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol), null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
@@ -1069,16 +1124,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol), null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol), null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
@@ -1086,8 +1139,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1153,9 +1205,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return null // TODO("optimized memory in-place +"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constValue = (operand as? PtNumber)?.number
|
||||
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
@@ -1170,7 +1223,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
@@ -1259,11 +1312,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)
|
||||
@@ -1321,11 +1370,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
|
||||
@@ -1380,7 +1425,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 %"")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
@@ -12,7 +14,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return when(call.name) {
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
|
||||
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
|
||||
"cmp" -> funcCmp(call)
|
||||
"sgn" -> funcSgn(call)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||
@@ -22,25 +24,30 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"callfar" -> funcCallfar(call)
|
||||
"callfar2" -> funcCallfar2(call)
|
||||
"call" -> funcCall(call)
|
||||
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
|
||||
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
"msw" -> funcMsw(call)
|
||||
"lsw" -> funcLsw(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)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekl" -> funcPeek(call, IRDataType.LONG)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebool" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokel" -> funcPoke(call, IRDataType.LONG)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
"mkword" -> funcMkword(call)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
|
||||
"mklong", "mklong2" -> funcMklong(call)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(call)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(call)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(call)
|
||||
"setlsb" -> funcSetLsbMsb(call, false)
|
||||
"setmsb" -> funcSetLsbMsb(call, true)
|
||||
"rol" -> funcRolRor(call)
|
||||
@@ -52,6 +59,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -201,6 +209,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.registers.next(IRDataType.LONG)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.LONG, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.LONG, reg1=tr.resultReg)
|
||||
}
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.LONG, tr.resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
||||
@@ -213,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)
|
||||
@@ -250,6 +269,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
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)
|
||||
@@ -280,6 +307,52 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMklong(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultReg = codeGen.registers.next(IRDataType.LONG)
|
||||
if(call.args.size==2) {
|
||||
// mklong2(word, word)
|
||||
if((call.args[0] as? PtNumber)?.number == 0.0) {
|
||||
// msw is 0, use EXT
|
||||
val lswTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, lswTr, lswTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = lswTr.resultReg), null)
|
||||
} else {
|
||||
val mswTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, mswTr, mswTr.resultReg, -1)
|
||||
val lswTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, lswTr, lswTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswTr.resultReg, reg3 = lswTr.resultReg), null)
|
||||
}
|
||||
} else {
|
||||
// mklong(msb, b3, b2, lsb)
|
||||
if((call.args[0] as? PtNumber)?.number == 0.0 && (call.args[1] as? PtNumber)?.number == 0.0 && (call.args[2] as? PtNumber)?.number == 0.0) {
|
||||
// use EXT.b + EXT.w
|
||||
val lsbTr = exprGen.translateExpression(call.args[3])
|
||||
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||
val wordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=wordReg, reg2 = lsbTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = wordReg), null)
|
||||
} else {
|
||||
val msbTr = exprGen.translateExpression(call.args[0])
|
||||
val b2Tr = exprGen.translateExpression(call.args[1])
|
||||
val b1Tr = exprGen.translateExpression(call.args[2])
|
||||
val lsbTr = exprGen.translateExpression(call.args[3])
|
||||
addToResult(result, msbTr, msbTr.resultReg, -1)
|
||||
addToResult(result, b2Tr, b2Tr.resultReg, -1)
|
||||
addToResult(result, b1Tr, b1Tr.resultReg, -1)
|
||||
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||
val lswReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val mswReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=mswReg, reg2 = msbTr.resultReg, reg3 = b2Tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=lswReg, reg2 = b1Tr.resultReg, reg3 = lsbTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswReg, reg3 = lswReg), null)
|
||||
}
|
||||
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.LONG, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val type = irType(call.type)
|
||||
@@ -304,6 +377,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
|
||||
BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
|
||||
BaseDataType.WORD -> IMSyscall.CLAMP_WORD
|
||||
BaseDataType.LONG -> IMSyscall.CLAMP_LONG
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
result += codeGen.makeSyscall(syscall, listOf(
|
||||
@@ -500,7 +574,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabBlockName.memory_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -508,30 +582,54 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val labelname = SymbolTable.labelnameForStructInstance(call)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = labelname)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "${StStructInstanceBlockName}.$labelname")
|
||||
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)
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
private fun funcLsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
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, 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)
|
||||
}
|
||||
|
||||
private fun funcMsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.MSIGW, IRDataType.LONG, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arg = call.args[0]
|
||||
|
||||
@@ -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)
|
||||
@@ -446,7 +446,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else (it as PtNumber).number.toInt()
|
||||
}
|
||||
when {
|
||||
elementDt.isIntegerOrBool -> {
|
||||
elementDt.isWordOrByteOrBool -> {
|
||||
if (elementDt.isByteOrBool) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
|
||||
if (elementDt.isWord) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
|
||||
val gottemLabel = codeGen.createLabelName()
|
||||
@@ -686,6 +686,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isLong -> {
|
||||
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 {
|
||||
@@ -703,7 +707,11 @@ 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.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -719,7 +727,11 @@ 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.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
@@ -743,6 +755,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
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)
|
||||
@@ -768,6 +784,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.UWORD -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
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)
|
||||
@@ -775,6 +795,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
when(valueDt.base) {
|
||||
BaseDataType.UBYTE, BaseDataType.BOOL -> {
|
||||
// ubyte to long: double sign extend
|
||||
val wordreg = codeGen.registers.next(IRDataType.WORD)
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
// byte to long: double sign extend
|
||||
val wordreg = codeGen.registers.next(IRDataType.WORD)
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
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.POINTER -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
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 $valueDt to long ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultFpReg2 = codeGen.registers.next(IRDataType.FLOAT)
|
||||
when(valueDt.base) {
|
||||
@@ -799,7 +846,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
// no further conversion required, pointers are all just uwords
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type} ${cast.position}")
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
@@ -1137,7 +1184,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegisters)
|
||||
)
|
||||
}
|
||||
else TODO("extsub with banked address got called ${callTarget.name}")
|
||||
else TODO("extsub with banked address got called ${callTarget.name} ${fcall.position}")
|
||||
}
|
||||
addInstr(result, call, null)
|
||||
val resultRegs = returnRegisters.filter{it.dt!=IRDataType.FLOAT}.map{it.registerNum}
|
||||
@@ -1715,9 +1762,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
result += pointerTr.chunks
|
||||
pointerReg = pointerTr.resultReg
|
||||
} else if(left.pointerderef!=null) {
|
||||
TODO("get pointer from deref $left")
|
||||
TODO("get pointer from deref $left ${left.position}")
|
||||
} else {
|
||||
throw AssemblyError("weird arrayindexer $left")
|
||||
throw AssemblyError("weird arrayindexer $left ${left.position}")
|
||||
}
|
||||
val struct = left.type.subType!! as StStruct
|
||||
val constindex = left.index as? PtNumber
|
||||
|
||||
@@ -47,6 +47,7 @@ class IRCodeGen(
|
||||
ensureFirstChunkLabels(irProg)
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
irProg.splitSSAchunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
@@ -271,7 +272,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 }
|
||||
@@ -1141,7 +1142,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}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1859,7 +1860,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) {
|
||||
@@ -1911,7 +1912,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
|
||||
@@ -1975,12 +1976,17 @@ class IRCodeGen(
|
||||
in Cx16VirtualRegisters -> {
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
require(paramDt==IRDataType.LONG)
|
||||
val startreg = registerOrFlag.registerOrPair!!.startregname()
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${startreg}")
|
||||
}
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
else -> throw AssemblyError("unsupported register arg $registerOrFlag")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
@@ -161,10 +161,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(chunk.label!=null)
|
||||
return false
|
||||
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
|
||||
// if the previous chunk doesn't end in a SSA branching instruction, flow continues into the next chunk, so they may be joined
|
||||
val lastInstruction = previous.instructions.lastOrNull()
|
||||
if(lastInstruction!=null)
|
||||
return lastInstruction.opcode !in OpcodesThatJump
|
||||
return lastInstruction.opcode !in OpcodesThatEndSSAblock
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -300,16 +300,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
// remove useless RETURN
|
||||
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode in OpcodesThatJump) {
|
||||
if(previous.opcode in OpcodesThatBranchUnconditionally) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
// replace subsequent opcodes that jump by just the first
|
||||
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
|
||||
if(idx>0 && (ins.opcode in OpcodesThatBranchUnconditionally)) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode in OpcodesThatJump) {
|
||||
if(previous.opcode in OpcodesThatBranchUnconditionally) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
@@ -362,7 +362,7 @@ jump p8_label_gen_2
|
||||
*/
|
||||
var changed=false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>=2 && ins.opcode in OpcodesThatJump) {
|
||||
if(idx>=2 && ins.opcode in OpcodesThatBranchUnconditionally) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
val previous2 = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
|
||||
@@ -415,13 +415,19 @@ jump p8_label_gen_2
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
-1 if ins.type == IRDataType.LONG -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode.OR -> {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) || (ins.immediate == 65535 && ins.type == IRDataType.WORD)) {
|
||||
} else if ((ins.immediate == 255 && ins.type == IRDataType.BYTE) ||
|
||||
(ins.immediate == 65535 && ins.type == IRDataType.WORD) ||
|
||||
(ins.immediate == -1 && ins.type == IRDataType.LONG)) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = ins.immediate)
|
||||
changed = true
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ internal class RegisterPool {
|
||||
// everything from 99000 onwards is reserved for special purposes:
|
||||
// 99000 - 99099 : WORD registers for syscall arguments and response value(s)
|
||||
// 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
|
||||
// 99200 - 99299 : LONG registers for syscall arguments and response value(s)
|
||||
|
||||
private var nextRegister: Int=1
|
||||
private val registerTypes: MutableMap<Int, IRDataType> = mutableMapOf()
|
||||
@@ -18,6 +19,8 @@ internal class RegisterPool {
|
||||
registerTypes[i] = IRDataType.WORD
|
||||
for(i in 99100..99199)
|
||||
registerTypes[i] = IRDataType.BYTE
|
||||
for(i in 99200..99299)
|
||||
registerTypes[i] = IRDataType.LONG
|
||||
}
|
||||
|
||||
fun next(type: IRDataType): Int {
|
||||
|
||||
@@ -47,10 +47,16 @@ private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement {
|
||||
return if (elt.boolean != null)
|
||||
IRStArrayElement(elt.boolean, null, null)
|
||||
else if(elt.number!=null)
|
||||
IRStArrayElement(null, elt.number, null)
|
||||
else {
|
||||
val symbol = elt.addressOfSymbol ?: (StStructInstanceBlockName + "." + (elt.structInstance ?: elt.structInstanceUninitialized))
|
||||
IRStArrayElement(null, null, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
@@ -72,7 +78,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
val newArray = mutableListOf<IRStArrayElement>()
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?:
|
||||
throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
@@ -129,11 +136,11 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
private fun convert(mem: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in mem.name)
|
||||
IRStMemorySlab(mem.name, mem.size, mem.align)
|
||||
else
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("$StMemorySlabBlockName.${mem.name}", mem.size, mem.align)
|
||||
}
|
||||
|
||||
|
||||
@@ -142,8 +149,8 @@ private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType,
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
return if('.' in instance.name)
|
||||
IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
else
|
||||
IRStStructInstance("${StStructInstanceBlockName}.${instance.name}", instance.structName, values, instance.size)
|
||||
}
|
||||
|
||||
|
||||
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?
|
||||
@@ -10,12 +10,14 @@ internal object DummyMemsizer : IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.LONG -> numElements*4
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
@@ -204,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
|
||||
}
|
||||
})
|
||||
@@ -25,6 +25,7 @@ class TestVmCodeGen: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
@@ -340,7 +341,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks.size shouldBe 2
|
||||
}
|
||||
|
||||
test("integer comparison expressions against zero") {
|
||||
@@ -536,7 +537,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks.size shouldBe 2
|
||||
}
|
||||
|
||||
test("extsub allowed in ir-codegen") {
|
||||
|
||||
@@ -39,7 +39,10 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
// see if LONG values may be reduced to something smaller
|
||||
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
|
||||
if(smaller.type!=BaseDataType.LONG) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
|
||||
if(parent !is Assignment || !parent.target.inferType(program).isLong) {
|
||||
// do NOT reduce the type if the target of the assignment is a long
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +88,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
* (X + c1) - c2 -> X + (c1-c2)
|
||||
*/
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
@@ -344,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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,7 +443,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val numval = decl.value as? NumericLiteral
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(valueDt issimpletype BaseDataType.LONG) {
|
||||
if(valueDt issimpletype BaseDataType.LONG || decl.datatype.isLong) {
|
||||
return noModifications // this is handled in the numericalvalue case
|
||||
}
|
||||
if(!(valueDt istype decl.datatype)) {
|
||||
|
||||
@@ -26,19 +26,15 @@ class VarConstantValueTypeAdjuster(
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(decl.parent is AnonymousScope)
|
||||
throw FatalAstException("vardecl may no longer occur in anonymousscope")
|
||||
throw FatalAstException("vardecl may no longer occur in anonymousscope ${decl.position}")
|
||||
|
||||
try {
|
||||
val declConstValue = decl.value?.constValue(program)
|
||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.type != decl.datatype.base) {
|
||||
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
|
||||
// avoid silent float roundings
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
}
|
||||
val declConstValue = decl.value?.constValue(program)
|
||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.type != decl.datatype.base) {
|
||||
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
|
||||
// avoid silent float roundings
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
// replace variables by constants, if possible
|
||||
@@ -173,7 +169,12 @@ class VarConstantValueTypeAdjuster(
|
||||
val replaceFunc = if(t1.isBytes) {
|
||||
if(t1 issimpletype BaseDataType.BYTE) "clamp__byte" else "clamp__ubyte"
|
||||
} else if(t1.isInteger) {
|
||||
if(t1 issimpletype BaseDataType.WORD) "clamp__word" else "clamp__uword"
|
||||
when {
|
||||
t1 issimpletype BaseDataType.WORD -> "clamp__word"
|
||||
t1 issimpletype BaseDataType.UWORD -> "clamp__uword"
|
||||
t1 issimpletype BaseDataType.LONG -> "clamp__long"
|
||||
else -> throw FatalAstException("clamp type")
|
||||
}
|
||||
} else {
|
||||
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
|
||||
return noModifications
|
||||
@@ -195,10 +196,12 @@ class VarConstantValueTypeAdjuster(
|
||||
else
|
||||
"${funcName}__ubyte"
|
||||
} else if(t1.isInteger && t2.isInteger) {
|
||||
replaceFunc = if(t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD)
|
||||
"${funcName}__word"
|
||||
else
|
||||
"${funcName}__uword"
|
||||
replaceFunc = when {
|
||||
t1 issimpletype BaseDataType.LONG || t2 issimpletype BaseDataType.LONG -> "${funcName}__long"
|
||||
t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD -> "${funcName}__word"
|
||||
t1 issimpletype BaseDataType.UWORD || t2 issimpletype BaseDataType.UWORD -> "${funcName}__uword"
|
||||
else -> throw FatalAstException("min/max type")
|
||||
}
|
||||
} else if(t1.isNumeric && t2.isNumeric) {
|
||||
errors.err("min/max not supported for floats", functionCallExpr.position)
|
||||
return noModifications
|
||||
@@ -218,6 +221,7 @@ class VarConstantValueTypeAdjuster(
|
||||
val replaceFunc = when {
|
||||
dt.isSignedByte -> "abs__byte"
|
||||
dt.isSignedWord -> "abs__word"
|
||||
dt.isLong -> "abs__long"
|
||||
dt.isFloat -> "abs__float"
|
||||
dt.isUnsignedByte || dt.isUnsignedWord -> {
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
@@ -250,6 +254,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
|
||||
}
|
||||
|
||||
@@ -313,41 +326,36 @@ internal class ConstantIdentifierReplacer(
|
||||
if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
|
||||
return noModifications
|
||||
|
||||
try {
|
||||
val cval = identifier.constValue(program) ?: return noModifications
|
||||
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||
if(arrayIdx!=null && cval.type.isNumeric) {
|
||||
// special case when the identifier is used as a pointer var
|
||||
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
val cval = identifier.constValue(program) ?: return noModifications
|
||||
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||
if(arrayIdx!=null && cval.type.isNumeric) {
|
||||
// special case when the identifier is used as a pointer var
|
||||
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
when {
|
||||
cval.type.isNumericOrBool -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
}
|
||||
when {
|
||||
cval.type.isNumericOrBool -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
}
|
||||
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> return noModifications
|
||||
)
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
return noModifications
|
||||
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log2
|
||||
|
||||
|
||||
@@ -90,6 +91,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
val newExpr = applyAbsorptionLaws(expr)
|
||||
if(newExpr!=null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
@@ -244,15 +247,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,15 +273,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,19 +435,92 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
|
||||
// fun isPowerOfTwo(number: Double): Boolean {
|
||||
// val intValue = number.toInt()
|
||||
// return intValue > 0 && (intValue and (intValue - 1)) == 0
|
||||
// }
|
||||
|
||||
fun isFactorOf256(number: Double): Boolean {
|
||||
val intValue = number.toInt()
|
||||
return intValue >= 256 && (intValue and 0xFF) == 0
|
||||
}
|
||||
|
||||
if (leftDt.isUnsignedWord && rightVal!=null) {
|
||||
if (expr.operator == ">" && rightVal.number == 255.0 || expr.operator == ">=" && rightVal.number == 256.0) {
|
||||
// uword > 255 --> msb(value)!=0
|
||||
// uword >= 256 --> msb(value)!=0
|
||||
expr.operator = "!="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator==">=" && isFactorOf256(rightVal.number)) {
|
||||
// uword >= $xx00 --> msb(value)>=xx
|
||||
val msbFraction = floor(rightVal.number / 256.0)
|
||||
expr.operator = ">="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator==">" && isFactorOf256(rightVal.number+1)) {
|
||||
// uword > $xxFF --> msb(value)>xx
|
||||
val msbFraction = floor((rightVal.number) / 256.0)
|
||||
expr.operator = ">"
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator == "<" && rightVal.number == 256.0 || expr.operator == "<=" && rightVal.number == 255.0) {
|
||||
// uword < 256 --> msb(value)==0
|
||||
// uword <= 255 --> msb(value)==0
|
||||
expr.operator = "=="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator == "<" && isFactorOf256(rightVal.number)) {
|
||||
// uword < $xx00 --> msb(value)<xx
|
||||
val msbFraction = floor(rightVal.number / 256.0)
|
||||
expr.operator = "<"
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator=="<=" && isFactorOf256(rightVal.number+1)) {
|
||||
// uword <= $xxFF --> msb(value)<=xx
|
||||
val msbFraction = floor((rightVal.number) / 256.0)
|
||||
expr.operator = "<="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
if((arrayIndexedExpression.parent as? BinaryExpression)?.operator !=".") {
|
||||
val binexprParent = arrayIndexedExpression.parent as? BinaryExpression
|
||||
if(binexprParent?.operator!=".") {
|
||||
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef==null) {
|
||||
// possibly pointer[0].field --> pointer.field
|
||||
val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(target?.datatype?.isPointer==true) {
|
||||
val field = (binexprParent.right as? IdentifierReference)?.nameInSource
|
||||
if(field!=null) {
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource + field, false, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression.parent, deref, arrayIndexedExpression.parent.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val ptrDeref = arrayIndexedExpression.pointerderef
|
||||
|
||||
@@ -8,7 +8,7 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.compiler.CallGraph
|
||||
@@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) {
|
||||
if (block.name !in PROG8_CONTAINER_MODULES && "ignore_unused" !in block.options()) {
|
||||
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
// id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("io.github.goooler.shadow") version "8.1.8"
|
||||
// id("io.github.goooler.shadow") version "8.1.8"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
id("com.peterabeles.gversion") version "1.10.3"
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
bcd {
|
||||
; Decimal addition an subtraction routines.
|
||||
; For CPUs that support BCD mode (binary coded decimal) (not all 6502 variants support this mode...)
|
||||
; This is useful for example for counting decimal score in a game, to avoid costly conversion to a decimal display string. Just print the hex representation.
|
||||
|
||||
sub addb(byte a, byte b) -> byte {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addub(ubyte a, ubyte b) -> ubyte {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addw(word a, word b) -> word {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub adduw(uword a, uword b) -> uword {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addl(long a, long b) -> long {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
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
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subub(ubyte a, ubyte b) -> ubyte {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subuw(uword a, uword b) -> uword {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subl(long a, long b) -> long {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
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
|
||||
sed
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clearbcd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -506,6 +529,7 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -513,7 +537,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
@@ -545,6 +569,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
@@ -554,24 +579,34 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta c64.IREQMASK ; enable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
jsr sys.set_rasterline
|
||||
lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
|
||||
lda #<_raster_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -593,34 +628,46 @@ _raster_irq_handler
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
rts
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr sys.set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
; -- only set a new raster line for the raster IRQ
|
||||
%asm {{
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
lda c64.SCROLY
|
||||
and #%01111111
|
||||
cpy #0
|
||||
beq +
|
||||
ora #%10000000
|
||||
+ sta c64.SCROLY ; clear most significant bit of raster position
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
@@ -680,6 +727,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp c64.RASTER
|
||||
bne -
|
||||
bit c64.SCROLY
|
||||
bmi -
|
||||
rts
|
||||
_larger
|
||||
cmp c64.RASTER
|
||||
bne _larger
|
||||
bit c64.SCROLY
|
||||
bpl _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -989,6 +1056,48 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -1165,12 +1274,6 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
@@ -1199,6 +1302,11 @@ asmsub init_system() {
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
; basic is not banked in, adjust MEMTOP
|
||||
ldx #<$c000
|
||||
ldy #>$c000
|
||||
clc
|
||||
jsr cbm.MEMTOP
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
@@ -1220,6 +1328,10 @@ asmsub cleanup_at_exit() {
|
||||
sta $ff00 ; default bank 15
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
ldx #<$ff00
|
||||
ldy #>$ff00
|
||||
clc
|
||||
jsr cbm.MEMTOP ; adjust MEMTOP to original value again
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
lda _exitcode
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -132,6 +132,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -313,12 +336,13 @@ asmsub banks(ubyte banks @A) {
|
||||
%asm {{
|
||||
and #%00000111
|
||||
sta P8ZP_SCRATCH_REG
|
||||
php
|
||||
sei
|
||||
lda $01
|
||||
and #%11111000
|
||||
ora P8ZP_SCRATCH_REG
|
||||
sta $01
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -511,6 +535,7 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -518,7 +543,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
@@ -551,6 +576,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
@@ -560,24 +586,34 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
jsr sys.set_rasterline
|
||||
lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
|
||||
lda #<_raster_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -600,33 +636,46 @@ _raster_irq_handler
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
rts
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr sys.set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
; -- only set a new raster line for the raster IRQ
|
||||
%asm {{
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
lda c64.SCROLY
|
||||
and #%01111111
|
||||
cpy #0
|
||||
beq +
|
||||
ora #%10000000
|
||||
+ sta c64.SCROLY ; clear most significant bit of raster position
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
@@ -687,6 +736,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp c64.RASTER
|
||||
bne -
|
||||
bit c64.SCROLY
|
||||
bmi -
|
||||
rts
|
||||
_larger
|
||||
cmp c64.RASTER
|
||||
bne _larger
|
||||
bit c64.SCROLY
|
||||
bpl _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -995,6 +1064,49 @@ _no_msb_size
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -1172,12 +1284,6 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
+106
-12
@@ -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 {{
|
||||
@@ -155,6 +153,30 @@ asmsub str_uwhex (uword value @ AY) -> str @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub str_ulhex(long value) -> str {
|
||||
%asm {{
|
||||
lda value+3
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out
|
||||
sty string_out+1
|
||||
lda value+2
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+2
|
||||
sty string_out+3
|
||||
lda value+1
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+4
|
||||
sty string_out+5
|
||||
lda value
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+6
|
||||
sty string_out+7
|
||||
lda #0
|
||||
sta string_out+8
|
||||
}}
|
||||
return &string_out
|
||||
}
|
||||
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(X) -> str @AY {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
@@ -228,15 +250,91 @@ asmsub str_w (word value @ AY) clobbers(X) -> str @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub str_l (long value) -> str {
|
||||
; ---- convert the long value into decimal string form, without left padding 0s
|
||||
; 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
|
||||
ldx #0
|
||||
- lda string_out+1,x
|
||||
cmp #'0'
|
||||
bne +
|
||||
inx
|
||||
bne -
|
||||
+ cpx #10 ; just all 0? keep one
|
||||
bne +
|
||||
dex
|
||||
|
||||
+ ; 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
|
||||
|
||||
lda #<string_out
|
||||
ldy #>string_out
|
||||
rts
|
||||
|
||||
hex2dec:
|
||||
; converts 10 digits (32 bit values have max. 10 decimal digits)
|
||||
ldx #9+1
|
||||
l3 jsr div10
|
||||
ora #$30
|
||||
sta string_out,x
|
||||
dex
|
||||
bne l3
|
||||
rts
|
||||
|
||||
; divides a 32 bit value by 10
|
||||
; remainder is returned in akku
|
||||
div10
|
||||
ldy #32 ; 32 bits
|
||||
lda #0
|
||||
clc
|
||||
l4 rol
|
||||
cmp #10
|
||||
bcc skip
|
||||
sbc #10
|
||||
skip rol value
|
||||
rol value+1
|
||||
rol value+2
|
||||
rol value+3
|
||||
dey
|
||||
bpl l4
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- string conversion to numbers -----
|
||||
|
||||
asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
asmsub any2uword(str string @AY) -> uword @AY, ubyte @X {
|
||||
; -- parses a string into a 16 bit unsigned number. String may be in decimal, hex or binary format.
|
||||
; (the latter two require a $ or % prefix to be recognised)
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; returns amount of processed characters in A, and the parsed number will be in cx16.r15.
|
||||
; if the string was invalid, 0 will be returned in A.
|
||||
; returns the parsed number word in AY, and the number of processed characters (including the prefix symbol) in X.
|
||||
; if the string was invalid, 0 will be returned as count in X (and the word value in AY will be undefined).
|
||||
%asm {{
|
||||
pha
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -257,13 +355,7 @@ _hex pla
|
||||
_bin pla
|
||||
jsr bin2uword
|
||||
_result
|
||||
pha
|
||||
lda cx16.r15
|
||||
sta P8ZP_SCRATCH_B1 ; result value
|
||||
pla
|
||||
sta cx16.r15
|
||||
sty cx16.r15+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
ldx cx16.r15
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -767,4 +859,6 @@ output .fill 5 ; 0-terminated output buffer (to make printing easier)
|
||||
}}
|
||||
}
|
||||
|
||||
ubyte[16] @shared string_out ; internal result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -283,7 +283,7 @@ palette {
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias set_default = cx16.set_default_palette
|
||||
|
||||
; get the bank and address of the word-array containing the 256 default palette colors
|
||||
; get the bank and address (in A, and XY) of the word-array containing the 256 default palette colors
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias get_default = cx16.get_default_palette
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
; Simple routines to control sprites.
|
||||
|
||||
; They're not written for high performance, but for simplicity.
|
||||
; That's why they control 1 sprite at a time. \
|
||||
; That's why they control 1 sprite at a time.
|
||||
; The exceptions are pos_batch() and pos_batch_split(); these are quite efficient
|
||||
; to update sprite positions of multiple sprites in one call.
|
||||
|
||||
; HIGH PERFORMANCE sprite handling would probably have a copy of the sprite registers for each sprite instead,
|
||||
; and a unrolled loop that copies those into the VERA registers when needed.
|
||||
|
||||
; note: sprites z-order will be in front of all layers.
|
||||
; note: collision mask is not supported here yet.
|
||||
; note: "palette offset" is counted as 0-15 (vera multiplies the offset by 16 to get at the actual color index)
|
||||
|
||||
@@ -127,6 +127,28 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM_safe() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM_safe
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
stz cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -1454,22 +1476,6 @@ sub search_x16edit() -> ubyte {
|
||||
return 255
|
||||
}
|
||||
|
||||
asmsub cpu_is_65816() -> bool @A {
|
||||
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(str args_ptr, ubyte args_size) {
|
||||
; -- Set the inter-program arguments.
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
@@ -1576,6 +1582,7 @@ sys {
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -1585,7 +1592,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV+1
|
||||
lda #1
|
||||
tsb cx16.VERA_IEN ; enable the vsync irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_irq_handler
|
||||
@@ -1615,6 +1622,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda _orig_irqvec
|
||||
sta cbm.CINV
|
||||
@@ -1624,7 +1632,7 @@ asmsub restore_irq() clobbers(A) {
|
||||
and #%11110000 ; disable all Vera IRQs but the vsync
|
||||
ora #%00000001
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
.section BSS_NOCLEAR
|
||||
_orig_irqvec .word ?
|
||||
@@ -1636,9 +1644,10 @@ _orig_irqvec .word ?
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; Sets the handler for the LINE interrupt, and enable (only) that interrupt.
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
lda cx16.VERA_IEN
|
||||
@@ -1652,7 +1661,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -1668,15 +1677,32 @@ _raster_irq_handler
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
%asm {{
|
||||
php
|
||||
@@ -1732,13 +1758,15 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
rts
|
||||
|
||||
+ sei
|
||||
+ php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
plp
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- sei
|
||||
- php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
plp
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
@@ -1759,6 +1787,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp cx16.VERA_SCANLINE_L
|
||||
bne -
|
||||
bit cx16.VERA_IEN
|
||||
bvs -
|
||||
rts
|
||||
_larger
|
||||
cmp cx16.VERA_SCANLINE_L
|
||||
bne _larger
|
||||
bit cx16.VERA_IEN
|
||||
bvc _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -2093,6 +2141,47 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cpu_is_65816() -> bool @A {
|
||||
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
@@ -116,26 +116,29 @@ verafx {
|
||||
|
||||
|
||||
asmsub mult16(uword value1 @R0, uword value2 @R1) clobbers(X) -> uword @AY {
|
||||
; Returns the 16 bits unsigned result of R0*R1 in AY.
|
||||
; Returns the lower 16 bits unsigned result of R0*R1 in AY
|
||||
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
|
||||
; Verafx doesn't support unsigned values like this for full 32 bit result.
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda cx16.r0+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jsr verafx.muls
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
stx cx16.r0
|
||||
ldx P8ZP_SCRATCH_W1+1
|
||||
stx cx16.r0+1
|
||||
jmp muls16
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub muls16(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY {
|
||||
; Returns just the lower 16 bits signed result of the multiplication in cx16.AY.
|
||||
; Note: clobbers R0, R1, and VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
jsr muls
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
|
||||
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
|
||||
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> long @R0R1_32 {
|
||||
; Returns the 32 bits signed result in R0:R1 (lower word, upper word).
|
||||
; Vera Fx multiplication support only works on signed values!
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
@@ -171,12 +174,14 @@ verafx {
|
||||
stz cx16.VERA_DATA0 ; multiply and write out result
|
||||
lda #%00010001 ; $01 with Increment 1
|
||||
sta cx16.VERA_ADDR_H ; so we can read out the result
|
||||
lda cx16.VERA_DATA0 ; store the lower 16 bits of the result in AY
|
||||
lda cx16.VERA_DATA0 ; store the lower 16 bits of the result in R0
|
||||
ldy cx16.VERA_DATA0
|
||||
ldx cx16.VERA_DATA0 ; store the upper 16 bits of the result in R0
|
||||
stx cx16.r0s
|
||||
ldx cx16.VERA_DATA0
|
||||
stx cx16.r0s+1
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda cx16.VERA_DATA0 ; store the upper 16 bits of the result in R1
|
||||
ldy cx16.VERA_DATA0 ; store the upper 16 bits of the result in R1
|
||||
sta cx16.r1L
|
||||
sty cx16.r1H
|
||||
stz cx16.VERA_FX_CTRL ; Cache write disable
|
||||
stz cx16.VERA_FX_MULT ; $9F2C reset multiply bit
|
||||
stz cx16.VERA_CTRL ; reset DCSEL
|
||||
|
||||
@@ -220,6 +220,12 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
sub mul32(uword a, uword b) -> long {
|
||||
; return 32 bits result of a*b
|
||||
cx16.r2 = a*b
|
||||
return mklong2(mul16_last_upper(), cx16.r2)
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
cx16.r0L = 3 ; quadrant
|
||||
@@ -548,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()
|
||||
}
|
||||
@@ -602,19 +607,17 @@ log2_tab
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(^^ubyte data, uword length) -> long {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
; 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++
|
||||
}
|
||||
crc32_end()
|
||||
return crc32_end()
|
||||
}
|
||||
|
||||
sub crc32_start() {
|
||||
@@ -666,13 +669,12 @@ log2_tab
|
||||
; }
|
||||
}
|
||||
|
||||
sub crc32_end() {
|
||||
; finalize the "streaming" crc32
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
void crc32_end_result()
|
||||
sub crc32_end() -> long {
|
||||
; finalize the "streaming" crc32 and returns the result
|
||||
return crc32_end_result()
|
||||
}
|
||||
|
||||
asmsub crc32_end_result() -> uword @R15, uword @R14 {
|
||||
asmsub crc32_end_result() -> long @R14R15_32 {
|
||||
; finalize the "streaming" crc32
|
||||
; returns the result value in cx16.r15 (high word) and r14 (low word)
|
||||
%asm {{
|
||||
|
||||
@@ -81,6 +81,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub kbdbuf_clear() {
|
||||
; -- convenience helper routine to clear the keyboard buffer
|
||||
%asm {{
|
||||
@@ -509,6 +532,37 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -686,11 +740,6 @@ cx16 {
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
@@ -29,6 +29,36 @@ abs_w_into_AY .proc
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
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.r14L
|
||||
eor #$FF
|
||||
sta cx16.r14L
|
||||
lda cx16.r14H
|
||||
eor #$FF
|
||||
sta cx16.r14H
|
||||
lda cx16.r15L
|
||||
eor #$FF
|
||||
sta cx16.r15L
|
||||
lda cx16.r15H
|
||||
eor #$FF
|
||||
sta cx16.r15H
|
||||
; Add 1 to whole 32-bit value
|
||||
inc cx16.r14L
|
||||
bne +
|
||||
inc cx16.r14H
|
||||
bne +
|
||||
inc cx16.r15L
|
||||
bne +
|
||||
inc cx16.r15H
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
func_sign_b_into_A .proc
|
||||
cmp #0
|
||||
beq _zero
|
||||
@@ -59,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
|
||||
@@ -405,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
|
||||
@@ -414,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
|
||||
@@ -424,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
|
||||
@@ -436,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
|
||||
|
||||
@@ -449,3 +449,195 @@ _fullpage lda (P8ZP_SCRATCH_W1),y
|
||||
bne _fullpage
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
long_not_equals .proc
|
||||
; checks if the 32 bits long value pointed to by AY is NOT equal to the one pointed to by P8ZP_SCRATCH_W1
|
||||
; returns A=1 if NOT equals otherwise A=0
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #3
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _notequal
|
||||
dey
|
||||
bpl -
|
||||
lda #0
|
||||
rts
|
||||
_notequal lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
long_add_inplace .proc
|
||||
; long pointed to by AY += long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
clc
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_sub_inplace .proc
|
||||
; long pointed to by AY -= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sec
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_or_inplace .proc
|
||||
; long pointed to by AY OR= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_and_inplace .proc
|
||||
; long pointed to by AY AND= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_xor_inplace .proc
|
||||
; long pointed to by AY XOR= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_shiftleftX_inplace .proc
|
||||
; bit shift left X bits the long value pointed to by AY
|
||||
cpx #0
|
||||
beq _end
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
- ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
asl a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dex
|
||||
bne -
|
||||
_end rts
|
||||
.pend
|
||||
|
||||
long_shiftrightX_inplace .proc
|
||||
; bit shift right X bits the long value pointed to by AY
|
||||
cpx #0
|
||||
beq _end
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
- ldy #3
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
asl a ; save sign bit
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dex
|
||||
bne -
|
||||
_end rts
|
||||
.pend
|
||||
@@ -109,6 +109,12 @@ txt {
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_ulhex(long value, bool prefix) {
|
||||
if prefix
|
||||
cbm.CHROUT('$')
|
||||
print(conv.str_ulhex(value))
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
@@ -165,6 +171,11 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_l(long value) {
|
||||
; prints a 32 bit value to the screen
|
||||
print(conv.str_l(value))
|
||||
}
|
||||
|
||||
asmsub input_chars (^^ubyte buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
|
||||
; Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
bcd {
|
||||
; virtual machine does not support BCD
|
||||
}
|
||||
@@ -102,7 +102,7 @@ sub str_uwbin (uword value) -> str {
|
||||
}
|
||||
|
||||
sub str_uwhex (uword value) -> str {
|
||||
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
|
||||
; ---- convert the uword in hexadecimal string form (4 digits)
|
||||
ubyte bits = msb(value)
|
||||
string_out[0] = hex_digits[bits>>4]
|
||||
string_out[1] = hex_digits[bits&15]
|
||||
@@ -113,6 +113,26 @@ sub str_uwhex (uword value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_ulhex (long value) -> str {
|
||||
; ---- convert the long in hexadecimal string form (8 digits)
|
||||
uword upperw = msw(value)
|
||||
uword lowerw = lsw(value)
|
||||
ubyte bits = msb(upperw)
|
||||
string_out[0] = hex_digits[bits>>4]
|
||||
string_out[1] = hex_digits[bits&15]
|
||||
bits = lsb(upperw)
|
||||
string_out[2] = hex_digits[bits>>4]
|
||||
string_out[3] = hex_digits[bits&15]
|
||||
bits = msb(lowerw)
|
||||
string_out[4] = hex_digits[bits>>4]
|
||||
string_out[5] = hex_digits[bits&15]
|
||||
bits = lsb(lowerw)
|
||||
string_out[6] = hex_digits[bits>>4]
|
||||
string_out[7] = hex_digits[bits&15]
|
||||
string_out[8] = 0
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_uw0 (uword value) -> str {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
uword value2 = value/10
|
||||
@@ -141,7 +161,7 @@ sub str_uw (uword value) -> str {
|
||||
}
|
||||
|
||||
sub str_w (word value) -> str {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
; ---- convert the (signed) word into decimal string form, without left padding 0's
|
||||
^^ubyte out_ptr = &string_out
|
||||
if value<0 {
|
||||
@(out_ptr) = '-'
|
||||
@@ -152,6 +172,16 @@ sub str_w (word value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_l (long value) -> str {
|
||||
; ---- convert the (signed) long into decimal string form, without left padding 0's
|
||||
%ir {{
|
||||
loadm.l r99200,conv.str_l.value
|
||||
load.w r99000,conv.string_out
|
||||
syscall 60 (r99200.l, r99000.w) : r99000.w
|
||||
returnr.w r99000
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_str_uw(uword value, str out_ptr) {
|
||||
uword value2 = value/10
|
||||
ubyte digits = value-value2*10 as ubyte
|
||||
@@ -267,12 +297,15 @@ sub bin2uword(str string) -> uword {
|
||||
}
|
||||
}
|
||||
|
||||
sub any2uword(str string) -> uword {
|
||||
sub any2uword(str string) -> uword, ubyte {
|
||||
; -- convert any number string (any prefix allowed) to uword.
|
||||
; returns the parsed word value, and the number of processed characters (including the prefix symbol)
|
||||
ubyte length
|
||||
while string[length]!=0 length++
|
||||
when string[0] {
|
||||
'$' -> return hex2uword(string)
|
||||
'%' -> return bin2uword(string)
|
||||
else -> return str2uword(string)
|
||||
'$' -> return hex2uword(string), length
|
||||
'%' -> return bin2uword(string), length
|
||||
else -> return str2uword(string), length
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,6 +304,12 @@ math {
|
||||
}}
|
||||
}
|
||||
|
||||
sub mul32(uword a, uword b) -> long {
|
||||
; return 32 bits result of a*b
|
||||
cx16.r2 = a*b
|
||||
return mklong2(mul16_last_upper(), cx16.r2)
|
||||
}
|
||||
|
||||
sub diff(ubyte b1, ubyte b2) -> ubyte {
|
||||
if b1>b2
|
||||
return b1-b2
|
||||
@@ -353,10 +359,9 @@ math {
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(^^ubyte data, uword length) -> long {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
; Clobbers R12 through R15, returns the result in R0:R1
|
||||
; 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.
|
||||
crc32_start()
|
||||
cx16.r12 = data
|
||||
@@ -365,7 +370,7 @@ math {
|
||||
crc32_update(@(cx16.r12))
|
||||
cx16.r12++
|
||||
}
|
||||
crc32_end()
|
||||
return crc32_end()
|
||||
}
|
||||
|
||||
sub crc32_start() {
|
||||
@@ -391,11 +396,11 @@ math {
|
||||
}
|
||||
}
|
||||
|
||||
sub crc32_end() {
|
||||
; finalize the "streaming" crc32
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
sub crc32_end() -> long {
|
||||
; finalize the "streaming" crc32 and return the result in R0:R1
|
||||
cx16.r15 ^= $ffff
|
||||
cx16.r14 ^= $ffff
|
||||
return mklong2(cx16.r15, cx16.r14)
|
||||
}
|
||||
|
||||
; there's no crc32_end_result() here because IR cannot return multiple values yet
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -192,6 +192,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub pushl(long l) {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
loadm.l r99200,sys.pushl.l
|
||||
push.l r99200
|
||||
}}
|
||||
}
|
||||
|
||||
sub push_returnaddress(uword w) {
|
||||
; note: this actually doesn't do anything useful on the VM because the code execution doesn't use the simulated cpu stack
|
||||
%ir {{
|
||||
@@ -222,6 +230,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub popl() -> long {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
pop.l r99200
|
||||
returnr.l r99200
|
||||
}}
|
||||
}
|
||||
|
||||
sub read_flags() -> ubyte {
|
||||
; "simulate" the 6502 status register a little bit
|
||||
if_neg {
|
||||
@@ -244,6 +260,11 @@ sys {
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
@@ -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,7 +16,7 @@ sub width() -> ubyte {
|
||||
sub height() -> ubyte {
|
||||
%ir {{
|
||||
syscall 46 (): r99000.w
|
||||
msig.b r99100,r99000
|
||||
msigb.w r99100,r99000
|
||||
returnr.b r99100
|
||||
}}
|
||||
}
|
||||
@@ -136,6 +136,13 @@ sub print_uwhex (uword value, bool prefix) {
|
||||
print(conv.str_uwhex(value))
|
||||
}
|
||||
|
||||
sub print_ulhex (long value, bool prefix) {
|
||||
; ---- print the ulong in hexadecimal form (4 digits)
|
||||
if prefix
|
||||
chrout('$')
|
||||
print(conv.str_ulhex(value))
|
||||
}
|
||||
|
||||
sub print_uw0 (uword value) {
|
||||
; ---- print the uword value in decimal form, with left padding 0s (5 positions total)
|
||||
print(conv.str_uw0(value))
|
||||
@@ -151,6 +158,14 @@ sub print_w (word value) {
|
||||
print(conv.str_w(value))
|
||||
}
|
||||
|
||||
sub print_l (long value) {
|
||||
; ---- print the (signed) long in decimal form, without left padding 0's
|
||||
%ir {{
|
||||
loadm.l r99200,txt.print_l.value
|
||||
syscall 59 (r99200.l)
|
||||
}}
|
||||
}
|
||||
|
||||
sub input_chars (str buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
@@ -2,6 +2,7 @@ package prog8
|
||||
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.AstException
|
||||
import prog8.buildversion.VERSION
|
||||
import prog8.code.source.ImportFileSystem.expandTilde
|
||||
import prog8.code.target.CompilationTargets
|
||||
import prog8.code.target.Cx16Target
|
||||
@@ -12,13 +13,10 @@ import prog8.compiler.CompilerArguments
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.compileProgram
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardWatchEventKinds
|
||||
import java.nio.file.WatchKey
|
||||
import java.net.URI
|
||||
import java.nio.file.*
|
||||
import java.time.LocalDateTime
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isReadable
|
||||
import kotlin.io.path.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
@@ -46,6 +44,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
|
||||
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
|
||||
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
|
||||
val libSearch by cli.option(ArgType.String, fullName = "libsearch", description = "search for a regex pattern in the embedded library files")
|
||||
val libDump by cli.option(ArgType.String, fullName = "libdump", description = "dump all the embedded library files into the specified output directory")
|
||||
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
|
||||
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
|
||||
@@ -62,7 +62,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
|
||||
val dontSplitWordArrays by cli.option(ArgType.Boolean, fullName = "dontsplitarrays", description = "don't store any word array as split lsb/msb in memory, as if all of those have @nosplit")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
|
||||
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
|
||||
@@ -90,6 +89,16 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
if(quietAll!=true)
|
||||
banner()
|
||||
|
||||
if(libDump!=null) {
|
||||
scanLibraryFiles(libDump, null)
|
||||
return true
|
||||
}
|
||||
|
||||
if(libSearch!=null) {
|
||||
scanLibraryFiles(null, libSearch)
|
||||
return true
|
||||
}
|
||||
|
||||
val outputPath = pathFrom(outputDir)
|
||||
if(!outputPath.toFile().isDirectory) {
|
||||
System.err.println("Output path doesn't exist")
|
||||
@@ -193,7 +202,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
slabsHighBank,
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
dontSplitWordArrays == true,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
@@ -278,7 +286,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
slabsHighBank,
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
dontSplitWordArrays == true,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
@@ -363,3 +370,62 @@ fun runVm(irFilename: String, quiet: Boolean) {
|
||||
val vmdef = VMTarget()
|
||||
vmdef.launchEmulator(0, irFile, quiet)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun scanLibraryFiles(dump: String?, searchPattern: String?) {
|
||||
val library_prefix = "/prog8lib"
|
||||
|
||||
val dumpPath = if(dump!=null) pathFrom(dump) else null
|
||||
val pattern = searchPattern?.toRegex(RegexOption.IGNORE_CASE)
|
||||
if(pattern!=null) {
|
||||
println("You can also have a look in the documentation for the libraries at https://prog8.readthedocs.io/en/latest/libraries.html")
|
||||
println("The library source files are available in the Github repository at https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib")
|
||||
println("Searching for pattern '$pattern' in embedded library files.\n")
|
||||
}
|
||||
if(dumpPath!=null) {
|
||||
println("Dumping embedded library files into $dumpPath\n")
|
||||
dumpPath.createDirectories()
|
||||
val license = dumpPath / "${library_prefix.drop(1)}-$VERSION/LICENSE.txt"
|
||||
license.parent.createDirectories()
|
||||
license.writeText("These library files belong to the Prog8 compiler project, see https://github.com/irmen/prog8/\n" +
|
||||
"They are licensed under the GNU GPL 3.0 software license, see https://www.gnu.org/licenses/gpl.html\n")
|
||||
}
|
||||
|
||||
fun search(path: Path) {
|
||||
val hits = mutableListOf<String>()
|
||||
path.useLines { lines ->
|
||||
lines.forEachIndexed { index, line ->
|
||||
if (pattern!!.containsMatchIn(line)) {
|
||||
hits.add("${(index + 1).toString().padStart(6)}: ${line.trimStart()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
if(hits.isNotEmpty()) {
|
||||
println("Found in Library file '${path.absolutePathString().drop(library_prefix.length+1)}':")
|
||||
hits.forEach(::println)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
fun dump(path: Path) {
|
||||
val targetfolder = dumpPath!! / (path.first().pathString + "-$VERSION")
|
||||
val target = targetfolder / path.pathString.drop(library_prefix.length+1)
|
||||
target.parent.createDirectories()
|
||||
target.writeBytes(path.readBytes())
|
||||
}
|
||||
|
||||
// Get the location of the current JAR
|
||||
val jarUrl = object {}.javaClass.protectionDomain.codeSource.location
|
||||
FileSystems.newFileSystem(URI.create("jar:$jarUrl"), emptyMap<String, String>()).use { fs ->
|
||||
val dir = fs.getPath(library_prefix)
|
||||
Files.walk(dir).use { paths ->
|
||||
paths.filter { Files.isRegularFile(it) }.forEach { path ->
|
||||
if(pattern!=null)
|
||||
search(path)
|
||||
else
|
||||
dump(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
@@ -15,15 +16,20 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"abs" to ::builtinAbs,
|
||||
"len" to ::builtinLen,
|
||||
"sizeof" to ::builtinSizeof,
|
||||
"offsetof" to ::builtinOffsetof,
|
||||
"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) } },
|
||||
"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,
|
||||
"mklong2" to ::builtinMklong,
|
||||
"clamp__ubyte" to ::builtinClampUByte,
|
||||
"clamp__byte" to ::builtinClampByte,
|
||||
"clamp__uword" to ::builtinClampUWord,
|
||||
@@ -32,10 +38,12 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"min__byte" to ::builtinMinByte,
|
||||
"min__uword" to ::builtinMinUWord,
|
||||
"min__word" to ::builtinMinWord,
|
||||
"min__long" to ::builtinMinLong,
|
||||
"max__ubyte" to ::builtinMaxUByte,
|
||||
"max__byte" to ::builtinMaxByte,
|
||||
"max__uword" to ::builtinMaxUWord,
|
||||
"max__word" to ::builtinMaxWord
|
||||
"max__word" to ::builtinMaxWord,
|
||||
"max__long" to ::builtinMaxLong
|
||||
)
|
||||
|
||||
internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||
@@ -90,6 +98,25 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
else throw SyntaxError("abs requires one integer argument", position)
|
||||
}
|
||||
|
||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// 1 arg, "Struct.field"
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("offsetof requires one argument", position)
|
||||
val identifier = (args[0] as? IdentifierReference)?.nameInSource
|
||||
if(identifier==null || identifier.size<2)
|
||||
throw CannotEvaluateException("offsetof","argument should be an identifier of the form Struct.field")
|
||||
|
||||
val structname = identifier.dropLast(1)
|
||||
val fieldname = identifier.last()
|
||||
val struct = args[0].definingScope.lookup(structname) as? StructDecl
|
||||
if(struct==null)
|
||||
throw SyntaxError("cannot find struct '$structname'", args[0].position)
|
||||
val offset = struct.offsetof(fieldname, program.memsizer)
|
||||
if(offset==null)
|
||||
throw SyntaxError("no such field '${identifier.joinToString(".")}'", args[0].position)
|
||||
return NumericLiteral.optimalInteger(offset.toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// 1 arg, type = anything, result type = ubyte or uword
|
||||
if(args.size!=1)
|
||||
@@ -182,6 +209,29 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMklong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
when(args.size) {
|
||||
2 -> {
|
||||
val constMsw = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constLsw = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsw.number.toInt() shl 16) or constLsw.number.toInt()
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
4 -> {
|
||||
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constB2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constB1 = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constLsb = args[3].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsb.number.toInt() shl 24) or
|
||||
(constB2.number.toInt() shl 16) or
|
||||
(constB1.number.toInt() shl 8) or
|
||||
constLsb.number.toInt()
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
else -> throw SyntaxError("mkword requires msw and lsw, or msb,b2,b1,lsb arguments", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sgn requires one argument", position)
|
||||
@@ -225,6 +275,15 @@ private fun builtinMinUWord(args: List<Expression>, position: Position, program:
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMinLong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
@@ -261,6 +320,15 @@ private fun builtinMaxUWord(args: List<Expression>, position: Position, program:
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxLong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinClampUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
|
||||
@@ -5,6 +5,8 @@ import prog8.ast.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.buildversion.VERSION
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.printAst
|
||||
@@ -33,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>)
|
||||
|
||||
@@ -53,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,
|
||||
@@ -86,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 {
|
||||
@@ -118,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
|
||||
}
|
||||
@@ -179,14 +182,16 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
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
|
||||
}
|
||||
|
||||
if (args.printAst2) {
|
||||
@@ -204,6 +209,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
intermediateAst,
|
||||
symbolTable!!,
|
||||
args.errors,
|
||||
compilationOptions,
|
||||
program.generatedLabelSequenceNumber
|
||||
@@ -244,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) {
|
||||
@@ -450,7 +456,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
return CompilationOptions(
|
||||
outputType, launcherType,
|
||||
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, rombale,
|
||||
compTarget, 0u, 0xffffu
|
||||
compTarget, VERSION, 0u, 0xffffu
|
||||
)
|
||||
}
|
||||
|
||||
@@ -493,6 +499,8 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
}
|
||||
|
||||
removeUnusedCode(program, errors,compilerOptions)
|
||||
program.constantFold(errors, compilerOptions)
|
||||
|
||||
for(numCycles in 0..10000) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors)
|
||||
@@ -558,6 +566,7 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
||||
}
|
||||
|
||||
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
symbolTable: SymbolTable,
|
||||
errors: IErrorReporter,
|
||||
compilerOptions: CompilationOptions,
|
||||
lastGeneratedLabelSequenceNr: Int
|
||||
@@ -572,10 +581,6 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
else
|
||||
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.cpu}")
|
||||
|
||||
// need to make a new symboltable here to capture possible changes made by optimization steps performed earlier!
|
||||
val stMaker = SymbolTableMaker(program, compilerOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
|
||||
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
||||
errors.report()
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
checkLongType(identifier)
|
||||
val stmt = identifier.targetStatement(program.builtinFunctions)
|
||||
if(stmt==null) {
|
||||
if(identifier.parent is ArrayIndexedExpression) {
|
||||
@@ -241,6 +240,11 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
|
||||
}
|
||||
|
||||
BaseDataType.LONG -> {
|
||||
if(!iterableDt.elementType().isInteger)
|
||||
errors.err("long loop variable can only loop over integers", forLoop.position)
|
||||
}
|
||||
|
||||
BaseDataType.FLOAT -> {
|
||||
// Looping over float variables is very inefficient because the loopvar is going to
|
||||
// get copied over with new values all the time. We don't support this for now.
|
||||
@@ -363,10 +367,6 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(label)
|
||||
}
|
||||
|
||||
override fun visit(numLiteral: NumericLiteral) {
|
||||
checkLongType(numLiteral)
|
||||
}
|
||||
|
||||
private fun hasReturnOrExternalJumpOrRts(scope: IStatementContainer): Boolean {
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,7 +813,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
checkLongType(addressOf)
|
||||
val variable=addressOf.identifier?.targetVarDecl()
|
||||
if (variable!=null) {
|
||||
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
|
||||
@@ -862,8 +861,6 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl.names.size>1)
|
||||
throw InternalCompilerException("vardecls with multiple names should have been converted into individual vardecls")
|
||||
|
||||
if(decl.datatype.isLong && decl.type!=VarDeclType.CONST)
|
||||
errors.err("cannot use long type for variables; only for constants", decl.position)
|
||||
if(decl.type==VarDeclType.MEMORY) {
|
||||
if (decl.datatype.isString)
|
||||
errors.err("strings cannot be memory-mapped", decl.position)
|
||||
@@ -949,6 +946,9 @@ internal class AstChecker(private val program: Program,
|
||||
dt.isWordArray ->
|
||||
if(arraySize > 128)
|
||||
err("regular word array length must be 1-128, use split array to get to 256")
|
||||
dt.isLongArray ->
|
||||
if(arraySize > 64)
|
||||
err("long array length must be 1-64")
|
||||
dt.isFloatArray ->
|
||||
if(arraySize > 51)
|
||||
err("float array length must be 1-51")
|
||||
@@ -1052,6 +1052,10 @@ internal class AstChecker(private val program: Program,
|
||||
if (length == 0 || length > 128)
|
||||
err("regular word array length must be 1-128, use split array to get to 256")
|
||||
}
|
||||
decl.datatype.isLongArray -> {
|
||||
if (length == 0 || length > 64)
|
||||
err("long array length must be 1-64")
|
||||
}
|
||||
decl.datatype.isFloatArray -> {
|
||||
if (length == 0 || length > 51)
|
||||
err("float array length must be 1-51")
|
||||
@@ -1086,13 +1090,13 @@ 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) {
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
errors.err("cannot declare memory mapped struct instances, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
errors.err("struct instances cannot be declared as memory-mapped currently, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
else
|
||||
errors.err("struct instances cannot be declared directly, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
}
|
||||
@@ -1269,17 +1273,23 @@ internal class AstChecker(private val program: Program,
|
||||
val arrayspec = ArrayIndex.forArray(array)
|
||||
checkValueTypeAndRangeArray(array.type.getOrUndef(), arrayspec, array)
|
||||
} else {
|
||||
errors.err("undefined array type (multiple element types?)", array.position)
|
||||
errors.err("undefined array type (multiple or incompatible element types?)", array.position)
|
||||
}
|
||||
|
||||
if(array.parent is VarDecl) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || (it is FunctionCallExpression && it.target.targetStructDecl()!=null) }) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || it is StaticStructInitializer }) {
|
||||
errors.err("initialization value contains non-constant elements", array.value[0].position)
|
||||
}
|
||||
|
||||
if(array.value.any { it is FunctionCallExpression }) {
|
||||
errors.err("it is not yet possible to use struct initializations in an array, you have to do it one by one for now", array.value[0].position)
|
||||
// TODO this is because later in the simplified AST the allocate struct variable is still missing somehow
|
||||
val elementDt = (array.parent as VarDecl).datatype.elementType()
|
||||
if(elementDt.isPointer) {
|
||||
// all elements in the initializer array should be of the same element type
|
||||
array.value.forEach {
|
||||
val valueDt = it.inferType(program).getOrUndef()
|
||||
if(!valueDt.isUnsignedWord && valueDt != elementDt) {
|
||||
errors.err("struct initializer element has invalid type, expected $elementDt or uword but got $valueDt", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if(array.parent is ForLoop) {
|
||||
@@ -1323,7 +1333,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
checkLongType(expr)
|
||||
val dt = expr.expression.inferType(program).getOrUndef()
|
||||
if(!dt.isUndefined) {
|
||||
|
||||
@@ -1443,7 +1452,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) {
|
||||
@@ -1468,8 +1477,6 @@ internal class AstChecker(private val program: Program,
|
||||
return
|
||||
}
|
||||
|
||||
checkLongType(expr)
|
||||
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
if(!leftIDt.isKnown || !rightIDt.isKnown) {
|
||||
@@ -1515,7 +1522,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(rightDt.isWord) {
|
||||
val shift = expr.right.constValue(program)?.number?.toInt()
|
||||
if(shift==null || shift > 255) {
|
||||
errors.err("shift by a word value not supported, max is a byte", expr.position)
|
||||
errors.err("bit shift value can not be larger than a byte", expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1546,8 +1553,13 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("can only use string repeat with a constant number value", expr.left.position)
|
||||
} else if(leftDt.isBool && rightDt.isByte || leftDt.isByte && rightDt.isBool) {
|
||||
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt.isWord && rightDt.isByte)) {
|
||||
// exception allowed: shifting a word by a byte
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>")
|
||||
&& (leftDt.isByte && rightDt.isByte ||
|
||||
leftDt.isWord && rightDt.isByte ||
|
||||
leftDt.isWord && rightDt.isWord ||
|
||||
leftDt.isLong && rightDt.isByte ||
|
||||
leftDt.isLong && rightDt.isWord)) {
|
||||
// exception allowed: shifting a word by a byte, long by a word or byte
|
||||
} else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) {
|
||||
// exception allowed: bitwise operations with any integers
|
||||
} else if((leftDt.isUnsignedWord && rightDt.isString) || (leftDt.isString && rightDt.isUnsignedWord)) {
|
||||
@@ -1608,7 +1620,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
checkLongType(typecast)
|
||||
if(typecast.type.isPassByRef)
|
||||
errors.err("cannot type cast to string or array type", typecast.position)
|
||||
|
||||
@@ -1628,6 +1639,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)
|
||||
}
|
||||
|
||||
@@ -1665,7 +1680,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
checkLongType(functionCallExpr)
|
||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
@@ -1898,36 +1912,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(target is StructDecl) {
|
||||
// it's a static struct inializer, check the values
|
||||
if(args.isNotEmpty()) {
|
||||
args.forEach {
|
||||
if(it is IdentifierReference) {
|
||||
val target = it.targetVarDecl()
|
||||
if(target!=null && target.datatype.isPointer) {
|
||||
errors.err("a pointer variable cannot be used in a static initialization value because its value is only known at runtime (use 0 here, and assign it later manually)", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
|
||||
errors.err("initialization value contains non-constant elements", args[0].position)
|
||||
if (target.fields.size != args.size)
|
||||
errors.err("initialization value needs to have same number of values as the struct has fields, or be empty: expected ${target.fields.size} or 0, got ${args.size}", args[0].position)
|
||||
else
|
||||
target.fields.zip(args).withIndex().forEach { (index, fv) ->
|
||||
val (field, value) = fv
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt isNotAssignableTo field.first) {
|
||||
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO rest?
|
||||
}
|
||||
|
||||
args.forEach{
|
||||
checkLongType(it)
|
||||
if(it.inferType(program).isStructInstance)
|
||||
errors.err("structs can only be passed via a pointer", it.position)
|
||||
}
|
||||
@@ -1940,7 +1925,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
checkLongType(arrayIndexedExpression)
|
||||
val target = arrayIndexedExpression.plainarrayvar?.targetStatement(program.builtinFunctions)
|
||||
if(target is VarDecl) {
|
||||
if (!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
|
||||
@@ -1974,7 +1958,10 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
|
||||
}
|
||||
} else if(target!=null) {
|
||||
throw FatalAstException("target is not a variable")
|
||||
if(target is Subroutine || target is Label )
|
||||
errors.err("cannot array index a subroutine or label directly, use an intermediate uword pointer = &symbol instead", arrayIndexedExpression.position)
|
||||
else
|
||||
errors.err("indexed symbol is not a suitable variable", arrayIndexedExpression.position)
|
||||
}
|
||||
|
||||
if(arrayIndexedExpression.pointerderef!=null) {
|
||||
@@ -2078,6 +2065,8 @@ internal class AstChecker(private val program: Program,
|
||||
!iterableEltDt.isByte
|
||||
} else if (elementDt.isWords) {
|
||||
!iterableEltDt.isWord
|
||||
} else if (elementDt.isLong) {
|
||||
!iterableEltDt.isLong
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -2105,6 +2094,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))
|
||||
@@ -2179,17 +2174,8 @@ internal class AstChecker(private val program: Program,
|
||||
else
|
||||
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program) issimpletype BaseDataType.LONG) {
|
||||
if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) {
|
||||
if (expression.parent !is RepeatLoop) {
|
||||
if (errors.noErrorForLine(expression.position))
|
||||
errors.err("integer overflow", expression.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visit(deref)
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
|
||||
@@ -2282,6 +2268,20 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
return err("invalid word array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
targetDt.isPointerArray -> {
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
val maxLength = if(targetDt.isSplitWordArray) 256 else 128
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize>maxLength)
|
||||
return err("array length must be 1-$maxLength")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid array size, must be 1-$maxLength")
|
||||
}
|
||||
targetDt.isFloatArray -> {
|
||||
// value may be either a single float, or a float arraysize
|
||||
if(value.type istype targetDt) {
|
||||
@@ -2359,7 +2359,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(value.type==BaseDataType.FLOAT)
|
||||
err("integer value expected instead of float; possible loss of precision")
|
||||
val number=value.number
|
||||
if (number < -2147483647 || number > 2147483647)
|
||||
if (number < -2147483647.0 || number > 2147483647.0)
|
||||
return err("value '$number' out of range for long")
|
||||
}
|
||||
targetDt.isArray -> {
|
||||
@@ -2396,6 +2396,7 @@ internal class AstChecker(private val program: Program,
|
||||
else
|
||||
cast.valueOrZero().number
|
||||
}
|
||||
is StaticStructInitializer -> it.structname.hashCode() and 0xffff
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
@@ -2420,7 +2421,7 @@ internal class AstChecker(private val program: Program,
|
||||
else -> throw FatalAstException("invalid type $targetDt")
|
||||
}
|
||||
if (!correct) {
|
||||
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf })
|
||||
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf || it is StaticStructInitializer })
|
||||
errors.err("initialization value contains non-constant elements", value.value[0].position)
|
||||
else
|
||||
errors.err("array element out of range for type $targetDt", value.position)
|
||||
@@ -2470,7 +2471,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).
|
||||
@@ -2481,7 +2482,7 @@ internal class AstChecker(private val program: Program,
|
||||
else if (targetDatatype.isPointer) {
|
||||
if(sourceDatatype.isPointer) {
|
||||
if(!(sourceDatatype isAssignableTo targetDatatype))
|
||||
errors.err("cannot assign different pointer type, expected $targetDatatype got $sourceDatatype", position)
|
||||
errors.err("cannot assign different pointer type, expected $targetDatatype or uword but got $sourceDatatype", position)
|
||||
} else if(sourceDatatype.isString && targetDatatype.sub?.isByte==true) {
|
||||
// assigning a string to a byte pointer is allowed.
|
||||
} else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance)
|
||||
@@ -2512,11 +2513,40 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(deref: ArrayIndexedPtrDereference) {
|
||||
if(deref.parent is AssignTarget)
|
||||
errors.err("no support for assigning to a array indexed pointer target like this yet. Split the assignment statement by using an intermediate variable.", deref.position)
|
||||
else
|
||||
if(deref.parent !is AssignTarget)
|
||||
errors.err("no support for getting the target value of pointer array indexing like this yet. Split the expression by using an intermediate variable.", deref.position) // this may never occur anymore since more ArrayIndexedPtrDereference got rewritten
|
||||
|
||||
super.visit(deref)
|
||||
}
|
||||
|
||||
override fun visit(initializer: StaticStructInitializer) {
|
||||
val args = initializer.args
|
||||
if(args.isNotEmpty()) {
|
||||
args.forEach {
|
||||
if(it is IdentifierReference) {
|
||||
val target = it.targetVarDecl()
|
||||
if(target!=null && target.datatype.isPointer) {
|
||||
errors.err("a pointer variable cannot be used in a static initialization value because its value is only known at runtime (use 0 here, and assign it later manually)", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
|
||||
errors.err("initialization value contains non-constant elements", args[0].position)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
require(args.size==struct.fields.size)
|
||||
struct.fields.zip(args).withIndex().forEach { (index, fv) ->
|
||||
val (field, value) = fv
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt isNotAssignableTo field.first)
|
||||
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(initializer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||
|
||||
@@ -165,14 +165,23 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
super.visit(string)
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) = visitFunctionCall(functionCallExpr)
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
visitFunctionCall(functionCallExpr)
|
||||
super.visit(functionCallExpr)
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
visitFunctionCall(functionCallStatement)
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(initializer: StaticStructInitializer) {
|
||||
val fields = initializer.structname.targetStructDecl()!!.fields
|
||||
if(initializer.args.isNotEmpty() && initializer.args.size != fields.size) {
|
||||
val pos = (if(initializer.args.any()) initializer.args[0] else initializer).position
|
||||
invalidNumberOfArgsError(pos, initializer.args.size, fields.map { it.second }, true)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
if (initializer.args.isNotEmpty() && initializer.args.size != struct.fields.size) {
|
||||
val pos = (if (initializer.args.any()) initializer.args[0] else initializer).position
|
||||
invalidNumberOfArgsError(pos, initializer.args.size, struct.fields.map { it.second }, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +113,6 @@ class AstPreprocessor(val program: Program,
|
||||
val replacements = mutableListOf<IAstModification>()
|
||||
|
||||
for(decl in vars) {
|
||||
if(shouldUnSplitArray(decl))
|
||||
continue // unsplitting must be done first
|
||||
if(decl.type != VarDeclType.VAR) {
|
||||
movements.add(IAstModification.InsertFirst(decl, parentscope))
|
||||
replacements.add(IAstModification.Remove(decl, scope))
|
||||
@@ -174,6 +172,8 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if(expr.operator=="in") {
|
||||
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||
@@ -196,10 +196,6 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(shouldUnSplitArray(decl)) {
|
||||
return makeUnSplitArray(decl)
|
||||
}
|
||||
|
||||
// convert all antlr names to structs
|
||||
val antlrTypeName = decl.datatype.subTypeFromAntlr
|
||||
if(antlrTypeName!=null) {
|
||||
@@ -230,18 +226,6 @@ class AstPreprocessor(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun shouldUnSplitArray(decl: VarDecl): Boolean =
|
||||
options.dontSplitWordArrays && decl.datatype.isSplitWordArray
|
||||
|
||||
private fun makeUnSplitArray(decl: VarDecl): Iterable<IAstModification> {
|
||||
val splitDt = DataType.arrayFor(decl.datatype.sub!!, false)
|
||||
val newDecl = VarDecl(
|
||||
decl.type, decl.origin, splitDt, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, emptyList(),
|
||||
decl.value?.copy(), decl.sharedWithAsm, decl.alignment, false, decl.position
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(decl, newDecl, decl.parent))
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
@@ -267,7 +251,7 @@ class AstPreprocessor(val program: Program,
|
||||
.filter { it.name !in namesInSub && it.name !in existingAliases }
|
||||
.forEach {
|
||||
if (it.registerOrPair in Cx16VirtualRegisters) {
|
||||
if(it.type.isIntegerOrBool) {
|
||||
if(it.type.isWordOrByteOrBool || it.type.isPointer) {
|
||||
val mappedParamVar = VarDecl.fromParameter(it)
|
||||
mods += IAstModification.InsertFirst(mappedParamVar, subroutine)
|
||||
} else {
|
||||
|
||||
@@ -153,6 +153,8 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if (options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications
|
||||
|
||||
|
||||
@@ -79,18 +79,26 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
val (replaced, cast) = arg1.typecastTo(if(dt1.isUnsignedByte) BaseDataType.UWORD else BaseDataType.WORD, dt1, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg1, cast, functionCallStatement))
|
||||
} else {
|
||||
} else if(dt1.isWord) {
|
||||
if(dt2.isWord)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg2.typecastTo(if(dt2.isUnsignedByte) BaseDataType.UWORD else BaseDataType.WORD, dt2, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg2, cast, functionCallStatement))
|
||||
} else if(dt1.isLong) {
|
||||
if(dt2.isLong)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg2.typecastTo(BaseDataType.LONG, dt2, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg2, cast, functionCallStatement))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
val shifts = expr.right.constValue(program)
|
||||
if(shifts!=null) {
|
||||
|
||||
@@ -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)
|
||||
@@ -605,11 +617,15 @@ _after:
|
||||
}
|
||||
|
||||
val replacementScope = AnonymousScope(if(conditionVar==null)
|
||||
mutableListOf(ifSt, jumplistArray)
|
||||
mutableListOf(ifSt)
|
||||
else
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt, jumplistArray)
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt)
|
||||
, ongoto.position)
|
||||
return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
|
||||
val declscope = parent.definingScope
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ongoto, replacementScope, parent),
|
||||
IAstModification.InsertFirst(jumplistArray, declscope)
|
||||
)
|
||||
}
|
||||
|
||||
override fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
@@ -647,7 +663,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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -658,6 +674,61 @@ _after:
|
||||
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
// get rid of the ArrayIndexedPtrDereference AST node, replace it with other AST nodes that are equivalent
|
||||
|
||||
fun pokeFunc(dt: DataType): Pair<String, DataType?> {
|
||||
return when {
|
||||
dt.isBool -> "pokebool" to null
|
||||
dt.isUnsignedByte -> "poke" to null
|
||||
dt.isSignedByte -> "poke" to DataType.UBYTE
|
||||
dt.isUnsignedWord -> "pokew" to null
|
||||
dt.isSignedWord -> "pokew" to DataType.UWORD
|
||||
dt.isLong -> "pokel" to null
|
||||
dt.isFloat -> "pokef" to null
|
||||
else -> throw FatalAstException("can only deref a numeric or boolean pointer here")
|
||||
}
|
||||
}
|
||||
|
||||
if(parent is AssignTarget) {
|
||||
if(!deref.derefLast) {
|
||||
val assignment = parent.parent as Assignment
|
||||
val field = deref.chain.last()
|
||||
val ptr = deref.chain.dropLast(1)
|
||||
if(field.second==null && ptr.last().second!=null) {
|
||||
val ptrName = ptr.map { it.first }
|
||||
val ptrVar = deref.definingScope.lookup(ptrName) as? VarDecl
|
||||
if(ptrVar!=null && (ptrVar.datatype.isPointer || ptrVar.datatype.isPointerArray)) {
|
||||
val struct = ptrVar.datatype.subType!! as StructDecl
|
||||
val offsetNumber = NumericLiteral.optimalInteger(struct.offsetof(field.first, program.memsizer)!!.toInt(), deref.position)
|
||||
val pointerIdentifier = IdentifierReference(ptrName, deref.position)
|
||||
val address: Expression
|
||||
if(ptrVar.datatype.isPointer) {
|
||||
// pointer[idx].field = value --> pokeXXX(pointer as uword + idx*sizeof(Struct) + offsetof(Struct.field), value)
|
||||
val structSize = ptrVar.datatype.dereference().size(program.memsizer)
|
||||
val pointerAsUword = TypecastExpression(pointerIdentifier, DataType.UWORD, true, deref.position)
|
||||
val idx = ptr.last().second!!.indexExpr
|
||||
val scaledIndex = BinaryExpression(idx, "*", NumericLiteral(BaseDataType.UWORD, structSize.toDouble(), deref.position), deref.position)
|
||||
val structAddr = BinaryExpression(pointerAsUword, "+", scaledIndex, deref.position)
|
||||
address = BinaryExpression(structAddr, "+", offsetNumber, deref.position)
|
||||
}
|
||||
else {
|
||||
// pointerarray[idx].field = value --> pokeXXX(pointerarray[idx] as uword + offsetof(Struct.field), value)
|
||||
val index = ArrayIndexedExpression(pointerIdentifier, null, ptr.last().second!!, deref.position)
|
||||
val pointerAsUword = TypecastExpression(index, DataType.UWORD, true, deref.position)
|
||||
address = BinaryExpression(pointerAsUword, "+", offsetNumber, deref.position)
|
||||
}
|
||||
val (pokeFunc, valueCast) = pokeFunc(parent.inferType(program).getOrUndef())
|
||||
val value = if(valueCast==null) assignment.value else TypecastExpression(assignment.value, valueCast, true, assignment.value.position)
|
||||
val pokeCall = FunctionCallStatement(IdentifierReference(listOf(pokeFunc), assignment.position),
|
||||
mutableListOf(address, value), false, assignment.position)
|
||||
|
||||
if(assignment.isAugmentable)
|
||||
errors.warn("in-place assignment of indexed pointer variable currently is very inefficient, maybe use a temporary pointer variable", assignment.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment, pokeCall, assignment.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(deref.chain.last().second!=null && deref.derefLast && deref.chain.dropLast(1).all { it.second==null } ) {
|
||||
|
||||
// parent could be Assigment directly, or a binexpr chained pointer expression (with '.' operator)
|
||||
@@ -710,15 +781,7 @@ _after:
|
||||
val dt = deref.inferType(program).getOrUndef()
|
||||
if(dt.isNumericOrBool) {
|
||||
// if it's something else beside number (like, a struct instance) we don't support rewriting that...
|
||||
val (pokeFunc, cast) =
|
||||
if (dt.isBool) "pokebool" to null
|
||||
else if (dt.isUnsignedByte) "poke" to null
|
||||
else if (dt.isSignedByte) "poke" to DataType.UBYTE
|
||||
else if (dt.isUnsignedWord) "pokew" to null
|
||||
else if (dt.isSignedWord) "pokew" to DataType.UWORD
|
||||
else if (dt.isLong) "pokel" to null
|
||||
else if (dt.isFloat) "pokef" to null
|
||||
else throw FatalAstException("can only deref a numeric or boolean pointer here")
|
||||
val (pokeFunc, cast) = pokeFunc(dt)
|
||||
val indexer = deref.chain.last().second!!
|
||||
val identifier = IdentifierReference(deref.chain.map { it.first }, deref.position)
|
||||
val indexed = ArrayIndexedExpression(identifier, null, indexer, deref.position)
|
||||
@@ -872,4 +935,68 @@ _after:
|
||||
errors.err(error, ifExpr.condition.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
fun convertArrayIntoStructInitializer(array: ArrayLiteral, struct: ISubType): StaticStructInitializer {
|
||||
val structname = IdentifierReference(struct.scopedNameString.split("."), array.position)
|
||||
return StaticStructInitializer(structname, array.value.toMutableList(), array.position)
|
||||
}
|
||||
|
||||
fun checkNumberOfElements(struct: StructDecl, array: ArrayLiteral): Boolean {
|
||||
val numValues = array.value.size
|
||||
if (numValues>0 && struct.fields.size != numValues) {
|
||||
if (numValues < struct.fields.size) {
|
||||
val missing = struct.fields.drop(numValues).joinToString(", ") { it.second }
|
||||
errors.err("invalid number of field values: expected ${struct.fields.size} or 0 but got ${numValues}, missing: $missing", array.position)
|
||||
} else
|
||||
errors.err("invalid number of field values: expected ${struct.fields.size} or 0 but got ${numValues}", array.position)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if(parent is VarDecl) {
|
||||
if (parent.datatype.isPointerArray && parent.datatype.elementType().subType!=null) {
|
||||
val struct = parent.datatype.elementType().subType as StructDecl
|
||||
val allremovals = mutableListOf<VarDecl>()
|
||||
var noErrors = true
|
||||
var changes = false
|
||||
array.value.withIndex().forEach { (index, elt) ->
|
||||
if(elt is ArrayLiteral) {
|
||||
noErrors = noErrors and checkNumberOfElements(struct, elt)
|
||||
if(noErrors) {
|
||||
array.value[index] = convertArrayIntoStructInitializer(elt, struct)
|
||||
changes = true
|
||||
}
|
||||
} else if(elt is IdentifierReference) {
|
||||
val arrayvar = elt.targetVarDecl()!!.value as ArrayLiteral
|
||||
noErrors = noErrors and checkNumberOfElements(struct, arrayvar)
|
||||
if(noErrors) {
|
||||
array.value[index] = convertArrayIntoStructInitializer(arrayvar, struct)
|
||||
allremovals += elt.targetVarDecl()!!
|
||||
changes = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(changes && noErrors) {
|
||||
array.linkParents(parent)
|
||||
return allremovals.map { IAstModification.Remove(it, it.parent as IStatementContainer) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(parent is Assignment) {
|
||||
val targetDt = parent.target.inferType(program).getOrUndef()
|
||||
if(targetDt.isPointer && targetDt.subType!=null) {
|
||||
val struct = targetDt.subType as StructDecl
|
||||
if(checkNumberOfElements(struct, array)) {
|
||||
val initializser = convertArrayIntoStructInitializer(array, struct)
|
||||
return listOf(IAstModification.ReplaceNode(array, initializser, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
+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
|
||||
|
||||
@@ -270,7 +270,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!))
|
||||
srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!))
|
||||
srcTarget.pointerDereference!=null -> target.add(transform(srcTarget.pointerDereference!!))
|
||||
srcTarget.arrayIndexedDereference!=null -> TODO("this array indexed dereference should have been converted to some other ast nodes ${srcTarget.position}")
|
||||
srcTarget.arrayIndexedDereference!=null -> throw FatalAstException("this should have been converted to some other ast nodes ${srcTarget.position}")
|
||||
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
|
||||
}
|
||||
return target
|
||||
|
||||
@@ -21,6 +21,8 @@ internal fun postprocessSimplifiedAst(
|
||||
private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) {
|
||||
|
||||
fun getStStruct(subType: ISubType): StStruct {
|
||||
if(subType is StStruct)
|
||||
return subType
|
||||
val stNode = st.lookup(subType.scopedNameString) as? StStruct
|
||||
if(stNode != null)
|
||||
return stNode
|
||||
@@ -28,7 +30,7 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
|
||||
throw FatalAstException("cannot find in ST: ${subType.scopedNameString} $subType")
|
||||
}
|
||||
|
||||
fun fixSubtype(type: DataType) {
|
||||
fun fixSubtypeIntoStType(type: DataType) {
|
||||
if(type.subType!=null && type.subType !is StStruct) {
|
||||
type.subType = getStStruct(type.subType!!)
|
||||
}
|
||||
@@ -36,13 +38,21 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
|
||||
|
||||
fun fixSubtypes(node: PtNode) {
|
||||
when(node) {
|
||||
is IPtVariable -> fixSubtype(node.type)
|
||||
is PtPointerDeref -> fixSubtype(node.type)
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
|
||||
is PtExpression -> fixSubtype(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtype(it) }
|
||||
is PtSubroutineParameter -> fixSubtype(node.type)
|
||||
is IPtVariable -> {
|
||||
fixSubtypeIntoStType(node.type)
|
||||
// if it's an array, fix the subtypes of its elements as well
|
||||
if(node.type.isArray && node is PtVariable) {
|
||||
(node.value as? PtArray)?.let {array ->
|
||||
array.children.forEach { fixSubtypes(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtPointerDeref -> fixSubtypeIntoStType(node.type)
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtypeIntoStType(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtypeIntoStType(it.second) }
|
||||
is PtExpression -> fixSubtypeIntoStType(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtypeIntoStType(it) }
|
||||
is PtSubroutineParameter -> fixSubtypeIntoStType(node.type)
|
||||
else -> { /* has no datatype */ }
|
||||
}
|
||||
}
|
||||
@@ -162,7 +172,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
is PtAddressOf -> value.arrayIndexExpr == null || notComplex(value.arrayIndexExpr!!)
|
||||
is PtBuiltinFunctionCall -> {
|
||||
when (value.name) {
|
||||
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) }
|
||||
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "mklong", "mklong2", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -175,7 +185,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
is PtNumber,
|
||||
is PtRange,
|
||||
is PtString -> true
|
||||
is PtIdentifier -> true // actually PtIdentifier IS "complex" this time (it's a variable that might change) but it's kinda annoying to give a warning message for this very common case
|
||||
// note that PtIdentifier als is "complex" this time (it's a variable that might change)
|
||||
else -> false
|
||||
}
|
||||
|
||||
@@ -192,7 +202,6 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
}
|
||||
|
||||
// complex return value, need to store it before calling the defer block
|
||||
errors.warn("using defer with nontrivial return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
|
||||
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
|
||||
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
|
||||
val popCalls = pushAndPopCalls.map { it.second }
|
||||
|
||||
@@ -384,4 +384,25 @@ internal class StatementReorderer(
|
||||
)
|
||||
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 }
|
||||
if(zeroIndexer!=null) {
|
||||
val target = deref.definingScope.lookup(listOf(zeroIndexer.first))
|
||||
if(target is VarDecl && target.datatype.isPointer) {
|
||||
val position = deref.chain.indexOf(zeroIndexer)
|
||||
val rest = deref.chain.drop(position + 1)
|
||||
if (rest.size == 1 && rest[0].second == null) {
|
||||
// pointer[0]^^.field = xxx --> pointer^^.field = xxx
|
||||
val noindexer = zeroIndexer.first to null
|
||||
val newchain = deref.chain.take(position) + noindexer + rest
|
||||
val newDeref = PtrDereference(newchain.map { it.first }, false, deref.position)
|
||||
return listOf(IAstModification.ReplaceNode(deref, newDeref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
val leftCv = expr.left.constValue(program)
|
||||
@@ -87,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 =
|
||||
@@ -95,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())
|
||||
@@ -126,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 0xffffffffL+rightCv.number
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr.right,
|
||||
NumericLiteral(leftDt.getOrUndef().base, value, expr.right.position),
|
||||
@@ -170,7 +176,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
|
||||
if((expr.operator!="<<" && expr.operator!=">>") || !leftDt.isWords || !rightDt.isBytes) {
|
||||
if((expr.operator!="<<" && expr.operator!=">>") || !leftDt.isInteger || !rightDt.isBytes) {
|
||||
// determine common datatype and add typecast as required to make left and right equal types
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOrUndef(), rightDt.getOrUndef(), expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
@@ -265,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,9 +644,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return if(initializer.args.isEmpty())
|
||||
noModifications
|
||||
else {
|
||||
val struct = initializer.structname.targetStructDecl()!!
|
||||
val paramsPossibleDatatypes = struct.fields.map { listOf(it.first) }
|
||||
fixupArgumentList(paramsPossibleDatatypes, initializer.args, initializer)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val paramsPossibleDatatypes = struct.fields.map { listOf(it.first) }
|
||||
fixupArgumentList(paramsPossibleDatatypes, initializer.args, initializer)
|
||||
}
|
||||
else noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
var pushTypecast: DataType? = null
|
||||
var pushWord = false
|
||||
var pushFloat = false
|
||||
var pushLong = false
|
||||
|
||||
when {
|
||||
value.type.isBool -> {
|
||||
@@ -29,13 +30,14 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
}
|
||||
value.type.isUnsignedByte -> {}
|
||||
value.type.isUnsignedWord -> pushWord = true
|
||||
value.type.isLong -> pushLong = true
|
||||
value.type.isPassByRef -> pushWord = true
|
||||
value.type.isFloat -> pushFloat = true
|
||||
else -> throw FatalAstException("unsupported return value type ${value.type} with defer")
|
||||
}
|
||||
|
||||
val pushFunc = if(pushFloat) "floats.push" else if(pushWord) "sys.pushw" else "sys.push"
|
||||
val popFunc = if(pushFloat) "floats.pop" else if(pushWord) "sys.popw" else "sys.pop"
|
||||
val pushFunc = if(pushFloat) "floats.push" else if(pushWord) "sys.pushw" else if (pushLong) "sys.pushl" else "sys.push"
|
||||
val popFunc = if(pushFloat) "floats.pop" else if(pushWord) "sys.popw" else if(pushLong) "sys.popl" else "sys.pop"
|
||||
val pushCall = PtFunctionCall(pushFunc, true, DataType.UNDEFINED, value.position)
|
||||
if(pushTypecast!=null) {
|
||||
val typecast = PtTypeCast(pushTypecast, value.position).also {
|
||||
@@ -47,7 +49,7 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
}
|
||||
val popCall = if(popTypecast!=null) {
|
||||
PtTypeCast(popTypecast, value.position).also {
|
||||
val returnDt = if(pushWord) DataType.UWORD else DataType.UBYTE
|
||||
val returnDt = if(pushWord) DataType.UWORD else if(pushLong) DataType.LONG else if(pushFloat) DataType.FLOAT else DataType.UBYTE
|
||||
it.add(PtFunctionCall(popFunc, false, returnDt, value.position))
|
||||
}
|
||||
} else
|
||||
|
||||
@@ -49,9 +49,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
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
|
||||
if(valueDt.isSigned && decl.datatype.isUnsigned ||
|
||||
valueDt.isUnsigned && decl.datatype.isSigned) {
|
||||
// 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 {
|
||||
@@ -69,34 +71,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
// 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)
|
||||
}
|
||||
@@ -113,7 +98,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))
|
||||
}
|
||||
@@ -124,8 +109,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
return if(parent is IStatementContainer)
|
||||
listOf(ScopeFlatten(scope, parent as IStatementContainer))
|
||||
else
|
||||
else {
|
||||
if(scope.statements.any {it is VarDecl}) {
|
||||
throw FatalAstException("there are leftover vardecls in the nested scope at ${scope.position}, they should have been moved/placed in the declaration scope (subroutine) by now")
|
||||
}
|
||||
noModifications
|
||||
}
|
||||
}
|
||||
|
||||
private class ScopeFlatten(val scope: AnonymousScope, val into: IStatementContainer) : IAstModification {
|
||||
@@ -219,17 +208,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))
|
||||
}
|
||||
@@ -444,7 +433,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
newIndex.linkParents(arrayIndexedExpression.indexer)
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef!=null) {
|
||||
TODO("cleanup pointer indexing")
|
||||
TODO("cleanup pointer indexing ${arrayIndexedExpression.position}")
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
|
||||
@@ -145,7 +145,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
if(addrOf!=null) {
|
||||
val identType = addrOf.identifier?.inferType(program)?.getOrUndef()
|
||||
if(identType?.isSplitWordArray==true) {
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual (because arg is a @split word array) expected: $expected", call.args[mismatch].position)
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual (because arg is a split word array) expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.ast.Program
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.compiler.ModuleImporter
|
||||
@@ -49,7 +50,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(".file should point to specified path") {
|
||||
error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
val error2 = importer.importMainModule(srcPathAbs).getErrorOrElse { error("should have import error") }
|
||||
withClue(".file should be normalized") {
|
||||
"${error2.file}" shouldBe "${error2.file.normalize()}"
|
||||
@@ -57,7 +58,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(".file should point to specified path") {
|
||||
error2.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
|
||||
test("testDirectory") {
|
||||
@@ -75,7 +76,7 @@ class TestModuleImporter: FunSpec({
|
||||
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
|
||||
shouldThrow<FileSystemException> { importer.importMainModule(srcPathAbs) }
|
||||
.let {
|
||||
@@ -86,7 +87,7 @@ class TestModuleImporter: FunSpec({
|
||||
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ class TestModuleImporter: FunSpec({
|
||||
val path = assumeReadableFile(searchIn[0], fileName)
|
||||
|
||||
val module = importer.importMainModule(path.absolute()).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -118,7 +119,7 @@ class TestModuleImporter: FunSpec({
|
||||
}
|
||||
|
||||
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -131,7 +132,7 @@ class TestModuleImporter: FunSpec({
|
||||
assumeReadableFile(searchIn, path)
|
||||
|
||||
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -152,7 +153,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +171,10 @@ class TestModuleImporter: FunSpec({
|
||||
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("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
}
|
||||
}
|
||||
@@ -203,14 +205,14 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false }
|
||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist"
|
||||
errors.report()
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
|
||||
val result2 = importer.importImplicitLibraryModule(filenameWithExt)
|
||||
withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null }
|
||||
withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false }
|
||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8"
|
||||
errors.report()
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +234,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
|
||||
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
importer.errors.report()
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class TestBuiltinFunctions: FunSpec({
|
||||
func.parameters[0].name shouldBe "address"
|
||||
func.parameters[0].possibleDatatypes shouldBe arrayOf(BaseDataType.UWORD)
|
||||
func.parameters[1].name shouldBe "value"
|
||||
func.parameters[1].possibleDatatypes shouldBe arrayOf(BaseDataType.UBYTE)
|
||||
func.parameters[1].possibleDatatypes shouldBe arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
|
||||
func.pure shouldBe false
|
||||
func.returnType shouldBe null
|
||||
}
|
||||
@@ -184,5 +184,23 @@ main {
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("sgn") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared b1 = -100
|
||||
word @shared w1 = -1000
|
||||
long @shared l1 = -1000000
|
||||
|
||||
cx16.r0sL = sgn(b1)
|
||||
cx16.r1sL = sgn(w1)
|
||||
cx16.r2sL = sgn(l1)
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -47,7 +47,6 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
slabsHighBank = null,
|
||||
slabsGolden = false,
|
||||
compilationTarget = target.name,
|
||||
dontSplitWordArrays = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
@@ -97,6 +96,7 @@ class TestCompilerOnExamplesC64: FunSpec({
|
||||
"library/main",
|
||||
"plasma",
|
||||
"rasterbars",
|
||||
"simplemultiplexer",
|
||||
"sprites",
|
||||
"starfield",
|
||||
"tehtriz",
|
||||
@@ -149,6 +149,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
"bubbleuniverse",
|
||||
"charfade",
|
||||
"charsets",
|
||||
"circles",
|
||||
"cobramk3-gfx",
|
||||
@@ -176,6 +177,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"test_gfx_lores",
|
||||
"test_gfx_hires",
|
||||
"testmonogfx",
|
||||
"textspotlight",
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
@@ -223,6 +225,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"textelite",
|
||||
"pointers/animalgame",
|
||||
"pointers/binarytree",
|
||||
"pointers/hashtable",
|
||||
"pointers/sortedlist",
|
||||
"pointers/sorting"
|
||||
),
|
||||
@@ -253,6 +256,7 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
"sincos",
|
||||
"pointers/animalgame",
|
||||
"pointers/binarytree",
|
||||
"pointers/hashtable",
|
||||
"pointers/sortedlist",
|
||||
"pointers/fountain-virtual",
|
||||
"pointers/sorting"
|
||||
|
||||
@@ -39,7 +39,6 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
slabsHighBank = null,
|
||||
slabsGolden = false,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
dontSplitWordArrays = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
|
||||
Loaded 100 of 212 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user