mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 01:23:34 +00:00
Compare commits
388 Commits
Author | SHA1 | Date | |
---|---|---|---|
07cce3b3fc | |||
f2c19afd95 | |||
d159e70e1c | |||
ac693a2541 | |||
1e988116ce | |||
ec9e722927 | |||
4cd5e8c378 | |||
b759d5e06a | |||
1469033c1e | |||
c15fd75df7 | |||
73524e01a6 | |||
9e54e11113 | |||
01ac5f29db | |||
67a2241e32 | |||
72b6dc3de7 | |||
6f5b645995 | |||
458ad1de57 | |||
216f48b7c1 | |||
b2d1757e5a | |||
6e53eb9d5c | |||
e5ee5be9c5 | |||
bd237b2b95 | |||
d31cf766eb | |||
56d530ff04 | |||
0bbb2240f2 | |||
1c8e4dba73 | |||
4a9956c4a4 | |||
59c0e6ae32 | |||
94c30fc21e | |||
8bb3b3be20 | |||
85e3c2c5a2 | |||
4be381c597 | |||
6ff5470cf1 | |||
151dcfdef9 | |||
c282b4cb9f | |||
c426f4626c | |||
0e3c92626e | |||
5099525e24 | |||
e22b4cbb67 | |||
2b48828179 | |||
3e181362dd | |||
71fd98e39e | |||
71cd8b6d51 | |||
ad75fcbf7e | |||
f8b04a6357 | |||
d8fcbb78d3 | |||
8408bf3789 | |||
3e1185658e | |||
d778cdcd61 | |||
90b303fc03 | |||
eb86b1270d | |||
a1f0cc878b | |||
f2e2720b15 | |||
ec8cfe1591 | |||
22eac159e5 | |||
956b0c3fa7 | |||
a6427e0949 | |||
22031f39b0 | |||
c4673d3a67 | |||
e83e021541 | |||
c1f2ecd413 | |||
46fbe01df9 | |||
8647a8290e | |||
bac51f4b31 | |||
582aab180a | |||
5fb714fcb2 | |||
3994de77d0 | |||
24c8d1f1f4 | |||
110f877dcc | |||
9cd3a9f8e8 | |||
1464050bf5 | |||
95e9e1b550 | |||
bda1c1c1eb | |||
d020a7974a | |||
a51fad3aab | |||
3cd32778bb | |||
8d67056f84 | |||
e986973b5e | |||
448c934cba | |||
96ef7ba55d | |||
4372de1e7e | |||
af0fb88adf | |||
066233eee8 | |||
b6f85d10b0 | |||
6f75413c09 | |||
d45fe4ce74 | |||
e828c013e6 | |||
988459f744 | |||
7c701bdf3f | |||
446fc35d5c | |||
bec9cc7047 | |||
961380acb6 | |||
84c0685a60 | |||
629222f103 | |||
8c448e5bc2 | |||
b5fa6c2d0a | |||
680b2df08a | |||
09bd47f98b | |||
7f69f9ce4f | |||
4179b4e543 | |||
66364554c4 | |||
43f2448789 | |||
130cee1e70 | |||
b976360248 | |||
225bfc4164 | |||
d7ceda4d82 | |||
14d091e60a | |||
2809668ef4 | |||
bafb86e00b | |||
f5db31b8ff | |||
e1d0dbed0c | |||
1d1fe364d0 | |||
2b9316c4ff | |||
c50cbbb526 | |||
b93d9ecd7e | |||
96243db88b | |||
4daf75a8cc | |||
8c63d7cf5b | |||
6f78a32e64 | |||
af6731c9c8 | |||
25cf0d2b94 | |||
9389791d91 | |||
aa8191d0a1 | |||
0d5c78e875 | |||
e8679ae03b | |||
d1d224b7fc | |||
df995f7bc9 | |||
af39502450 | |||
ffa38955d6 | |||
8d82fb6d8f | |||
306770331a | |||
d3f433c8cf | |||
cf49cbd1f8 | |||
8a99e75299 | |||
2dbf849c82 | |||
ba3dce0b4c | |||
ca9588380a | |||
ae2619602d | |||
de06353194 | |||
3ff3f5e1cc | |||
4b747859b3 | |||
2201765366 | |||
dfa1d5e398 | |||
ce9a90f626 | |||
2deb18beb2 | |||
0f7454059c | |||
f9ba09ac4d | |||
4e74873eae | |||
f0cd03d14f | |||
f2b069c562 | |||
bc89306dc1 | |||
bf4da1655b | |||
d819aa270f | |||
e6d945f835 | |||
4fe408f1fd | |||
c376e42092 | |||
63a653cdf0 | |||
5d900800f2 | |||
def06dbc0b | |||
9b66a597bb | |||
f1ee3b4e60 | |||
6395e39d63 | |||
2a6d9d7e31 | |||
32a7cd31da | |||
dd4a56cb5f | |||
d110d1cb5f | |||
48858019b7 | |||
aff6b1fca5 | |||
d260182ef3 | |||
e39a38b0d9 | |||
82d7179c92 | |||
f42746ba06 | |||
1f69deaccd | |||
ea8b7ab193 | |||
9938959026 | |||
d5e5485d2e | |||
97b9c8f320 | |||
35aebbc209 | |||
81f7419f70 | |||
2f951bd54d | |||
18f5963b09 | |||
836509c1d1 | |||
949d536e42 | |||
f69b17e165 | |||
49a0584c54 | |||
e21aa2c8f0 | |||
40071b1431 | |||
02e29e6990 | |||
e19de0901e | |||
137d506e42 | |||
90c4a26d52 | |||
f378a8997b | |||
1377bed988 | |||
8f9f947c42 | |||
37f6c2858f | |||
13d7f239ab | |||
a6f3c84e28 | |||
fe4e0e9835 | |||
809917f13b | |||
2b35498370 | |||
f45eabdd9e | |||
438f3ee8d2 | |||
4bea31f051 | |||
5eae7a2b93 | |||
364ef3e55c | |||
e61818f194 | |||
0f9ce319d4 | |||
5d90871789 | |||
88a9e09918 | |||
c50ecf6055 | |||
a18de75da9 | |||
e112dfd910 | |||
9154d8bd37 | |||
0b55372b3b | |||
3ad7fb010f | |||
3f64d1bb5a | |||
a6f564ad88 | |||
d97da3bb7b | |||
a77d3c92ad | |||
6d17e5307c | |||
c2205e473a | |||
4ffb194847 | |||
744cd6ec42 | |||
f08fc18ab5 | |||
462af76770 | |||
9cec554f7c | |||
08b25e610d | |||
e896d5a1a6 | |||
b939562062 | |||
256781bba5 | |||
19705196d6 | |||
3ce692bb10 | |||
78bdbde3ae | |||
8d8c066447 | |||
5da9379c37 | |||
032d20ff37 | |||
d19b17cbfe | |||
4a4f8ff5db | |||
60a9209a14 | |||
0f9e167df3 | |||
2e2b8c498e | |||
144199730f | |||
4bb4eab3b2 | |||
cf9151f669 | |||
aef4598cec | |||
3ada0fdf84 | |||
a5d97b326e | |||
2640015fb1 | |||
6cd42ddafe | |||
1f17c22132 | |||
5c62f612cc | |||
b9ca1c2e2c | |||
93b2ff2e52 | |||
3991d23a69 | |||
1be139759c | |||
d0674ad688 | |||
ffb47458ff | |||
84ec1be8a4 | |||
f4dafec645 | |||
97ce72521d | |||
d2f0e74879 | |||
d9e3895c45 | |||
5075901830 | |||
f1193bb5a0 | |||
d3dc279105 | |||
acc942f690 | |||
e947067dcf | |||
bd9ebf4603 | |||
f41192a52a | |||
ff54d6abd7 | |||
f40bcc219f | |||
679965410a | |||
c6e13ae2a3 | |||
20cdcc673b | |||
89f46222d9 | |||
b27cbfac5e | |||
31c946aeeb | |||
bfc8a26381 | |||
9d98746501 | |||
63b03ba70c | |||
70bab76b36 | |||
15d24d4308 | |||
9ec62eb045 | |||
12f841e30d | |||
335599ed22 | |||
0b717f9e76 | |||
e941f6ecca | |||
ef7744dbda | |||
c83a61c460 | |||
335684caf7 | |||
8d6220ce51 | |||
39ea5c5f99 | |||
b03597ac13 | |||
58f323c087 | |||
513a68584c | |||
88d5c68b32 | |||
14f9382cf9 | |||
cffb582568 | |||
e1812ce16c | |||
7a3163f59a | |||
6f3b2749b0 | |||
c144d4e501 | |||
edfd9d55ba | |||
774897260e | |||
65ba91411d | |||
9cbb8e1a64 | |||
53e9ad5088 | |||
cf6ea63fa6 | |||
1de0ebb7bc | |||
77c1376d6d | |||
353f1954a5 | |||
8bf3406cf8 | |||
936bf9a05c | |||
4487499663 | |||
3976cc26a2 | |||
e6ff87ecd0 | |||
c0887b5f08 | |||
f14dda4eca | |||
bd7f75c130 | |||
fbe3ce008b | |||
7ac6c8f2d1 | |||
fdfbb7bdf0 | |||
1c16bbb742 | |||
9735527062 | |||
402827497e | |||
f81aa0d867 | |||
d32a970101 | |||
cd651aa416 | |||
8a3189123a | |||
b37231d0f5 | |||
3c55719bf1 | |||
af8279a9b9 | |||
c38508c262 | |||
b0e8738ab8 | |||
cae480768e | |||
a70276c190 | |||
0c461ffe2e | |||
237511f2d6 | |||
cdcb652033 | |||
71e678b382 | |||
3050156325 | |||
4bfdbad2e4 | |||
06137ecdc4 | |||
d89f5b0df8 | |||
b6e2b36692 | |||
a6d789cfbc | |||
c07907e7bd | |||
7d8496c874 | |||
164ac56db1 | |||
fdddb8ca64 | |||
a9d4b8b0fa | |||
ec7b9f54c2 | |||
307558a7e7 | |||
febf423eab | |||
a999c23014 | |||
69f1ade595 | |||
b166576e54 | |||
ee2ba5f398 | |||
cb9825484d | |||
76cda82e23 | |||
37b61d9e6b | |||
52f0222a6d | |||
75ccac2f2c | |||
5c771a91f7 | |||
a242ad10e6 | |||
b5086b6a8f | |||
3e47dad12a | |||
235610f40c | |||
6b59559c65 | |||
23e954f716 | |||
983c899cad | |||
c2f9385965 | |||
ceb2c9e4f8 | |||
68a7f9c665 | |||
ffd8d9c7c1 | |||
c66fc8630c | |||
9ca1c66f2b | |||
33647a29d0 | |||
02b12cc762 | |||
3280993e2a | |||
3723c22054 | |||
0a2c4ea0c4 | |||
58a83c0439 | |||
d665489054 | |||
9200992024 | |||
6408cc46a8 | |||
961bcdb7ae | |||
edee70cf31 |
@ -4,8 +4,8 @@ sudo: false
|
|||||||
# dist: xenial
|
# dist: xenial
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- chmod +x gradlew
|
- chmod +x ./gradlew
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- gradle test
|
- ./gradlew test
|
||||||
|
|
||||||
|
79
README.md
79
README.md
@ -2,23 +2,32 @@
|
|||||||
[](https://travis-ci.org/irmen/prog8)
|
[](https://travis-ci.org/irmen/prog8)
|
||||||
[](https://prog8.readthedocs.io/)
|
[](https://prog8.readthedocs.io/)
|
||||||
|
|
||||||
Prog8 - Structured Programming Language for 8-bit 6502/6510 microprocessors
|
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||||
===========================================================================
|
============================================================================
|
||||||
|
|
||||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||||
|
|
||||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
*Software license: GNU GPL 3.0, see file LICENSE*
|
||||||
|
|
||||||
|
|
||||||
This is a structured programming language for the 8-bit 6502/6510 microprocessor from the late 1970's and 1980's
|
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||||
as used in many home computers from that era. It is a medium to low level programming language,
|
as used in many home computers from that era. It is a medium to low level programming language,
|
||||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler):
|
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||||
|
|
||||||
- reduction of source code length
|
Documentation
|
||||||
|
-------------
|
||||||
|
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||||
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
|
|
||||||
|
What use Prog8 provide?
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
- reduction of source code length over raw assembly
|
||||||
- modularity, symbol scoping, subroutines
|
- modularity, symbol scoping, subroutines
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string and array variables and string sharing
|
- automatic variable allocations, automatic string and array variables and string sharing
|
||||||
- subroutines with a input- and output parameter signature
|
- subroutines with an input- and output parameter signature
|
||||||
- constant folding in expressions
|
- constant folding in expressions
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||||
@ -29,7 +38,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
|||||||
- inline assembly allows you to have full control when every cycle or byte matters
|
- inline assembly allows you to have full control when every cycle or byte matters
|
||||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||||
|
|
||||||
Rapid edit-compile-run-debug cycle:
|
*Rapid edit-compile-run-debug cycle:*
|
||||||
|
|
||||||
- use a modern PC to do the work on
|
- use a modern PC to do the work on
|
||||||
- very quick compilation times
|
- very quick compilation times
|
||||||
@ -37,15 +46,16 @@ Rapid edit-compile-run-debug cycle:
|
|||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
|
|
||||||
Prog8 is mainly targeted at the Commodore-64 machine at this time.
|
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
|
||||||
|
|
||||||
Documentation/manual
|
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
||||||
--------------------
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
||||||
https://prog8.readthedocs.io/
|
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
Required tools
|
|
||||||
--------------
|
|
||||||
|
Additional required tools
|
||||||
|
-------------------------
|
||||||
|
|
||||||
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
||||||
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
||||||
@ -55,8 +65,9 @@ A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepac
|
|||||||
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
||||||
IntelliJ IDEA with the Kotlin plugin).
|
IntelliJ IDEA with the Kotlin plugin).
|
||||||
|
|
||||||
It's handy to have a C-64 emulator or a real C-64 to run the programs on. The compiler assumes the presence
|
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||||
of the [Vice emulator](http://vice-emu.sourceforge.net/)
|
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||||
|
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
||||||
|
|
||||||
|
|
||||||
Example code
|
Example code
|
||||||
@ -64,44 +75,45 @@ Example code
|
|||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64utils
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
ubyte[256] sieve
|
ubyte[256] sieve
|
||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2 ; is increased in the loop
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
; clear the sieve, to reset starting situation on subsequent runs
|
||||||
memset(sieve, 256, false)
|
memset(sieve, 256, false)
|
||||||
|
; calculate primes
|
||||||
c64scr.print("prime numbers up to 255:\n\n")
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
while true {
|
repeat {
|
||||||
ubyte prime = find_next_prime()
|
ubyte prime = find_next_prime()
|
||||||
if prime==0
|
if prime==0
|
||||||
break
|
break
|
||||||
c64scr.print_ub(prime)
|
txt.print_ub(prime)
|
||||||
c64scr.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
c64scr.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
c64scr.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
|
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
if candidate_prime==0
|
||||||
return 0
|
return 0 ; we wrapped; no more primes available in the sieve
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; found next one, mark the multiples and return it.
|
||||||
sieve[candidate_prime] = true
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
multiple += candidate_prime
|
||||||
@ -111,11 +123,11 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when compiled an ran on a C-64 you'll get:
|
when compiled an ran on a C-64 you'll get:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
||||||
|
|
||||||

|

|
||||||
@ -127,3 +139,8 @@ Another example (cube3d-sprites.p8) draws the vertices of a rotating 3d cube:
|
|||||||
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
The CommanderX16 compiler target is quite capable already too, here's a well known space ship
|
||||||
|
animated in 3D with hidden line removal, in the CommanderX16 emulator:
|
||||||
|
|
||||||
|

|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// id "org.jetbrains.kotlin.jvm" version "1.3.72"
|
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'org.jetbrains.dokka' version "0.9.18"
|
id 'org.jetbrains.dokka' version "0.9.18"
|
||||||
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
||||||
@ -110,3 +110,7 @@ dokka {
|
|||||||
outputFormat = 'html'
|
outputFormat = 'html'
|
||||||
outputDirectory = "$buildDir/kdoc"
|
outputDirectory = "$buildDir/kdoc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task wrapper(type: Wrapper) {
|
||||||
|
gradleVersion = '6.1.1'
|
||||||
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
@ -1,49 +1,56 @@
|
|||||||
; --- low level floating point assembly routines for the C64
|
; --- low level floating point assembly routines for the C64
|
||||||
|
|
||||||
|
FL_ONE_const .byte 129 ; 1.0
|
||||||
|
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||||
|
|
||||||
|
floats_store_reg .byte 0 ; temp storage
|
||||||
|
|
||||||
|
|
||||||
ub2float .proc
|
ub2float .proc
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
; clobbers A, Y
|
; clobbers A, Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy c64.SCRATCH_ZPB1
|
ldy P8ZP_SCRATCH_B1
|
||||||
jsr FREADUY
|
lda #0
|
||||||
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
jsr GIVAYF
|
||||||
ldy c64.SCRATCH_ZPWORD2+1
|
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
b2float .proc
|
b2float .proc
|
||||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
; clobbers A, Y
|
; clobbers A, Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
lda c64.SCRATCH_ZPB1
|
lda P8ZP_SCRATCH_B1
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
uw2float .proc
|
uw2float .proc
|
||||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr GIVUAYFAY
|
jsr GIVUAYFAY
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
w2float .proc
|
w2float .proc
|
||||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy c64.SCRATCH_ZPWORD1
|
ldy P8ZP_SCRATCH_W1
|
||||||
lda c64.SCRATCH_ZPWORD1+1
|
lda P8ZP_SCRATCH_W1+1
|
||||||
jsr GIVAYF
|
jsr GIVAYF
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
@ -51,8 +58,8 @@ w2float .proc
|
|||||||
stack_b2float .proc
|
stack_b2float .proc
|
||||||
; -- b2float operating on the stack
|
; -- b2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -60,9 +67,9 @@ stack_b2float .proc
|
|||||||
stack_w2float .proc
|
stack_w2float .proc
|
||||||
; -- w2float operating on the stack
|
; -- w2float operating on the stack
|
||||||
inx
|
inx
|
||||||
ldy c64.ESTACK_LO,x
|
ldy P8ESTACK_LO,x
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr GIVAYF
|
jsr GIVAYF
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -70,44 +77,45 @@ stack_w2float .proc
|
|||||||
stack_ub2float .proc
|
stack_ub2float .proc
|
||||||
; -- ub2float operating on the stack
|
; -- ub2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
tay
|
tay
|
||||||
jsr FREADUY
|
lda #0
|
||||||
|
jsr GIVAYF
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_uw2float .proc
|
stack_uw2float .proc
|
||||||
; -- uw2float operating on the stack
|
; -- uw2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
ldy c64.ESTACK_HI,x
|
ldy P8ESTACK_HI,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr GIVUAYFAY
|
jsr GIVUAYFAY
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2w .proc ; also used for float2b
|
stack_float2w .proc ; also used for float2b
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda $64
|
lda $64
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
lda $65
|
lda $65
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2uw .proc ; also used for float2ub
|
stack_float2uw .proc ; also used for float2ub
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr GETADR
|
jsr GETADR
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
tya
|
tya
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -115,79 +123,68 @@ stack_float2uw .proc ; also used for float2ub
|
|||||||
push_float .proc
|
push_float .proc
|
||||||
; ---- push mflpt5 in A/Y onto stack
|
; ---- push mflpt5 in A/Y onto stack
|
||||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_rndf .proc
|
func_rndf .proc
|
||||||
; -- put a random floating point value on the stack
|
; -- put a random floating point value on the stack
|
||||||
stx c64.SCRATCH_ZPREG
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #1
|
lda #1
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jsr RND ; rng into fac1
|
jsr RND ; rng into fac1
|
||||||
ldx #<_rndf_rnum5
|
ldx #<_rndf_rnum5
|
||||||
ldy #>_rndf_rnum5
|
ldy #>_rndf_rnum5
|
||||||
jsr MOVMF ; fac1 to mem X/Y
|
jsr MOVMF ; fac1 to mem X/Y
|
||||||
ldx c64.SCRATCH_ZPREG
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda #<_rndf_rnum5
|
lda #<_rndf_rnum5
|
||||||
ldy #>_rndf_rnum5
|
ldy #>_rndf_rnum5
|
||||||
jmp push_float
|
jmp push_float
|
||||||
_rndf_rnum5 .byte 0,0,0,0,0
|
_rndf_rnum5 .byte 0,0,0,0,0
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
push_float_from_indexed_var .proc
|
|
||||||
; -- push the float from the array at A/Y with index on stack, onto the stack.
|
|
||||||
sta c64.SCRATCH_ZPWORD1
|
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
|
||||||
jsr prog8_lib.pop_index_times_5
|
|
||||||
jsr prog8_lib.add_a_to_zpword
|
|
||||||
lda c64.SCRATCH_ZPWORD1
|
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
|
||||||
jmp push_float
|
|
||||||
.pend
|
|
||||||
|
|
||||||
pop_float .proc
|
pop_float .proc
|
||||||
; ---- pops mflpt5 from stack to memory A/Y
|
; ---- pops mflpt5 from stack to memory A/Y
|
||||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #4
|
ldy #4
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -201,110 +198,74 @@ pop_float_fac1 .proc
|
|||||||
jmp MOVFM
|
jmp MOVFM
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
pop_float_fac2 .proc
|
||||||
|
; -- pops float from stack into FAC2
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jsr pop_float
|
||||||
|
lda #<fmath_float1
|
||||||
|
ldy #>fmath_float1
|
||||||
|
jmp CONUPK
|
||||||
|
.pend
|
||||||
|
|
||||||
pop_float_to_indexed_var .proc
|
pop_float_to_indexed_var .proc
|
||||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
jsr prog8_lib.pop_index_times_5
|
jsr prog8_lib.pop_index_times_5
|
||||||
jsr prog8_lib.add_a_to_zpword
|
jsr prog8_lib.add_a_to_zpword
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jmp pop_float
|
jmp pop_float
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
copy_float .proc
|
copy_float .proc
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta _target+1
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty _target+2
|
||||||
ldy #0
|
ldy #4
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
_loop lda (P8ZP_SCRATCH_W1),y
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
_target sta $ffff,y ; modified
|
||||||
iny
|
dey
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
bpl _loop
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
iny
|
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
inc_var_f .proc
|
inc_var_f .proc
|
||||||
; -- add 1 to float pointed to by A/Y
|
; -- add 1 to float pointed to by A/Y
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
lda #<FL_FONE
|
lda #<FL_ONE_const
|
||||||
ldy #>FL_FONE
|
ldy #>FL_ONE_const
|
||||||
jsr FADD
|
jsr FADD
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
ldx P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
dec_var_f .proc
|
dec_var_f .proc
|
||||||
; -- subtract 1 from float pointed to by A/Y
|
; -- subtract 1 from float pointed to by A/Y
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<FL_FONE
|
lda #<FL_ONE_const
|
||||||
ldy #>FL_FONE
|
ldy #>FL_ONE_const
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FSUB
|
jsr FSUB
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
ldx P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
inc_indexed_var_f .proc
|
|
||||||
; -- add 1 to float in array pointed to by A/Y, at index X
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp inc_var_f
|
|
||||||
.pend
|
|
||||||
|
|
||||||
dec_indexed_var_f .proc
|
|
||||||
; -- subtract 1 to float in array pointed to by A/Y, at index X
|
|
||||||
pha
|
|
||||||
txa
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
sta c64.SCRATCH_ZPB1
|
|
||||||
pla
|
|
||||||
clc
|
|
||||||
adc c64.SCRATCH_ZPB1
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jmp dec_var_f
|
|
||||||
.pend
|
|
||||||
|
|
||||||
|
|
||||||
pop_2_floats_f2_in_fac1 .proc
|
pop_2_floats_f2_in_fac1 .proc
|
||||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||||
@ -330,7 +291,7 @@ push_fac1_as_result .proc
|
|||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
jmp push_float
|
jmp push_float
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -342,21 +303,21 @@ pow_f .proc
|
|||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr pop_float
|
jsr pop_float
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr CONUPK ; fac2 = float1
|
jsr CONUPK ; fac2 = float1
|
||||||
lda #<fmath_float2
|
lda #<fmath_float2
|
||||||
ldy #>fmath_float2
|
ldy #>fmath_float2
|
||||||
jsr FPWR
|
jsr FPWR
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
div_f .proc
|
div_f .proc
|
||||||
; -- push f1/f2 on stack
|
; -- push f1/f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FDIV
|
jsr FDIV
|
||||||
@ -366,7 +327,7 @@ div_f .proc
|
|||||||
add_f .proc
|
add_f .proc
|
||||||
; -- push f1+f2 on stack
|
; -- push f1+f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FADD
|
jsr FADD
|
||||||
@ -376,7 +337,7 @@ add_f .proc
|
|||||||
sub_f .proc
|
sub_f .proc
|
||||||
; -- push f1-f2 on stack
|
; -- push f1-f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FSUB
|
jsr FSUB
|
||||||
@ -386,7 +347,7 @@ sub_f .proc
|
|||||||
mul_f .proc
|
mul_f .proc
|
||||||
; -- push f1*f2 on stack
|
; -- push f1*f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -394,19 +355,19 @@ mul_f .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
neg_f .proc
|
neg_f .proc
|
||||||
; -- push -flt back on stack
|
; -- toggle the sign bit on the stack
|
||||||
jsr pop_float_fac1
|
lda P8ESTACK_HI+3,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
eor #$80
|
||||||
jsr NEGOP
|
sta P8ESTACK_HI+3,x
|
||||||
jmp push_fac1_as_result
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
abs_f .proc
|
abs_f .proc
|
||||||
; -- push abs(float) on stack (as float)
|
; -- strip the sign bit on the stack
|
||||||
jsr pop_float_fac1
|
lda P8ESTACK_HI+3,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
and #$7f
|
||||||
jsr ABS
|
sta P8ESTACK_HI+3,x
|
||||||
jmp push_fac1_as_result
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
equal_f .proc
|
equal_f .proc
|
||||||
@ -415,24 +376,24 @@ equal_f .proc
|
|||||||
inx
|
inx
|
||||||
inx
|
inx
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO-3,x
|
lda P8ESTACK_LO-3,x
|
||||||
cmp c64.ESTACK_LO,x
|
cmp P8ESTACK_LO,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_LO-2,x
|
lda P8ESTACK_LO-2,x
|
||||||
cmp c64.ESTACK_LO+1,x
|
cmp P8ESTACK_LO+1,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_LO-1,x
|
lda P8ESTACK_LO-1,x
|
||||||
cmp c64.ESTACK_LO+2,x
|
cmp P8ESTACK_LO+2,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_HI-2,x
|
lda P8ESTACK_HI-2,x
|
||||||
cmp c64.ESTACK_HI+1,x
|
cmp P8ESTACK_HI+1,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_HI-1,x
|
lda P8ESTACK_HI-1,x
|
||||||
cmp c64.ESTACK_HI+2,x
|
cmp P8ESTACK_HI+2,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
_equals_true lda #1
|
_equals_true lda #1
|
||||||
_equals_store inx
|
_equals_store inx
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
_equals_false lda #0
|
_equals_false lda #0
|
||||||
beq _equals_store
|
beq _equals_store
|
||||||
@ -442,7 +403,7 @@ notequal_f .proc
|
|||||||
; -- are the two mflpt5 numbers on the stack different?
|
; -- are the two mflpt5 numbers on the stack different?
|
||||||
jsr equal_f
|
jsr equal_f
|
||||||
eor #1 ; invert the result
|
eor #1 ; invert the result
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -495,12 +456,12 @@ compare_floats .proc
|
|||||||
jsr MOVFM ; fac1 = flt1
|
jsr MOVFM ; fac1 = flt1
|
||||||
lda #<fmath_float2
|
lda #<fmath_float2
|
||||||
ldy #>fmath_float2
|
ldy #>fmath_float2
|
||||||
stx c64.SCRATCH_ZPREG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||||
ldx c64.SCRATCH_ZPREG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
_return_false lda #0
|
_return_false lda #0
|
||||||
_return_result sta c64.ESTACK_LO,x
|
_return_result sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
_return_true lda #1
|
_return_true lda #1
|
||||||
@ -510,7 +471,7 @@ _return_true lda #1
|
|||||||
func_sin .proc
|
func_sin .proc
|
||||||
; -- push sin(f) back onto stack
|
; -- push sin(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr SIN
|
jsr SIN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -518,7 +479,7 @@ func_sin .proc
|
|||||||
func_cos .proc
|
func_cos .proc
|
||||||
; -- push cos(f) back onto stack
|
; -- push cos(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr COS
|
jsr COS
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -526,7 +487,7 @@ func_cos .proc
|
|||||||
func_tan .proc
|
func_tan .proc
|
||||||
; -- push tan(f) back onto stack
|
; -- push tan(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr TAN
|
jsr TAN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -534,7 +495,7 @@ func_tan .proc
|
|||||||
func_atan .proc
|
func_atan .proc
|
||||||
; -- push atan(f) back onto stack
|
; -- push atan(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr ATN
|
jsr ATN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -542,7 +503,7 @@ func_atan .proc
|
|||||||
func_ln .proc
|
func_ln .proc
|
||||||
; -- push ln(f) back onto stack
|
; -- push ln(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr LOG
|
jsr LOG
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -550,7 +511,7 @@ func_ln .proc
|
|||||||
func_log2 .proc
|
func_log2 .proc
|
||||||
; -- push log base 2, ln(f)/ln(2), back onto stack
|
; -- push log base 2, ln(f)/ln(2), back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr LOG
|
jsr LOG
|
||||||
jsr MOVEF
|
jsr MOVEF
|
||||||
lda #<c64.FL_LOG2
|
lda #<c64.FL_LOG2
|
||||||
@ -562,7 +523,7 @@ func_log2 .proc
|
|||||||
|
|
||||||
func_sqrt .proc
|
func_sqrt .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr SQR
|
jsr SQR
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -570,7 +531,7 @@ func_sqrt .proc
|
|||||||
func_rad .proc
|
func_rad .proc
|
||||||
; -- convert degrees to radians (d * pi / 180)
|
; -- convert degrees to radians (d * pi / 180)
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<_pi_div_180
|
lda #<_pi_div_180
|
||||||
ldy #>_pi_div_180
|
ldy #>_pi_div_180
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -581,7 +542,7 @@ _pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
|||||||
func_deg .proc
|
func_deg .proc
|
||||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #<_one_over_pi_div_180
|
lda #<_one_over_pi_div_180
|
||||||
ldy #>_one_over_pi_div_180
|
ldy #>_one_over_pi_div_180
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -591,7 +552,7 @@ _one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
|||||||
|
|
||||||
func_round .proc
|
func_round .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr FADDH
|
jsr FADDH
|
||||||
jsr INT
|
jsr INT
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
@ -599,7 +560,7 @@ func_round .proc
|
|||||||
|
|
||||||
func_floor .proc
|
func_floor .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr INT
|
jsr INT
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -607,7 +568,7 @@ func_floor .proc
|
|||||||
func_ceil .proc
|
func_ceil .proc
|
||||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG
|
||||||
ldx #<fmath_float1
|
ldx #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
@ -617,53 +578,53 @@ func_ceil .proc
|
|||||||
jsr FCOMP
|
jsr FCOMP
|
||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
lda #<FL_FONE
|
lda #<FL_ONE_const
|
||||||
ldy #>FL_FONE
|
ldy #>FL_ONE_const
|
||||||
jsr FADD
|
jsr FADD
|
||||||
+ jmp push_fac1_as_result
|
+ jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_any_f .proc
|
func_any_f .proc
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x ; array size
|
lda P8ESTACK_LO,x ; array size
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
adc P8ZP_SCRATCH_B1 ; times 5 because of float
|
||||||
jmp prog8_lib.func_any_b._entry
|
jmp prog8_lib.func_any_b._entry
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_all_f .proc
|
func_all_f .proc
|
||||||
inx
|
inx
|
||||||
jsr prog8_lib.peek_address
|
jsr prog8_lib.peek_address
|
||||||
lda c64.ESTACK_LO,x ; array size
|
lda P8ESTACK_LO,x ; array size
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
adc P8ZP_SCRATCH_B1 ; times 5 because of float
|
||||||
tay
|
tay
|
||||||
dey
|
dey
|
||||||
- lda (c64.SCRATCH_ZPWORD1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
clc
|
clc
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
cpy #255
|
cpy #255
|
||||||
bne -
|
bne -
|
||||||
lda #1
|
lda #1
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
+ sta c64.ESTACK_LO+1,x
|
+ sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -674,26 +635,28 @@ func_max_f .proc
|
|||||||
ldy #>_largest_neg_float
|
ldy #>_largest_neg_float
|
||||||
_minmax_entry jsr MOVFM
|
_minmax_entry jsr MOVFM
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx floats_store_reg
|
||||||
- sty c64.SCRATCH_ZPREG
|
- sty P8ZP_SCRATCH_REG
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FCOMP
|
jsr FCOMP
|
||||||
_minmax_cmp cmp #255 ; modified
|
_minmax_cmp cmp #255 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
+ lda c64.SCRATCH_ZPWORD1
|
+ lda P8ZP_SCRATCH_W1
|
||||||
clc
|
clc
|
||||||
adc #5
|
adc #5
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ ldy c64.SCRATCH_ZPREG
|
+ ldy P8ZP_SCRATCH_REG
|
||||||
dey
|
dey
|
||||||
cpy #255
|
cpy #255
|
||||||
bne -
|
bne -
|
||||||
|
ldx floats_store_reg
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
||||||
.pend
|
.pend
|
||||||
@ -709,33 +672,35 @@ _largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_sum_f .proc
|
func_sum_f .proc
|
||||||
lda #<FL_ZERO
|
lda #<FL_ZERO_const
|
||||||
ldy #>FL_ZERO
|
ldy #>FL_ZERO_const
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx floats_store_reg
|
||||||
- sty c64.SCRATCH_ZPREG
|
- sty P8ZP_SCRATCH_REG
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FADD
|
jsr FADD
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
dey
|
dey
|
||||||
cpy #255
|
cpy #255
|
||||||
beq +
|
beq +
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
clc
|
clc
|
||||||
adc #5
|
adc #5
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc -
|
bcc -
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
inc P8ZP_SCRATCH_W1+1
|
||||||
bne -
|
bne -
|
||||||
+ jmp push_fac1_as_result
|
+ ldx floats_store_reg
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
sign_f .proc
|
sign_f .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
jsr SIGN
|
jsr SIGN
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -744,22 +709,22 @@ sign_f .proc
|
|||||||
set_0_array_float .proc
|
set_0_array_float .proc
|
||||||
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.ESTACK_LO,x
|
adc P8ESTACK_LO,x
|
||||||
tay
|
tay
|
||||||
lda #0
|
lda #0
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -767,17 +732,31 @@ set_0_array_float .proc
|
|||||||
set_array_float .proc
|
set_array_float .proc
|
||||||
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.ESTACK_LO,x
|
adc P8ESTACK_LO,x
|
||||||
clc
|
adc P8ZP_SCRATCH_W2
|
||||||
adc c64.SCRATCH_ZPWORD2
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
ldy c64.SCRATCH_ZPWORD2+1
|
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jmp copy_float
|
+ jmp copy_float
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
swap_floats .proc
|
||||||
|
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||||
|
ldy #4
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
pha
|
||||||
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
|
pla
|
||||||
|
sta (P8ZP_SCRATCH_W2),y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
.pend
|
@ -4,10 +4,10 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target c64
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
floats {
|
||||||
c64flt {
|
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
@ -35,13 +35,11 @@ c64flt {
|
|||||||
&float FL_TWOPI = $e2e5 ; 2 * PI
|
&float FL_TWOPI = $e2e5 ; 2 * PI
|
||||||
&float FL_FR4 = $e2ea ; .25
|
&float FL_FR4 = $e2ea ; .25
|
||||||
; oddly enough, 0.0 isn't available in the kernel.
|
; oddly enough, 0.0 isn't available in the kernel.
|
||||||
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
|
|
||||||
|
|
||||||
|
|
||||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||||
|
|
||||||
; checked functions below:
|
|
||||||
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||||
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
|
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
|
||||||
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
@ -52,22 +50,22 @@ romsub $bc0f = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
|||||||
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
romsub $bbd4 = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
|
|
||||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||||
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use floats.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
romsub $b1aa = FTOSWORDYA() clobbers(X) -> ubyte @ Y, ubyte @ A ; note: calls AYINT.
|
||||||
|
|
||||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||||
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
; (tip: use floats.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
romsub $b7f7 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||||
|
|
||||||
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
romsub $bc9b = QINT() clobbers(A,X,Y) ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||||
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||||
|
|
||||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||||
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
; (tip: use floats.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||||
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||||
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also floats.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
; there is also floats.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||||
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
; there is also floats.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||||
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||||
|
|
||||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||||
@ -91,6 +89,7 @@ romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1
|
|||||||
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||||
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||||
|
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
|
|
||||||
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
|
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
|
||||||
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
@ -163,9 +162,9 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
sta c64.SCRATCH_ZPREG
|
sta P8ZP_SCRATCH_REG
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -174,9 +173,9 @@ asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
|||||||
; ---- fac1 to signed word in A/Y
|
; ---- fac1 to signed word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||||
sta c64.SCRATCH_ZPREG
|
sta P8ZP_SCRATCH_REG
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -185,41 +184,34 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
; ---- fac1 to unsigned word in A/Y
|
; ---- fac1 to unsigned word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr GETADR ; this uses the inverse order, Y/A
|
jsr GETADR ; this uses the inverse order, Y/A
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPB1
|
ldy P8ZP_SCRATCH_B1
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
sub print_f (float value) {
|
||||||
; ---- prints the floating point value (without a newline) using basic rom routines.
|
; ---- prints the floating point value (without a newline).
|
||||||
%asm {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx floats_store_reg
|
||||||
lda #<value
|
lda #<value
|
||||||
ldy #>value
|
ldy #>value
|
||||||
jsr MOVFM ; load float into fac1
|
jsr MOVFM ; load float into fac1
|
||||||
jsr FOUT ; fac1 to string in A/Y
|
jsr FOUT ; fac1 to string in A/Y
|
||||||
jsr c64.STROUT ; print string in A/Y
|
sta P8ZP_SCRATCH_W1
|
||||||
ldx c64.SCRATCH_ZPREGX
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ ldx floats_store_reg
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_fln (float value) {
|
%asminclude "library:c64/floats.asm", ""
|
||||||
; ---- prints the floating point value (with a newline at the end) using basic rom routines
|
|
||||||
%asm {{
|
|
||||||
stx c64.SCRATCH_ZPREGX
|
|
||||||
lda #<value
|
|
||||||
ldy #>value
|
|
||||||
jsr MOVFM ; load float into fac1
|
|
||||||
jsr FPRINTLN ; print fac1 with newline
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%asminclude "library:c64floats.asm", ""
|
|
||||||
|
|
||||||
} ; ------ end of block c64flt
|
|
244
compiler/res/prog8lib/c64/graphics.p8
Normal file
244
compiler/res/prog8lib/c64/graphics.p8
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
%target c64
|
||||||
|
%import textio
|
||||||
|
|
||||||
|
; bitmap pixel graphics module for the C64
|
||||||
|
; only black/white monchrome 320x200 for now
|
||||||
|
; assumes bitmap screen memory is $2000-$3fff
|
||||||
|
|
||||||
|
graphics {
|
||||||
|
const uword BITMAP_ADDRESS = $2000
|
||||||
|
const uword WIDTH = 320
|
||||||
|
const ubyte HEIGHT = 200
|
||||||
|
|
||||||
|
sub enable_bitmap_mode() {
|
||||||
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
|
c64.SCROLY |= %00100000
|
||||||
|
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||||
|
clear_screen(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
|
memset(BITMAP_ADDRESS, 320*200/8, 0)
|
||||||
|
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
|
; Bresenham algorithm.
|
||||||
|
; This code special cases various quadrant loops to allow simple ++ and -- operations.
|
||||||
|
; TODO rewrite this in optimized assembly
|
||||||
|
if y1>y2 {
|
||||||
|
; make sure dy is always positive to avoid 8 instead of just 4 special cases
|
||||||
|
swap(x1, x2)
|
||||||
|
swap(y1, y2)
|
||||||
|
}
|
||||||
|
word @zp d = 0
|
||||||
|
ubyte positive_ix = true
|
||||||
|
word @zp dx = x2-x1
|
||||||
|
word @zp dy = y2-y1
|
||||||
|
if dx < 0 {
|
||||||
|
dx = -dx
|
||||||
|
positive_ix = false
|
||||||
|
}
|
||||||
|
dx *= 2
|
||||||
|
dy *= 2
|
||||||
|
internal_plotx = x1
|
||||||
|
|
||||||
|
if dx >= dy {
|
||||||
|
if positive_ix {
|
||||||
|
repeat {
|
||||||
|
internal_plot(y1)
|
||||||
|
if internal_plotx==x2
|
||||||
|
return
|
||||||
|
internal_plotx++
|
||||||
|
d += dy
|
||||||
|
if d > dx {
|
||||||
|
y1++
|
||||||
|
d -= dx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat {
|
||||||
|
internal_plot(y1)
|
||||||
|
if internal_plotx==x2
|
||||||
|
return
|
||||||
|
internal_plotx--
|
||||||
|
d += dy
|
||||||
|
if d > dx {
|
||||||
|
y1++
|
||||||
|
d -= dx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if positive_ix {
|
||||||
|
repeat {
|
||||||
|
internal_plot(y1)
|
||||||
|
if y1 == y2
|
||||||
|
return
|
||||||
|
y1++
|
||||||
|
d += dx
|
||||||
|
if d > dy {
|
||||||
|
internal_plotx++
|
||||||
|
d -= dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat {
|
||||||
|
internal_plot(y1)
|
||||||
|
if y1 == y2
|
||||||
|
return
|
||||||
|
y1++
|
||||||
|
d += dx
|
||||||
|
if d > dy {
|
||||||
|
internal_plotx--
|
||||||
|
d -= dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
; Midpoint algorithm
|
||||||
|
ubyte @zp ploty
|
||||||
|
ubyte @zp xx = radius
|
||||||
|
ubyte @zp yy = 0
|
||||||
|
byte @zp decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
internal_plotx = xcenter + xx
|
||||||
|
ploty = ycenter + yy
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter - xx
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter + xx
|
||||||
|
ploty = ycenter - yy
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter - xx
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter + yy
|
||||||
|
ploty = ycenter + xx
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter - yy
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter + yy
|
||||||
|
ploty = ycenter - xx
|
||||||
|
internal_plot(ploty)
|
||||||
|
internal_plotx = xcenter - yy
|
||||||
|
internal_plot(ploty)
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
; Midpoint algorithm, filled
|
||||||
|
ubyte xx = radius
|
||||||
|
ubyte yy = 0
|
||||||
|
byte decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
ubyte ycenter_plus_yy = ycenter + yy
|
||||||
|
ubyte ycenter_min_yy = ycenter - yy
|
||||||
|
ubyte ycenter_plus_xx = ycenter + xx
|
||||||
|
ubyte ycenter_min_xx = ycenter - xx
|
||||||
|
|
||||||
|
for internal_plotx in xcenter to xcenter+xx {
|
||||||
|
internal_plot(ycenter_plus_yy)
|
||||||
|
internal_plot(ycenter_min_yy)
|
||||||
|
}
|
||||||
|
for internal_plotx in xcenter-xx to xcenter-1 {
|
||||||
|
internal_plot(ycenter_plus_yy)
|
||||||
|
internal_plot(ycenter_min_yy)
|
||||||
|
}
|
||||||
|
for internal_plotx in xcenter to xcenter+yy {
|
||||||
|
internal_plot(ycenter_plus_xx)
|
||||||
|
internal_plot(ycenter_min_xx)
|
||||||
|
}
|
||||||
|
for internal_plotx in xcenter-yy to xcenter {
|
||||||
|
internal_plot(ycenter_plus_xx)
|
||||||
|
internal_plot(ycenter_min_xx)
|
||||||
|
}
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; here is the non-asm code for the plot routine below:
|
||||||
|
; sub plot_nonasm(uword px, ubyte py) {
|
||||||
|
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
|
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||||
|
; @(addr) |= ormask[lsb(px) & 7]
|
||||||
|
; }
|
||||||
|
|
||||||
|
asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||||
|
%asm {{
|
||||||
|
stx internal_plotx
|
||||||
|
sty internal_plotx+1
|
||||||
|
jmp internal_plot
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
; for efficiency of internal algorithms here is the internal plot routine
|
||||||
|
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
||||||
|
|
||||||
|
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
||||||
|
|
||||||
|
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||||
|
%asm {{
|
||||||
|
tay
|
||||||
|
lda internal_plotx+1
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lsr a ; 0
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda internal_plotx
|
||||||
|
pha
|
||||||
|
and #7
|
||||||
|
tax
|
||||||
|
|
||||||
|
lda _y_lookup_lo,y
|
||||||
|
clc
|
||||||
|
adc P8ZP_SCRATCH_W2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda _y_lookup_hi,y
|
||||||
|
adc P8ZP_SCRATCH_W2+1
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
|
||||||
|
pla ; internal_plotx
|
||||||
|
and #%11111000
|
||||||
|
tay
|
||||||
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
|
ora _ormask,x
|
||||||
|
sta (P8ZP_SCRATCH_W2),y
|
||||||
|
rts
|
||||||
|
|
||||||
|
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||||
|
|
||||||
|
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||||
|
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||||
|
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||||
|
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||||
|
|
||||||
|
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
||||||
|
|
||||||
|
_y_lookup_lo .byte <_plot_y_values
|
||||||
|
_y_lookup_hi .byte >_plot_y_values
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -5,20 +5,13 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target c64
|
||||||
|
|
||||||
c64 {
|
c64 {
|
||||||
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
|
|
||||||
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
|
|
||||||
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
|
|
||||||
&ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP
|
|
||||||
&ubyte SCRATCH_ZPREGX = $fa ; temp storage for X register (stack pointer)
|
|
||||||
&uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
|
||||||
&uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
|
||||||
|
|
||||||
|
|
||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
|
&ubyte STATUS = $90 ; kernel status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
@ -183,19 +176,11 @@ c64 {
|
|||||||
; ---- end of SID registers ----
|
; ---- end of SID registers ----
|
||||||
|
|
||||||
|
|
||||||
|
; ---- C64 ROM kernal routines ----
|
||||||
; ---- C64 basic routines ----
|
|
||||||
|
|
||||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
|
||||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
|
||||||
|
|
||||||
|
|
||||||
; ---- end of C64 basic routines ----
|
|
||||||
|
|
||||||
|
|
||||||
; ---- C64 kernal routines ----
|
|
||||||
|
|
||||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
||||||
|
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||||
|
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
@ -219,25 +204,245 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
|
|||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||||
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
|
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
; ---- end of C64 kernal routines ----
|
; ---- end of C64 ROM kernal routines ----
|
||||||
|
|
||||||
|
|
||||||
|
; ---- C64 specific system utility routines: ----
|
||||||
|
|
||||||
|
asmsub init_system() {
|
||||||
|
; Initializes the machine to a sane starting state.
|
||||||
|
; Called automatically by the loader program logic.
|
||||||
|
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||||
|
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||||
|
; Also a different color scheme is chosen to identify ourselves a little.
|
||||||
|
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
cld
|
||||||
|
lda #%00101111
|
||||||
|
sta $00
|
||||||
|
lda #%00100111
|
||||||
|
sta $01
|
||||||
|
jsr c64.IOINIT
|
||||||
|
jsr c64.RESTOR
|
||||||
|
jsr c64.CINT
|
||||||
|
lda #6
|
||||||
|
sta c64.EXTCOL
|
||||||
|
lda #7
|
||||||
|
sta c64.COLOR
|
||||||
|
lda #0
|
||||||
|
sta c64.BGCOL0
|
||||||
|
jsr disable_runstop_and_charsetswitch
|
||||||
|
clc
|
||||||
|
clv
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub reset_system() {
|
||||||
|
; Soft-reset the system back to Basic prompt.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #14
|
||||||
|
sta $01 ; bank the kernal in
|
||||||
|
jmp (c64.RESET_VEC)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub disable_runstop_and_charsetswitch() {
|
||||||
|
%asm {{
|
||||||
|
lda #$80
|
||||||
|
sta 657 ; disable charset switching
|
||||||
|
lda #239
|
||||||
|
sta 808 ; disable run/stop key
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_irqvec_excl() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #<_irq_handler
|
||||||
|
sta c64.CINV
|
||||||
|
lda #>_irq_handler
|
||||||
|
sta c64.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
_irq_handler jsr set_irqvec._irq_handler_init
|
||||||
|
jsr irq.irq
|
||||||
|
jsr set_irqvec._irq_handler_end
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||||
|
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_irqvec() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #<_irq_handler
|
||||||
|
sta c64.CINV
|
||||||
|
lda #>_irq_handler
|
||||||
|
sta c64.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
_irq_handler jsr _irq_handler_init
|
||||||
|
jsr irq.irq
|
||||||
|
jsr _irq_handler_end
|
||||||
|
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
||||||
|
|
||||||
|
_irq_handler_init
|
||||||
|
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||||
|
stx IRQ_X_REG
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
sta IRQ_SCRATCH_ZPB1
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
sta IRQ_SCRATCH_ZPREG
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
|
lda P8ZP_SCRATCH_W2+1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
|
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
cld
|
||||||
|
rts
|
||||||
|
|
||||||
|
_irq_handler_end
|
||||||
|
; restore all zp scratch registers and the X register
|
||||||
|
lda IRQ_SCRATCH_ZPB1
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
lda IRQ_SCRATCH_ZPREG
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
ldx IRQ_X_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
IRQ_X_REG .byte 0
|
||||||
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
|
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub restore_irqvec() {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #<c64.IRQDFRT
|
||||||
|
sta c64.CINV
|
||||||
|
lda #>c64.IRQDFRT
|
||||||
|
sta c64.CINV+1
|
||||||
|
lda #0
|
||||||
|
sta c64.IREQMASK ; disable raster irq
|
||||||
|
lda #%10000001
|
||||||
|
sta c64.CIA1ICR ; restore CIA1 irq
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
jsr _setup_raster_irq
|
||||||
|
lda #<_raster_irq_handler
|
||||||
|
sta c64.CINV
|
||||||
|
lda #>_raster_irq_handler
|
||||||
|
sta c64.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
|
||||||
|
_raster_irq_handler
|
||||||
|
jsr set_irqvec._irq_handler_init
|
||||||
|
jsr irq.irq
|
||||||
|
jsr set_irqvec._irq_handler_end
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
jmp c64.IRQDFRT
|
||||||
|
|
||||||
|
_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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
jsr set_rasterirq._setup_raster_irq
|
||||||
|
lda #<_raster_irq_handler
|
||||||
|
sta c64.CINV
|
||||||
|
lda #>_raster_irq_handler
|
||||||
|
sta c64.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
|
||||||
|
_raster_irq_handler
|
||||||
|
jsr set_irqvec._irq_handler_init
|
||||||
|
jsr irq.irq
|
||||||
|
jsr set_irqvec._irq_handler_end
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
; ---- end of C64 specific system utility routines ----
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
587
compiler/res/prog8lib/c64/textio.p8
Normal file
587
compiler/res/prog8lib/c64/textio.p8
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target c64
|
||||||
|
%import syslib
|
||||||
|
%import conv
|
||||||
|
|
||||||
|
|
||||||
|
txt {
|
||||||
|
|
||||||
|
const ubyte DEFAULT_WIDTH = 40
|
||||||
|
const ubyte DEFAULT_HEIGHT = 25
|
||||||
|
|
||||||
|
|
||||||
|
sub clear_screen() {
|
||||||
|
clear_screenchars(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
|
; ---- fill the character screen with the given fill character and character color.
|
||||||
|
; (assumes screen and color matrix are at their default addresses)
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr clear_screencolors
|
||||||
|
pla
|
||||||
|
jsr clear_screenchars
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen with the given fill character (leaves colors)
|
||||||
|
; (assumes screen matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
ldy #0
|
||||||
|
_loop sta c64.Screen,y
|
||||||
|
sta c64.Screen+$0100,y
|
||||||
|
sta c64.Screen+$0200,y
|
||||||
|
sta c64.Screen+$02e8,y
|
||||||
|
iny
|
||||||
|
bne _loop
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen colors with the given color (leaves characters).
|
||||||
|
; (assumes color matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
ldy #0
|
||||||
|
_loop sta c64.Colors,y
|
||||||
|
sta c64.Colors+$0100,y
|
||||||
|
sta c64.Colors+$0200,y
|
||||||
|
sta c64.Colors+$02e8,y
|
||||||
|
iny
|
||||||
|
bne _loop
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub color (ubyte txtcol) {
|
||||||
|
c64.COLOR = txtcol
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lowercase() {
|
||||||
|
c64.VMCSB |= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
sub uppercase() {
|
||||||
|
c64.VMCSB &= ~2
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character to the left
|
||||||
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
bcs +
|
||||||
|
jmp _scroll_screen
|
||||||
|
|
||||||
|
+ ; scroll the color memory
|
||||||
|
ldx #0
|
||||||
|
ldy #38
|
||||||
|
-
|
||||||
|
.for row=0, row<=24, row+=1
|
||||||
|
lda c64.Colors + 40*row + 1,x
|
||||||
|
sta c64.Colors + 40*row,x
|
||||||
|
.next
|
||||||
|
inx
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
_scroll_screen ; scroll the screen memory
|
||||||
|
ldx #0
|
||||||
|
ldy #38
|
||||||
|
-
|
||||||
|
.for row=0, row<=24, row+=1
|
||||||
|
lda c64.Screen + 40*row + 1,x
|
||||||
|
sta c64.Screen + 40*row,x
|
||||||
|
.next
|
||||||
|
inx
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
|
; ---- scroll the whole screen 1 character to the right
|
||||||
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
bcs +
|
||||||
|
jmp _scroll_screen
|
||||||
|
|
||||||
|
+ ; scroll the color memory
|
||||||
|
ldx #38
|
||||||
|
-
|
||||||
|
.for row=0, row<=24, row+=1
|
||||||
|
lda c64.Colors + 40*row + 0,x
|
||||||
|
sta c64.Colors + 40*row + 1,x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
_scroll_screen ; scroll the screen memory
|
||||||
|
ldx #38
|
||||||
|
-
|
||||||
|
.for row=0, row<=24, row+=1
|
||||||
|
lda c64.Screen + 40*row + 0,x
|
||||||
|
sta c64.Screen + 40*row + 1,x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
|
; ---- scroll the whole screen 1 character up
|
||||||
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
bcs +
|
||||||
|
jmp _scroll_screen
|
||||||
|
|
||||||
|
+ ; scroll the color memory
|
||||||
|
ldx #39
|
||||||
|
-
|
||||||
|
.for row=1, row<=24, row+=1
|
||||||
|
lda c64.Colors + 40*row,x
|
||||||
|
sta c64.Colors + 40*(row-1),x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
_scroll_screen ; scroll the screen memory
|
||||||
|
ldx #39
|
||||||
|
-
|
||||||
|
.for row=1, row<=24, row+=1
|
||||||
|
lda c64.Screen + 40*row,x
|
||||||
|
sta c64.Screen + 40*(row-1),x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||||
|
; ---- scroll the whole screen 1 character down
|
||||||
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
bcs +
|
||||||
|
jmp _scroll_screen
|
||||||
|
|
||||||
|
+ ; scroll the color memory
|
||||||
|
ldx #39
|
||||||
|
-
|
||||||
|
.for row=23, row>=0, row-=1
|
||||||
|
lda c64.Colors + 40*row,x
|
||||||
|
sta c64.Colors + 40*(row+1),x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
_scroll_screen ; scroll the screen memory
|
||||||
|
ldx #39
|
||||||
|
-
|
||||||
|
.for row=23, row>=0, row-=1
|
||||||
|
lda c64.Screen + 40*row,x
|
||||||
|
sta c64.Screen + 40*(row+1),x
|
||||||
|
.next
|
||||||
|
dex
|
||||||
|
bpl -
|
||||||
|
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||||
|
|
||||||
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print null terminated string from A/Y
|
||||||
|
; note: the compiler contains an optimization that will replace
|
||||||
|
; a call to this subroutine with a string argument of just one char,
|
||||||
|
; by just one call to c64.CHROUT of that single char.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
_print_byte_digits
|
||||||
|
pha
|
||||||
|
cpy #'0'
|
||||||
|
beq +
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
jmp _ones
|
||||||
|
+ pla
|
||||||
|
cmp #'0'
|
||||||
|
beq _ones
|
||||||
|
jsr c64.CHROUT
|
||||||
|
_ones txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the byte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
pha
|
||||||
|
cmp #0
|
||||||
|
bpl +
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ pla
|
||||||
|
jsr conv.byte2decimal
|
||||||
|
jmp print_ub._print_byte_digits
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
bcc +
|
||||||
|
pha
|
||||||
|
lda #'$'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
+ jsr conv.ubyte2hex
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'%'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ ldy #8
|
||||||
|
- lda #'0'
|
||||||
|
asl P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'1'
|
||||||
|
+ jsr c64.CHROUT
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubbin
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubbin
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubhex
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubhex
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq _allzero
|
||||||
|
cmp #'0'
|
||||||
|
bne _gotdigit
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
|
||||||
|
_gotdigit
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
|
bne _gotdigit
|
||||||
|
rts
|
||||||
|
_allzero
|
||||||
|
lda #'0'
|
||||||
|
jmp c64.CHROUT
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||||
|
%asm {{
|
||||||
|
cpy #0
|
||||||
|
bpl +
|
||||||
|
pha
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
eor #255
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
eor #255
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp print_uw
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||||
|
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||||
|
; It assumes the keyboard is selected as I/O channel!
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0 ; char counter = 0
|
||||||
|
- jsr c64.CHRIN
|
||||||
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
|
beq + ; yes, end.
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ lda #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||||
|
rts
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y) {
|
||||||
|
; ---- sets the character in the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda _screenrows+1,y
|
||||||
|
sta _mod+2
|
||||||
|
txa
|
||||||
|
clc
|
||||||
|
adc _screenrows,y
|
||||||
|
sta _mod+1
|
||||||
|
bcc +
|
||||||
|
inc _mod+2
|
||||||
|
+ pla
|
||||||
|
_mod sta $ffff ; modified
|
||||||
|
rts
|
||||||
|
|
||||||
|
_screenrows .word $0400 + range(0, 1000, 40)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||||
|
; ---- get the character in the screen matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda setchr._screenrows+1,y
|
||||||
|
sta _mod+2
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc setchr._screenrows,y
|
||||||
|
sta _mod+1
|
||||||
|
bcc _mod
|
||||||
|
inc _mod+2
|
||||||
|
_mod lda $ffff ; modified
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||||
|
; ---- set the color in A on the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda _colorrows+1,y
|
||||||
|
sta _mod+2
|
||||||
|
txa
|
||||||
|
clc
|
||||||
|
adc _colorrows,y
|
||||||
|
sta _mod+1
|
||||||
|
bcc +
|
||||||
|
inc _mod+2
|
||||||
|
+ pla
|
||||||
|
_mod sta $ffff ; modified
|
||||||
|
rts
|
||||||
|
|
||||||
|
_colorrows .word $d800 + range(0, 1000, 40)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||||
|
; ---- get the color in the screen color matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda setclr._colorrows+1,y
|
||||||
|
sta _mod+2
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc setclr._colorrows,y
|
||||||
|
sta _mod+1
|
||||||
|
bcc _mod
|
||||||
|
inc _mod+2
|
||||||
|
_mod lda $ffff ; modified
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||||
|
; ---- set char+color at the given position on the screen
|
||||||
|
%asm {{
|
||||||
|
lda row
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda setchr._screenrows+1,y
|
||||||
|
sta _charmod+2
|
||||||
|
adc #$d4
|
||||||
|
sta _colormod+2
|
||||||
|
lda setchr._screenrows,y
|
||||||
|
clc
|
||||||
|
adc column
|
||||||
|
sta _charmod+1
|
||||||
|
sta _colormod+1
|
||||||
|
bcc +
|
||||||
|
inc _charmod+2
|
||||||
|
inc _colormod+2
|
||||||
|
+ lda char
|
||||||
|
_charmod sta $ffff ; modified
|
||||||
|
lda charcolor
|
||||||
|
_colormod sta $ffff ; modified
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
|
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
tax
|
||||||
|
clc
|
||||||
|
jsr c64.PLOT
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen width (number of columns)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
txa
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen height (number of rows)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
tya
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
383
compiler/res/prog8lib/conv.p8
Normal file
383
compiler/res/prog8lib/conv.p8
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
; Prog8 definitions for number conversions routines.
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
|
||||||
|
conv {
|
||||||
|
|
||||||
|
; ----- number conversions to decimal strings
|
||||||
|
|
||||||
|
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
|
||||||
|
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||||
|
%asm {{
|
||||||
|
ldy #uword2decimal.ASCII_0_OFFSET
|
||||||
|
bne uword2decimal.hex_try200
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub uword2decimal (uword value @ AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||||
|
; ---- convert 16 bit uword in A/Y to decimal
|
||||||
|
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
||||||
|
; (these are terminated by a zero byte so they can be easily printed)
|
||||||
|
; also returns Y = 100's, A = 10's, X = 1's
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
|
||||||
|
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
||||||
|
;By Omegamatrix Further optimizations by tepples
|
||||||
|
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
|
||||||
|
|
||||||
|
;HexToDec99
|
||||||
|
; start in A
|
||||||
|
; end with A = 10's, decOnes (also in X)
|
||||||
|
|
||||||
|
;HexToDec255
|
||||||
|
; start in A
|
||||||
|
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||||
|
|
||||||
|
;HexToDec999
|
||||||
|
; start with A = high byte, Y = low byte
|
||||||
|
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||||
|
; requires 1 extra temp register on top of decOnes, could combine
|
||||||
|
; these two if HexToDec65535 was eliminated...
|
||||||
|
|
||||||
|
;HexToDec65535
|
||||||
|
; start with A/Y (low/high) as 16 bit value
|
||||||
|
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
|
||||||
|
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
|
||||||
|
|
||||||
|
|
||||||
|
ASCII_0_OFFSET = $30
|
||||||
|
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
|
||||||
|
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
|
||||||
|
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
|
||||||
|
|
||||||
|
|
||||||
|
HexToDec65535; SUBROUTINE
|
||||||
|
sty hexHigh ;3 @9
|
||||||
|
sta hexLow ;3 @12
|
||||||
|
tya
|
||||||
|
tax ;2 @14
|
||||||
|
lsr a ;2 @16
|
||||||
|
lsr a ;2 @18 integer divide 1024 (result 0-63)
|
||||||
|
|
||||||
|
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
|
||||||
|
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
|
||||||
|
|
||||||
|
;at this point we have a number 1-65 that we have to times by 24,
|
||||||
|
;add to original sum, and Mod 1024 to get a remainder 0-999
|
||||||
|
|
||||||
|
|
||||||
|
sta temp ;3 @25
|
||||||
|
asl a ;2 @27
|
||||||
|
adc temp ;3 @30 x3
|
||||||
|
tay ;2 @32
|
||||||
|
lsr a ;2 @34
|
||||||
|
lsr a ;2 @36
|
||||||
|
lsr a ;2 @38
|
||||||
|
lsr a ;2 @40
|
||||||
|
lsr a ;2 @42
|
||||||
|
tax ;2 @44
|
||||||
|
tya ;2 @46
|
||||||
|
asl a ;2 @48
|
||||||
|
asl a ;2 @50
|
||||||
|
asl a ;2 @52
|
||||||
|
clc ;2 @54
|
||||||
|
adc hexLow ;3 @57
|
||||||
|
sta hexLow ;3 @60
|
||||||
|
txa ;2 @62
|
||||||
|
adc hexHigh ;3 @65
|
||||||
|
sta hexHigh ;3 @68
|
||||||
|
ror a ;2 @70
|
||||||
|
lsr a ;2 @72
|
||||||
|
tay ;2 @74 integer divide 1,000 (result 0-65)
|
||||||
|
|
||||||
|
lsr a ;2 @76 split the 1,000 and 10,000 digit
|
||||||
|
tax ;2 @78
|
||||||
|
lda ShiftedBcdTab,x ;4 @82
|
||||||
|
tax ;2 @84
|
||||||
|
rol a ;2 @86
|
||||||
|
and #$0F ;2 @88
|
||||||
|
ora #ASCII_0_OFFSET
|
||||||
|
sta decThousands ;3 @91
|
||||||
|
txa ;2 @93
|
||||||
|
lsr a ;2 @95
|
||||||
|
lsr a ;2 @97
|
||||||
|
lsr a ;2 @99
|
||||||
|
ora #ASCII_0_OFFSET
|
||||||
|
sta decTenThousands ;3 @102
|
||||||
|
|
||||||
|
lda hexLow ;3 @105
|
||||||
|
cpy temp ;3 @108
|
||||||
|
bmi _doSubtract ;2³ @110/111
|
||||||
|
beq _useZero ;2³ @112/113
|
||||||
|
adc #23 + 24 ;2 @114
|
||||||
|
_doSubtract
|
||||||
|
sbc #23 ;2 @116
|
||||||
|
sta hexLow ;3 @119
|
||||||
|
_useZero
|
||||||
|
lda hexHigh ;3 @122
|
||||||
|
sbc #0 ;2 @124
|
||||||
|
|
||||||
|
Start100s
|
||||||
|
and #$03 ;2 @126
|
||||||
|
tax ;2 @128 0,1,2,3
|
||||||
|
cmp #2 ;2 @130
|
||||||
|
rol a ;2 @132 0,2,5,7
|
||||||
|
ora #ASCII_0_OFFSET
|
||||||
|
tay ;2 @134 Y = Hundreds digit
|
||||||
|
|
||||||
|
lda hexLow ;3 @137
|
||||||
|
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
|
||||||
|
bcs hex_doSub200 ;2³ @143/144
|
||||||
|
|
||||||
|
hex_try200
|
||||||
|
cmp #200 ;2 @145
|
||||||
|
bcc hex_try100 ;2³ @147/148
|
||||||
|
hex_doSub200
|
||||||
|
iny ;2 @149
|
||||||
|
iny ;2 @151
|
||||||
|
sbc #200 ;2 @153
|
||||||
|
hex_try100
|
||||||
|
cmp #100 ;2 @155
|
||||||
|
bcc HexToDec99 ;2³ @157/158
|
||||||
|
iny ;2 @159
|
||||||
|
sbc #100 ;2 @161
|
||||||
|
|
||||||
|
HexToDec99; SUBROUTINE
|
||||||
|
lsr a ;2 @163
|
||||||
|
tax ;2 @165
|
||||||
|
lda ShiftedBcdTab,x ;4 @169
|
||||||
|
tax ;2 @171
|
||||||
|
rol a ;2 @173
|
||||||
|
and #$0F ;2 @175
|
||||||
|
ora #ASCII_0_OFFSET
|
||||||
|
sta decOnes ;3 @178
|
||||||
|
txa ;2 @180
|
||||||
|
lsr a ;2 @182
|
||||||
|
lsr a ;2 @184
|
||||||
|
lsr a ;2 @186
|
||||||
|
ora #ASCII_0_OFFSET
|
||||||
|
|
||||||
|
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
|
||||||
|
sty decHundreds
|
||||||
|
sta decTens
|
||||||
|
ldx decOnes
|
||||||
|
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
|
||||||
|
|
||||||
|
|
||||||
|
HexToDec999; SUBROUTINE
|
||||||
|
sty hexLow ;3 @9
|
||||||
|
jmp Start100s ;3 @12
|
||||||
|
|
||||||
|
Mod100Tab
|
||||||
|
.byte 0,56,12,56+12
|
||||||
|
|
||||||
|
ShiftedBcdTab
|
||||||
|
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
|
||||||
|
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
|
||||||
|
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
|
||||||
|
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||||
|
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||||
|
|
||||||
|
decTenThousands .byte 0
|
||||||
|
decThousands .byte 0
|
||||||
|
decHundreds .byte 0
|
||||||
|
decTens .byte 0
|
||||||
|
decOnes .byte 0
|
||||||
|
.byte 0 ; zero-terminate the decimal output string
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- utility functions ----
|
||||||
|
|
||||||
|
|
||||||
|
asmsub byte2decimal (byte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
|
||||||
|
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||||
|
; note: if the number is negative, you have to deal with the '-' yourself!
|
||||||
|
%asm {{
|
||||||
|
cmp #0
|
||||||
|
bpl +
|
||||||
|
eor #255
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
+ jmp ubyte2decimal
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub ubyte2hex (ubyte value @ A) -> ubyte @ A, ubyte @ Y {
|
||||||
|
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
pha
|
||||||
|
and #$0f
|
||||||
|
tax
|
||||||
|
ldy _hex_digits,x
|
||||||
|
pla
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
tax
|
||||||
|
lda _hex_digits,x
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub uword2hex (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
tya
|
||||||
|
jsr ubyte2hex
|
||||||
|
sta output
|
||||||
|
sty output+1
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
jsr ubyte2hex
|
||||||
|
sta output+2
|
||||||
|
sty output+3
|
||||||
|
rts
|
||||||
|
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asmsub str2ubyte(str string @ AY) clobbers(Y) -> ubyte @A {
|
||||||
|
; -- returns the unsigned byte value of the string number argument in AY
|
||||||
|
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
; TODO implement optimized custom version of this instead of simply reusing str2uword
|
||||||
|
%asm {{
|
||||||
|
jmp str2uword
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asmsub str2byte(str string @ AY) clobbers(Y) -> ubyte @A {
|
||||||
|
; -- returns the signed byte value of the string number argument in AY
|
||||||
|
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
; TODO implement optimized custom version of this instead of simply reusing str2word
|
||||||
|
%asm {{
|
||||||
|
jmp str2word
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str2uword(str string @ AY) -> uword @ AY {
|
||||||
|
; -- returns the unsigned word value of the string number argument in AY
|
||||||
|
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
%asm {{
|
||||||
|
_result = P8ZP_SCRATCH_W2
|
||||||
|
sta _mod+1
|
||||||
|
sty _mod+2
|
||||||
|
ldy #0
|
||||||
|
sty _result
|
||||||
|
sty _result+1
|
||||||
|
_mod lda $ffff,y ; modified
|
||||||
|
sec
|
||||||
|
sbc #48
|
||||||
|
bpl +
|
||||||
|
_done ; return result
|
||||||
|
lda _result
|
||||||
|
ldy _result+1
|
||||||
|
rts
|
||||||
|
+ cmp #10
|
||||||
|
bcs _done
|
||||||
|
; add digit to result
|
||||||
|
pha
|
||||||
|
jsr _result_times_10
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc _result
|
||||||
|
sta _result
|
||||||
|
bcc +
|
||||||
|
inc _result+1
|
||||||
|
+ iny
|
||||||
|
bne _mod
|
||||||
|
; never reached
|
||||||
|
|
||||||
|
_result_times_10 ; (W*4 + W)*2
|
||||||
|
lda _result+1
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
lda _result
|
||||||
|
asl a
|
||||||
|
rol P8ZP_SCRATCH_REG
|
||||||
|
asl a
|
||||||
|
rol P8ZP_SCRATCH_REG
|
||||||
|
clc
|
||||||
|
adc _result
|
||||||
|
sta _result
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
adc _result+1
|
||||||
|
asl _result
|
||||||
|
rol a
|
||||||
|
sta _result+1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str2word(str string @ AY) -> word @ AY {
|
||||||
|
; -- returns the signed word value of the string number argument in AY
|
||||||
|
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
%asm {{
|
||||||
|
_result = P8ZP_SCRATCH_W2
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
sty _result
|
||||||
|
sty _result+1
|
||||||
|
sty _negative
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
cmp #'+'
|
||||||
|
bne +
|
||||||
|
iny
|
||||||
|
+ cmp #'-'
|
||||||
|
bne _parse
|
||||||
|
inc _negative
|
||||||
|
iny
|
||||||
|
_parse lda (P8ZP_SCRATCH_W1),y
|
||||||
|
sec
|
||||||
|
sbc #48
|
||||||
|
bpl _digit
|
||||||
|
_done ; return result
|
||||||
|
lda _negative
|
||||||
|
beq +
|
||||||
|
sec
|
||||||
|
lda #0
|
||||||
|
sbc _result
|
||||||
|
sta _result
|
||||||
|
lda #0
|
||||||
|
sbc _result+1
|
||||||
|
sta _result+1
|
||||||
|
+ lda _result
|
||||||
|
ldy _result+1
|
||||||
|
rts
|
||||||
|
_digit cmp #10
|
||||||
|
bcs _done
|
||||||
|
; add digit to result
|
||||||
|
pha
|
||||||
|
jsr str2uword._result_times_10
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc _result
|
||||||
|
sta _result
|
||||||
|
bcc +
|
||||||
|
inc _result+1
|
||||||
|
+ iny
|
||||||
|
bne _parse
|
||||||
|
; never reached
|
||||||
|
_negative .byte 0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
153
compiler/res/prog8lib/cx16/floats.p8
Normal file
153
compiler/res/prog8lib/cx16/floats.p8
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
; Prog8 definitions for floating point handling on the CommanderX16
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target cx16
|
||||||
|
%option enable_floats
|
||||||
|
|
||||||
|
floats {
|
||||||
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
|
||||||
|
const float PI = 3.141592653589793
|
||||||
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
|
|
||||||
|
; ---- ROM float functions ----
|
||||||
|
|
||||||
|
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||||
|
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||||
|
|
||||||
|
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||||
|
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||||
|
|
||||||
|
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||||
|
|
||||||
|
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||||
|
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||||
|
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||||
|
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||||
|
|
||||||
|
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||||
|
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||||
|
romsub $fe06 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||||
|
|
||||||
|
romsub $fe09 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||||
|
romsub $fe0c = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||||
|
romsub $fe0f = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
|
||||||
|
romsub $fe12 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||||
|
romsub $fe15 = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||||
|
romsub $fe1b = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
||||||
|
romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
||||||
|
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||||
|
romsub $fe27 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||||
|
romsub $fe2a = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||||
|
romsub $fe33 = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
|
romsub $fe36 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||||
|
romsub $fe3c = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||||
|
romsub $fe3f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||||
|
romsub $fe42 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||||
|
|
||||||
|
romsub $fe48 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||||
|
romsub $fe4b = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
|
romsub $fe4e = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||||
|
romsub $fe51 = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||||
|
romsub $fe54 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||||
|
romsub $fe5a = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||||
|
romsub $fe5d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||||
|
romsub $fe60 = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||||
|
romsub $fe6c = ABS() ; fac1 = ABS(fac1)
|
||||||
|
romsub $fe6f = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||||
|
romsub $fe78 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||||
|
romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||||
|
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||||
|
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
|
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
|
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
||||||
|
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
|
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
||||||
|
romsub $fea2 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
|
romsub $fea5 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
|
romsub $fea8 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
|
romsub $feab = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||||
|
romsub $feae = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||||
|
|
||||||
|
|
||||||
|
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
|
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
tya
|
||||||
|
ldy P8ZP_SCRATCH_W2
|
||||||
|
jsr GIVAYF ; load it as signed... correct afterwards
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
bpl +
|
||||||
|
lda #<_flt65536
|
||||||
|
ldy #>_flt65536
|
||||||
|
jsr FADD
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
|
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
tya
|
||||||
|
ldy P8ZP_SCRATCH_W2
|
||||||
|
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||||
|
; ---- fac1 to signed word in A/Y
|
||||||
|
%asm {{
|
||||||
|
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
tya
|
||||||
|
ldy P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||||
|
; ---- fac1 to unsigned word in A/Y
|
||||||
|
%asm {{
|
||||||
|
jsr GETADR ; this uses the inverse order, Y/A
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
tya
|
||||||
|
ldy P8ZP_SCRATCH_B1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_f (float value) {
|
||||||
|
; ---- prints the floating point value (without a newline).
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
lda #<value
|
||||||
|
ldy #>value
|
||||||
|
jsr MOVFM ; load float into fac1
|
||||||
|
jsr FOUT ; fac1 to string in A/Y
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
%asminclude "library:c64/floats.asm", ""
|
||||||
|
|
||||||
|
}
|
156
compiler/res/prog8lib/cx16/graphics.p8
Normal file
156
compiler/res/prog8lib/cx16/graphics.p8
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
%target cx16
|
||||||
|
%import syslib
|
||||||
|
|
||||||
|
; bitmap pixel graphics module for the CommanderX16
|
||||||
|
; wraps the graphics functions that are in ROM.
|
||||||
|
; only black/white monchrome 320x200 for now.
|
||||||
|
|
||||||
|
graphics {
|
||||||
|
const uword WIDTH = 320
|
||||||
|
const ubyte HEIGHT = 200
|
||||||
|
|
||||||
|
sub enable_bitmap_mode() {
|
||||||
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
|
void cx16.screen_set_mode($80)
|
||||||
|
cx16.r0 = 0
|
||||||
|
cx16.GRAPH_init()
|
||||||
|
clear_screen(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||||
|
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
|
||||||
|
cx16.GRAPH_clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
|
cx16.r0 = x1
|
||||||
|
cx16.r1 = y1
|
||||||
|
cx16.r2 = x2
|
||||||
|
cx16.r3 = y2
|
||||||
|
cx16.GRAPH_draw_line()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
;cx16.r0 = xcenter - radius/2
|
||||||
|
;cx16.r1 = ycenter - radius/2
|
||||||
|
;cx16.r2 = radius*2
|
||||||
|
;cx16.r3 = radius*2
|
||||||
|
;cx16.GRAPH_draw_oval(false) ; TODO currently is not implemented on cx16, does a BRK
|
||||||
|
|
||||||
|
; Midpoint algorithm
|
||||||
|
ubyte @zp xx = radius
|
||||||
|
ubyte @zp yy = 0
|
||||||
|
byte @zp decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
cx16.r0 = xcenter + xx
|
||||||
|
cx16.r1 = ycenter + yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + xx
|
||||||
|
cx16.r1 = ycenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + yy
|
||||||
|
cx16.r1 = ycenter + xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter + yy
|
||||||
|
cx16.r1 = ycenter - xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r0 = xcenter - yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0 {
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
} else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
; cx16.r0 = xcenter - radius/2
|
||||||
|
; cx16.r1 = ycenter - radius/2
|
||||||
|
; cx16.r2 = radius*2
|
||||||
|
; cx16.r3 = radius*2
|
||||||
|
; cx16.GRAPH_draw_oval(true) ; TODO currently is not implemented on cx16, does a BRK
|
||||||
|
|
||||||
|
ubyte xx = radius
|
||||||
|
ubyte yy = 0
|
||||||
|
byte decisionOver2 = 1-xx as byte
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
ubyte ycenter_plus_yy = ycenter + yy
|
||||||
|
ubyte ycenter_min_yy = ycenter - yy
|
||||||
|
ubyte ycenter_plus_xx = ycenter + xx
|
||||||
|
ubyte ycenter_min_xx = ycenter - xx
|
||||||
|
uword @zp plotx
|
||||||
|
|
||||||
|
for plotx in xcenter to xcenter+xx {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter-xx to xcenter-1 {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_yy
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter to xcenter+yy {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
for plotx in xcenter-yy to xcenter {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ycenter_plus_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
cx16.r1 = ycenter_min_xx
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub plot(uword plotx, ubyte ploty) {
|
||||||
|
cx16.r0 = plotx
|
||||||
|
cx16.r1 = ploty
|
||||||
|
cx16.FB_cursor_position()
|
||||||
|
cx16.FB_set_pixel(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
291
compiler/res/prog8lib/cx16/syslib.p8
Normal file
291
compiler/res/prog8lib/cx16/syslib.p8
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
; Prog8 definitions for the CommanderX16
|
||||||
|
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target cx16
|
||||||
|
|
||||||
|
|
||||||
|
c64 {
|
||||||
|
|
||||||
|
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||||
|
|
||||||
|
; STROUT --> use screen.print
|
||||||
|
; CLEARSCR -> use screen.clear_screen
|
||||||
|
; HOMECRSR -> use screen.plot
|
||||||
|
|
||||||
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
|
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
|
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||||
|
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
|
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||||
|
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||||
|
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||||
|
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||||
|
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||||
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||||
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||||
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||||
|
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||||
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
cx16 {
|
||||||
|
|
||||||
|
; 65c02 hardware vectors:
|
||||||
|
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||||
|
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||||
|
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
|
|
||||||
|
; the sixteen virtual 16-bit registers
|
||||||
|
&uword r0 = $0002
|
||||||
|
&uword r1 = $0004
|
||||||
|
&uword r2 = $0006
|
||||||
|
&uword r3 = $0008
|
||||||
|
&uword r4 = $000a
|
||||||
|
&uword r5 = $000c
|
||||||
|
&uword r6 = $000e
|
||||||
|
&uword r7 = $0010
|
||||||
|
&uword r8 = $0012
|
||||||
|
&uword r9 = $0014
|
||||||
|
&uword r10 = $0016
|
||||||
|
&uword r11 = $0018
|
||||||
|
&uword r12 = $001a
|
||||||
|
&uword r13 = $001c
|
||||||
|
&uword r14 = $001e
|
||||||
|
&uword r15 = $0020
|
||||||
|
|
||||||
|
; VERA registers
|
||||||
|
|
||||||
|
const uword VERA_BASE = $9F20
|
||||||
|
&ubyte VERA_ADDR_L = VERA_BASE + $0000
|
||||||
|
&ubyte VERA_ADDR_M = VERA_BASE + $0001
|
||||||
|
&ubyte VERA_ADDR_H = VERA_BASE + $0002
|
||||||
|
&ubyte VERA_DATA0 = VERA_BASE + $0003
|
||||||
|
&ubyte VERA_DATA1 = VERA_BASE + $0004
|
||||||
|
&ubyte VERA_CTRL = VERA_BASE + $0005
|
||||||
|
&ubyte VERA_IEN = VERA_BASE + $0006
|
||||||
|
&ubyte VERA_ISR = VERA_BASE + $0007
|
||||||
|
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $0008
|
||||||
|
&ubyte VERA_DC_VIDEO = VERA_BASE + $0009
|
||||||
|
&ubyte VERA_DC_HSCALE = VERA_BASE + $000A
|
||||||
|
&ubyte VERA_DC_VSCALE = VERA_BASE + $000B
|
||||||
|
&ubyte VERA_DC_BORDER = VERA_BASE + $000C
|
||||||
|
&ubyte VERA_DC_HSTART = VERA_BASE + $0009
|
||||||
|
&ubyte VERA_DC_HSTOP = VERA_BASE + $000A
|
||||||
|
&ubyte VERA_DC_VSTART = VERA_BASE + $000B
|
||||||
|
&ubyte VERA_DC_VSTOP = VERA_BASE + $000C
|
||||||
|
&ubyte VERA_L0_CONFIG = VERA_BASE + $000D
|
||||||
|
&ubyte VERA_L0_MAPBASE = VERA_BASE + $000E
|
||||||
|
&ubyte VERA_L0_TILEBASE = VERA_BASE + $000F
|
||||||
|
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $0010
|
||||||
|
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $0011
|
||||||
|
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $0012
|
||||||
|
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $0013
|
||||||
|
&ubyte VERA_L1_CONFIG = VERA_BASE + $0014
|
||||||
|
&ubyte VERA_L1_MAPBASE = VERA_BASE + $0015
|
||||||
|
&ubyte VERA_L1_TILEBASE = VERA_BASE + $0016
|
||||||
|
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $0017
|
||||||
|
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $0018
|
||||||
|
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $0019
|
||||||
|
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $001A
|
||||||
|
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $001B
|
||||||
|
&ubyte VERA_AUDIO_RATE = VERA_BASE + $001C
|
||||||
|
&ubyte VERA_AUDIO_DATA = VERA_BASE + $001D
|
||||||
|
&ubyte VERA_SPI_DATA = VERA_BASE + $001E
|
||||||
|
&ubyte VERA_SPI_CTRL = VERA_BASE + $001F
|
||||||
|
; VERA_PSG_BASE = $1F9C0
|
||||||
|
; VERA_PALETTE_BASE = $1FA00
|
||||||
|
; VERA_SPRITES_BASE = $1FC00
|
||||||
|
|
||||||
|
; I/O
|
||||||
|
|
||||||
|
const uword via1 = $9f60 ;VIA 6522 #1
|
||||||
|
&ubyte d1prb = via1+0
|
||||||
|
&ubyte d1pra = via1+1
|
||||||
|
&ubyte d1ddrb = via1+2
|
||||||
|
&ubyte d1ddra = via1+3
|
||||||
|
&ubyte d1t1l = via1+4
|
||||||
|
&ubyte d1t1h = via1+5
|
||||||
|
&ubyte d1t1ll = via1+6
|
||||||
|
&ubyte d1t1lh = via1+7
|
||||||
|
&ubyte d1t2l = via1+8
|
||||||
|
&ubyte d1t2h = via1+9
|
||||||
|
&ubyte d1sr = via1+10
|
||||||
|
&ubyte d1acr = via1+11
|
||||||
|
&ubyte d1pcr = via1+12
|
||||||
|
&ubyte d1ifr = via1+13
|
||||||
|
&ubyte d1ier = via1+14
|
||||||
|
&ubyte d1ora = via1+15
|
||||||
|
|
||||||
|
const uword via2 = $9f70 ;VIA 6522 #2
|
||||||
|
&ubyte d2prb =via2+0
|
||||||
|
&ubyte d2pra =via2+1
|
||||||
|
&ubyte d2ddrb =via2+2
|
||||||
|
&ubyte d2ddra =via2+3
|
||||||
|
&ubyte d2t1l =via2+4
|
||||||
|
&ubyte d2t1h =via2+5
|
||||||
|
&ubyte d2t1ll =via2+6
|
||||||
|
&ubyte d2t1lh =via2+7
|
||||||
|
&ubyte d2t2l =via2+8
|
||||||
|
&ubyte d2t2h =via2+9
|
||||||
|
&ubyte d2sr =via2+10
|
||||||
|
&ubyte d2acr =via2+11
|
||||||
|
&ubyte d2pcr =via2+12
|
||||||
|
&ubyte d2ifr =via2+13
|
||||||
|
&ubyte d2ier =via2+14
|
||||||
|
&ubyte d2ora =via2+15
|
||||||
|
|
||||||
|
|
||||||
|
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||||
|
; spelling of the names is taken from the Commander X-16 rom sources
|
||||||
|
|
||||||
|
; supported C128 additions
|
||||||
|
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
|
||||||
|
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
|
||||||
|
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
|
||||||
|
romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc
|
||||||
|
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||||
|
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||||
|
romsub $ff6e = jsrfar()
|
||||||
|
romsub $ff74 = fetch(ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||||
|
romsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||||
|
romsub $ff7a = cmpare(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X)
|
||||||
|
romsub $ff7d = primm()
|
||||||
|
|
||||||
|
; X16 additions
|
||||||
|
romsub $ff44 = macptr() clobbers(A,X,Y)
|
||||||
|
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||||
|
romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
|
||||||
|
romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
|
||||||
|
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||||
|
romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||||
|
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
|
romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L
|
||||||
|
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) ; outout args: r0, r1, r2, r3L
|
||||||
|
|
||||||
|
; TODO specify the correct clobbers for alle these functions below, we now assume all 3 regs are clobbered
|
||||||
|
|
||||||
|
; high level graphics & fonts
|
||||||
|
romsub $ff20 = GRAPH_init() clobbers(A,X,Y) ; uses vectors=r0
|
||||||
|
romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
|
||||||
|
romsub $ff26 = GRAPH_set_window() clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
||||||
|
romsub $ff2c = GRAPH_draw_line() clobbers(A,X,Y) ; uses x1=r0, y1=r1, x2=r2, y2=r3
|
||||||
|
romsub $ff2f = GRAPH_draw_rect(ubyte fill @Pc) clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3, cornerradius=r4
|
||||||
|
romsub $ff32 = GRAPH_move_rect() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, width=r4, height=r5
|
||||||
|
romsub $ff35 = GRAPH_draw_oval(ubyte fill @Pc) clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $ff38 = GRAPH_draw_image() clobbers(A,X,Y) ; uses x=r0, y=r1, ptr=r2, width=r3, height=r4
|
||||||
|
romsub $ff3b = GRAPH_set_font() clobbers(A,X,Y) ; uses ptr=r0
|
||||||
|
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
|
||||||
|
romsub $ff41 = GRAPH_put_char(ubyte char @A) clobbers(A,X,Y) ; uses x=r0, y=r1
|
||||||
|
|
||||||
|
; framebuffer
|
||||||
|
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||||
|
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A ; also outputs width=r0, height=r1
|
||||||
|
romsub $fefc = FB_set_palette(ubyte index @A, ubyte bytecount @X) clobbers(A,X,Y) ; also uses pointer=r0
|
||||||
|
romsub $feff = FB_cursor_position() clobbers(A,X,Y) ; uses x=r0, y=r1
|
||||||
|
romsub $ff02 = FB_cursor_next_line() clobbers(A,X,Y) ; uses x=r0
|
||||||
|
romsub $ff05 = FB_get_pixel() clobbers(X,Y) -> ubyte @A
|
||||||
|
romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
|
||||||
|
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
|
||||||
|
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L
|
||||||
|
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
|
||||||
|
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
|
||||||
|
|
||||||
|
; misc
|
||||||
|
romsub $fef0 = sprite_set_image(ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc ; also uses pixels=r0, mask=r1, bpp=r2L
|
||||||
|
romsub $fef3 = sprite_set_position(ubyte number @A) clobbers(A,X,Y) ; also uses x=r0 and y=r1
|
||||||
|
romsub $fee4 = memory_fill(ubyte value @A) clobbers(A,X,Y) ; uses address=r0, num_bytes=r1
|
||||||
|
romsub $fee7 = memory_copy() clobbers(A,X,Y) ; uses source=r0, target=r1, num_bytes=r2
|
||||||
|
romsub $feea = memory_crc() clobbers(A,X,Y) ; uses address=r0, num_bytes=r1 result->r2
|
||||||
|
romsub $feed = memory_decompress() clobbers(A,X,Y) ; uses input=r0, output=r1 result->r1
|
||||||
|
romsub $fedb = console_init() clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
|
||||||
|
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||||
|
romsub $fed8 = console_put_image() clobbers(A,X,Y) ; uses ptr=r0, width=r1, height=r2
|
||||||
|
romsub $fed5 = console_set_paging_message() clobbers(A,X,Y) ; uses messageptr=r0
|
||||||
|
romsub $fed2 = kbdbuf_put(ubyte key @A) clobbers(A,X,Y)
|
||||||
|
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
|
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; ---- end of kernal routines ----
|
||||||
|
|
||||||
|
asmsub init_system() {
|
||||||
|
; Initializes the machine to a sane starting state.
|
||||||
|
; Called automatically by the loader program logic.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
cld
|
||||||
|
;stz $00
|
||||||
|
;stz $01
|
||||||
|
;stz d1prb ; select rom bank 0
|
||||||
|
lda #$80
|
||||||
|
sta VERA_CTRL
|
||||||
|
jsr c64.IOINIT
|
||||||
|
jsr c64.RESTOR
|
||||||
|
jsr c64.CINT
|
||||||
|
lda #$90 ; black
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #1 ; swap fg/bg
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #$9e ; yellow
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #147 ; clear screen
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #0
|
||||||
|
tax
|
||||||
|
tay
|
||||||
|
clc
|
||||||
|
clv
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub reset_system() {
|
||||||
|
; Soft-reset the system back to Basic prompt.
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda #14
|
||||||
|
sta $01
|
||||||
|
stz cx16.d1prb ; bank the kernal in
|
||||||
|
jmp (cx16.RESET_VEC)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
696
compiler/res/prog8lib/cx16/textio.p8
Normal file
696
compiler/res/prog8lib/cx16/textio.p8
Normal file
@ -0,0 +1,696 @@
|
|||||||
|
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||||
|
;
|
||||||
|
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
;
|
||||||
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
|
%target cx16
|
||||||
|
%import syslib
|
||||||
|
%import conv
|
||||||
|
|
||||||
|
|
||||||
|
txt {
|
||||||
|
|
||||||
|
const ubyte DEFAULT_WIDTH = 80
|
||||||
|
const ubyte DEFAULT_HEIGHT = 60
|
||||||
|
|
||||||
|
|
||||||
|
sub clear_screen() {
|
||||||
|
clear_screenchars(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
|
; ---- fill the character screen with the given fill character and character color.
|
||||||
|
%asm {{
|
||||||
|
sty _ly+1
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
phy
|
||||||
|
_ly ldy #1 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sty cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
ply
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen with the given fill character (leaves colors)
|
||||||
|
; (assumes screen matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00100000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||||
|
stz cx16.VERA_ADDR_L ; start at (0,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||||
|
; ---- clear the character screen colors with the given color (leaves characters).
|
||||||
|
; (assumes color matrix is at the default address)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sta _la+1
|
||||||
|
jsr c64.SCREEN ; get dimensions in X/Y
|
||||||
|
txa
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
sta _lx+1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
lda #%00100000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 2, bank 0.
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_L ; start at (1,0)
|
||||||
|
stz cx16.VERA_ADDR_M
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
_la lda #0 ; modified
|
||||||
|
- sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dey
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M ; next line
|
||||||
|
bra _lx
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
||||||
|
|
||||||
|
sub color (ubyte txtcol) {
|
||||||
|
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||||
|
}
|
||||||
|
|
||||||
|
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||||
|
c64.CHROUT(color_to_charcode[bgcol & 15])
|
||||||
|
c64.CHROUT(1) ; switch fg and bg colors
|
||||||
|
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lowercase() {
|
||||||
|
cx16.screen_set_charset(3, 0) ; lowercase charset
|
||||||
|
}
|
||||||
|
|
||||||
|
sub uppercase() {
|
||||||
|
cx16.screen_set_charset(2, 0) ; uppercase charset
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_left (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character to the left
|
||||||
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
dex
|
||||||
|
stx _lx+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
stz cx16.VERA_CTRL ; data port 0: source column
|
||||||
|
lda #%00010000 ; auto increment 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda #2
|
||||||
|
sta cx16.VERA_ADDR_L ; begin in column 1
|
||||||
|
ldy P8ZP_SCRATCH_B1
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||||
|
lda #%00010000 ; auto increment 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
bpl _nextline
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_right (ubyte dummy @ Pc) clobbers(A) {
|
||||||
|
; ---- scroll the whole screen 1 character to the right
|
||||||
|
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
dex
|
||||||
|
stx _lx+1
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
dea
|
||||||
|
sta _rcol+1
|
||||||
|
ina
|
||||||
|
ina
|
||||||
|
sta _rcol2+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
stz cx16.VERA_CTRL ; data port 0: source column
|
||||||
|
lda #%00011000 ; auto decrement 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
_rcol lda #79*2-1 ; modified
|
||||||
|
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
||||||
|
ldy P8ZP_SCRATCH_B1
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||||
|
lda #%00011000 ; auto decrement 1
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
_rcol2 lda #79*2+1 ; modified
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
|
||||||
|
_lx ldx #0 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
bpl _nextline
|
||||||
|
|
||||||
|
lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_up (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character up
|
||||||
|
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
stx _nextline+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_ADDR_M ; start at second line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||||
|
stz cx16.VERA_ADDR_M ; start at top line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
ldx #80 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_CTRL ; data port 0
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
inc cx16.VERA_ADDR_M
|
||||||
|
bra _nextline
|
||||||
|
|
||||||
|
+ lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub scroll_down (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||||
|
; ---- scroll the whole screen 1 character down
|
||||||
|
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||||
|
; Carry flag is a dummy on the cx16
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr c64.SCREEN
|
||||||
|
stx _nextline+1
|
||||||
|
dey
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL ; data port 0 is source
|
||||||
|
dey
|
||||||
|
sty cx16.VERA_ADDR_M ; start at line before bottom line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1 is destination
|
||||||
|
iny
|
||||||
|
sty cx16.VERA_ADDR_M ; start at bottom line
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
lda #%00010000
|
||||||
|
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||||
|
|
||||||
|
_nextline
|
||||||
|
ldx #80 ; modified
|
||||||
|
- lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy char
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
sta cx16.VERA_DATA1 ; copy color
|
||||||
|
dex
|
||||||
|
bne -
|
||||||
|
dec P8ZP_SCRATCH_B1
|
||||||
|
beq +
|
||||||
|
stz cx16.VERA_CTRL ; data port 0
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
dec cx16.VERA_ADDR_M
|
||||||
|
lda #1
|
||||||
|
sta cx16.VERA_CTRL ; data port 1
|
||||||
|
stz cx16.VERA_ADDR_L
|
||||||
|
dec cx16.VERA_ADDR_M
|
||||||
|
bra _nextline
|
||||||
|
|
||||||
|
+ lda #0
|
||||||
|
sta cx16.VERA_CTRL
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse.
|
||||||
|
|
||||||
|
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print null terminated string from A/Y
|
||||||
|
; note: the compiler contains an optimization that will replace
|
||||||
|
; a call to this subroutine with a string argument of just one char,
|
||||||
|
; by just one call to c64.CHROUT of that single char.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
_print_byte_digits
|
||||||
|
pha
|
||||||
|
cpy #'0'
|
||||||
|
beq +
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
jmp _ones
|
||||||
|
+ pla
|
||||||
|
cmp #'0'
|
||||||
|
beq _ones
|
||||||
|
jsr c64.CHROUT
|
||||||
|
_ones txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- print the byte in A in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
cmp #0
|
||||||
|
bpl +
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ pla
|
||||||
|
jsr conv.byte2decimal
|
||||||
|
jmp print_ub._print_byte_digits
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
bcc +
|
||||||
|
pha
|
||||||
|
lda #'$'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
+ jsr conv.ubyte2hex
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'%'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ ldy #8
|
||||||
|
- lda #'0'
|
||||||
|
asl P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'1'
|
||||||
|
+ jsr c64.CHROUT
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubbin
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubbin
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||||
|
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr print_ubhex
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
jmp print_ubhex
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
plx
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq _allzero
|
||||||
|
cmp #'0'
|
||||||
|
bne _gotdigit
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
|
||||||
|
_gotdigit
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
|
bne _gotdigit
|
||||||
|
rts
|
||||||
|
_allzero
|
||||||
|
lda #'0'
|
||||||
|
jmp c64.CHROUT
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||||
|
%asm {{
|
||||||
|
cpy #0
|
||||||
|
bpl +
|
||||||
|
pha
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
eor #255
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
eor #255
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp print_uw
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||||
|
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||||
|
; It assumes the keyboard is selected as I/O channel!
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0 ; char counter = 0
|
||||||
|
- jsr c64.CHRIN
|
||||||
|
cmp #$0d ; return (ascii 13) pressed?
|
||||||
|
beq + ; yes, end.
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ lda #0
|
||||||
|
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||||
|
rts
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A) {
|
||||||
|
; ---- sets the character in the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
|
; ---- get the character in the screen matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
asl a
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
||||||
|
; ---- set the color in A on the screen matrix at the given position
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
txa
|
||||||
|
asl a
|
||||||
|
ina
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
pla
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||||
|
; ---- get the color in the screen color matrix at the given location
|
||||||
|
%asm {{
|
||||||
|
asl a
|
||||||
|
ina
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||||
|
; ---- set char+color at the given position on the screen
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
lda column
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
ldy row
|
||||||
|
lda charcolor
|
||||||
|
and #$0f
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda char
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
inx
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda cx16.VERA_DATA0
|
||||||
|
and #$f0
|
||||||
|
ora P8ZP_SCRATCH_B1
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
|
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
tax
|
||||||
|
clc
|
||||||
|
jsr c64.PLOT
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub width() clobbers(X,Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen width (number of columns)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
txa
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub height() clobbers(X, Y) -> ubyte @A {
|
||||||
|
; -- returns the text screen height (number of rows)
|
||||||
|
%asm {{
|
||||||
|
jsr c64.SCREEN
|
||||||
|
tya
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%import c64lib
|
|
||||||
|
|
||||||
math {
|
math {
|
||||||
%asminclude "library:math.asm", ""
|
%asminclude "library:math.asm", ""
|
||||||
}
|
}
|
||||||
|
2100
compiler/res/prog8lib/prog8_lib.asm
Normal file
2100
compiler/res/prog8lib/prog8_lib.asm
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%import c64lib
|
|
||||||
|
|
||||||
prog8_lib {
|
prog8_lib {
|
||||||
%asminclude "library:prog8lib.asm", ""
|
%asminclude "library:prog8_lib.asm", ""
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
|||||||
2.0
|
4.5
|
||||||
|
@ -4,12 +4,10 @@ import kotlinx.cli.*
|
|||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.CompilationResult
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import prog8.compiler.target.c64.Petscii
|
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import java.io.IOException
|
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardWatchEventKinds
|
import java.nio.file.StandardWatchEventKinds
|
||||||
@ -35,12 +33,13 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
|
|||||||
|
|
||||||
private fun compileMain(args: Array<String>) {
|
private fun compileMain(args: Array<String>) {
|
||||||
val cli = CommandLineInterface("prog8compiler")
|
val cli = CommandLineInterface("prog8compiler")
|
||||||
val startEmulator by cli.flagArgument("-emu", "auto-start the Vice C-64 emulator after successful compilation")
|
val startEmulator by cli.flagArgument("-emu", "auto-start emulator after successful compilation")
|
||||||
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
||||||
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
||||||
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||||
val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64")
|
val compilationTarget by cli.flagValueArgument("-target", "compilertarget",
|
||||||
|
"target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available", C64Target.name)
|
||||||
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -49,26 +48,6 @@ private fun compileMain(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
when(compilationTarget) {
|
|
||||||
"c64" -> {
|
|
||||||
with(CompilationTarget) {
|
|
||||||
name = "c64"
|
|
||||||
machine = C64MachineDefinition
|
|
||||||
encodeString = { str, altEncoding ->
|
|
||||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
}
|
|
||||||
decodeString = { bytes, altEncoding ->
|
|
||||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
|
||||||
}
|
|
||||||
asmGenerator = ::AsmGen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
System.err.println("invalid compilation target")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val outputPath = pathFrom(outputDir)
|
val outputPath = pathFrom(outputDir)
|
||||||
if(!outputPath.toFile().isDirectory) {
|
if(!outputPath.toFile().isDirectory) {
|
||||||
System.err.println("Output path doesn't exist")
|
System.err.println("Output path doesn't exist")
|
||||||
@ -83,7 +62,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
println("Continuous watch mode active. Main module: $filepath")
|
println("Continuous watch mode active. Main module: $filepath")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
||||||
println("Imported files (now watching:)")
|
println("Imported files (now watching:)")
|
||||||
for (importedFile in compilationResult.importedFiles) {
|
for (importedFile in compilationResult.importedFiles) {
|
||||||
print(" ")
|
print(" ")
|
||||||
@ -108,7 +87,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
} catch (x: ParsingFailedError) {
|
} catch (x: ParsingFailedError) {
|
||||||
@ -121,20 +100,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else if(startEmulator) {
|
else if(startEmulator) {
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
CompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
|
||||||
println("\nStarting C-64 emulator $emulator...")
|
|
||||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
|
||||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
|
||||||
val process: Process
|
|
||||||
try {
|
|
||||||
process=processb.start()
|
|
||||||
} catch(x: IOException) {
|
|
||||||
continue // try the next emulator executable
|
|
||||||
}
|
|
||||||
process.waitFor()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,14 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
|
output("(")
|
||||||
expr.left.accept(this)
|
expr.left.accept(this)
|
||||||
if(expr.operator.any { it.isLetter() })
|
if(expr.operator.any { it.isLetter() })
|
||||||
output(" ${expr.operator} ")
|
output(" ${expr.operator} ")
|
||||||
else
|
else
|
||||||
output(expr.operator)
|
output(expr.operator)
|
||||||
expr.right.accept(this)
|
expr.right.accept(this)
|
||||||
|
output(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(directive: Directive) {
|
override fun visit(directive: Directive) {
|
||||||
@ -85,7 +87,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
DataType.ARRAY_W -> "word["
|
DataType.ARRAY_W -> "word["
|
||||||
DataType.ARRAY_F -> "float["
|
DataType.ARRAY_F -> "float["
|
||||||
DataType.STRUCT -> "" // the name of the struct is enough
|
DataType.STRUCT -> "" // the name of the struct is enough
|
||||||
else -> "?????2"
|
else -> "?????"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,12 +104,21 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
|
|
||||||
|
// if the vardecl is a parameter of a subroutine, don't output it again
|
||||||
|
val paramNames = (decl.definingScope() as? Subroutine)?.parameters?.map { it.name }
|
||||||
|
if(paramNames!=null && decl.name in paramNames)
|
||||||
|
return
|
||||||
|
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR -> {}
|
VarDeclType.VAR -> {}
|
||||||
VarDeclType.CONST -> output("const ")
|
VarDeclType.CONST -> output("const ")
|
||||||
VarDeclType.MEMORY -> output("&")
|
VarDeclType.MEMORY -> output("&")
|
||||||
}
|
}
|
||||||
output(decl.struct?.name ?: "")
|
|
||||||
|
if(decl.datatype==DataType.STRUCT && decl.struct!=null)
|
||||||
|
output(decl.struct!!.name)
|
||||||
|
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.index.accept(this)
|
decl.arraysize!!.index.accept(this)
|
||||||
@ -134,7 +145,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
param.second.stack -> "stack"
|
param.second.stack -> "stack"
|
||||||
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
||||||
param.second.statusflag!=null -> param.second.statusflag.toString()
|
param.second.statusflag!=null -> param.second.statusflag.toString()
|
||||||
else -> "?????1"
|
else -> "?????"
|
||||||
}
|
}
|
||||||
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
||||||
if(param.first!==subroutine.parameters.last())
|
if(param.first!==subroutine.parameters.last())
|
||||||
@ -177,8 +188,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
private fun outputStatements(statements: List<Statement>) {
|
private fun outputStatements(statements: List<Statement>) {
|
||||||
for(stmt in statements) {
|
for(stmt in statements) {
|
||||||
if(stmt is VarDecl && stmt.autogeneratedDontRemove)
|
|
||||||
continue // skip autogenerated decls (to avoid generating a newline)
|
|
||||||
outputi("")
|
outputi("")
|
||||||
stmt.accept(this)
|
stmt.accept(this)
|
||||||
output("\n")
|
output("\n")
|
||||||
@ -283,33 +292,33 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if(binExpr!=null && binExpr.left isSameAs assignment.target
|
||||||
|
&& binExpr.operator !in setOf("and", "or", "xor")
|
||||||
|
&& binExpr.operator !in comparisonOperators) {
|
||||||
|
// we only support the inplace assignments of the form A = A <operator> <value>
|
||||||
|
assignment.target.accept(this)
|
||||||
|
output(" ${binExpr.operator}= ")
|
||||||
|
binExpr.right.accept(this)
|
||||||
|
} else {
|
||||||
assignment.target.accept(this)
|
assignment.target.accept(this)
|
||||||
if (assignment.aug_op != null)
|
|
||||||
output(" ${assignment.aug_op} ")
|
|
||||||
else
|
|
||||||
output(" = ")
|
output(" = ")
|
||||||
assignment.value.accept(this)
|
assignment.value.accept(this)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||||
postIncrDecr.target.accept(this)
|
postIncrDecr.target.accept(this)
|
||||||
output(postIncrDecr.operator)
|
output(postIncrDecr.operator)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(contStmt: Continue) {
|
|
||||||
output("continue")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(breakStmt: Break) {
|
override fun visit(breakStmt: Break) {
|
||||||
output("break")
|
output("break")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop) {
|
override fun visit(forLoop: ForLoop) {
|
||||||
output("for ")
|
output("for ")
|
||||||
if(forLoop.loopRegister!=null)
|
forLoop.loopVar.accept(this)
|
||||||
output(forLoop.loopRegister.toString())
|
|
||||||
else
|
|
||||||
forLoop.loopVar!!.accept(this)
|
|
||||||
output(" in ")
|
output(" in ")
|
||||||
forLoop.iterable.accept(this)
|
forLoop.iterable.accept(this)
|
||||||
output(" ")
|
output(" ")
|
||||||
@ -323,16 +332,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(foreverLoop: ForeverLoop) {
|
|
||||||
output("forever ")
|
|
||||||
foreverLoop.body.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop) {
|
override fun visit(repeatLoop: RepeatLoop) {
|
||||||
output("repeat ")
|
output("repeat ")
|
||||||
|
repeatLoop.iterations?.accept(this)
|
||||||
|
output(" ")
|
||||||
repeatLoop.body.accept(this)
|
repeatLoop.body.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
|
output("do ")
|
||||||
|
untilLoop.body.accept(this)
|
||||||
output(" until ")
|
output(" until ")
|
||||||
repeatLoop.untilCondition.accept(this)
|
untilLoop.condition.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(returnStmt: Return) {
|
override fun visit(returnStmt: Return) {
|
||||||
@ -348,12 +359,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget) {
|
override fun visit(assignTarget: AssignTarget) {
|
||||||
if(assignTarget.register!=null)
|
|
||||||
output(assignTarget.register.toString())
|
|
||||||
else {
|
|
||||||
assignTarget.memoryAddress?.accept(this)
|
assignTarget.memoryAddress?.accept(this)
|
||||||
assignTarget.identifier?.accept(this)
|
assignTarget.identifier?.accept(this)
|
||||||
}
|
|
||||||
assignTarget.arrayindexed?.accept(this)
|
assignTarget.arrayindexed?.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,10 +401,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
outputlni("}}")
|
outputlni("}}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(registerExpr: RegisterExpr) {
|
|
||||||
output(registerExpr.register.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||||
output(builtinFunctionStatementPlaceholder.name)
|
output(builtinFunctionStatementPlaceholder.name)
|
||||||
}
|
}
|
||||||
@ -431,12 +434,4 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structLv: StructLiteralValue) {
|
|
||||||
outputListMembers(structLv.values.asSequence(), '{', '}')
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
output("; NOP @ ${nopStatement.position} $nopStatement")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
@ -45,11 +45,19 @@ interface IFunctionCall {
|
|||||||
var args: MutableList<Expression>
|
var args: MutableList<Expression>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AsmGenInfo {
|
||||||
|
var usedRegsaveA = false
|
||||||
|
var usedRegsaveX = false
|
||||||
|
var usedRegsaveY = false
|
||||||
|
}
|
||||||
|
|
||||||
interface INameScope {
|
interface INameScope {
|
||||||
val name: String
|
val name: String
|
||||||
val position: Position
|
val position: Position
|
||||||
val statements: MutableList<Statement>
|
val statements: MutableList<Statement>
|
||||||
val parent: Node
|
val parent: Node
|
||||||
|
val asmGenInfo: AsmGenInfo
|
||||||
|
|
||||||
fun linkParents(parent: Node)
|
fun linkParents(parent: Node)
|
||||||
|
|
||||||
@ -58,7 +66,7 @@ interface INameScope {
|
|||||||
when(stmt) {
|
when(stmt) {
|
||||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
||||||
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is RepeatLoop -> if(stmt.body.name==name) return stmt.body
|
is UntilLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
||||||
is BranchStatement -> {
|
is BranchStatement -> {
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
if(stmt.truepart.name==name) return stmt.truepart
|
||||||
@ -138,7 +146,15 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} else {
|
} else {
|
||||||
// unqualified name, find the scope the localContext is in, look in that first
|
// unqualified name
|
||||||
|
// special case: the do....until statement can also look INSIDE the anonymous scope
|
||||||
|
if(localContext.parent.parent is UntilLoop) {
|
||||||
|
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.getLabelOrVariable(scopedName[0])
|
||||||
|
if(symbolFromInnerScope!=null)
|
||||||
|
return symbolFromInnerScope
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the scope the localContext is in, look in that first
|
||||||
var statementScope = localContext
|
var statementScope = localContext
|
||||||
while(statementScope !is ParentSentinel) {
|
while(statementScope !is ParentSentinel) {
|
||||||
val localScope = statementScope.definingScope()
|
val localScope = statementScope.definingScope()
|
||||||
@ -156,6 +172,7 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||||
|
fun containsNoVars() = statements.all { it !is VarDecl }
|
||||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
fun remove(stmt: Statement) {
|
||||||
@ -175,8 +192,8 @@ interface INameScope {
|
|||||||
find(it.truepart)
|
find(it.truepart)
|
||||||
find(it.elsepart)
|
find(it.elsepart)
|
||||||
}
|
}
|
||||||
|
is UntilLoop -> find(it.body)
|
||||||
is RepeatLoop -> find(it.body)
|
is RepeatLoop -> find(it.body)
|
||||||
is ForeverLoop -> find(it.body)
|
|
||||||
is WhileLoop -> find(it.body)
|
is WhileLoop -> find(it.body)
|
||||||
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
||||||
else -> { /* do nothing */ }
|
else -> { /* do nothing */ }
|
||||||
@ -187,6 +204,14 @@ interface INameScope {
|
|||||||
find(this)
|
find(this)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun nextSibling(stmt: Statement): Statement? {
|
||||||
|
val nextIdx = statements.indexOfFirst { it===stmt } + 1
|
||||||
|
return if(nextIdx < statements.size)
|
||||||
|
statements[nextIdx]
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAssignable {
|
interface IAssignable {
|
||||||
@ -224,13 +249,13 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
|
|||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach {
|
modules.forEach {
|
||||||
it.linkParents(this)
|
it.linkParents(namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(node is Module && replacement is Module)
|
require(node is Module && replacement is Module)
|
||||||
val idx = modules.indexOf(node)
|
val idx = modules.indexOfFirst { it===node }
|
||||||
modules[idx] = replacement
|
modules[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -244,10 +269,14 @@ class Module(override val name: String,
|
|||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
lateinit var program: Program
|
lateinit var program: Program
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
val importedBy = mutableListOf<Module>()
|
val importedBy = mutableListOf<Module>()
|
||||||
val imports = mutableSetOf<Module>()
|
val imports = mutableSetOf<Module>()
|
||||||
|
|
||||||
var loadAddress: Int = 0 // can be set with the %address directive
|
val loadAddress: Int by lazy {
|
||||||
|
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
|
||||||
|
address
|
||||||
|
}
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -257,14 +286,13 @@ class Module(override val name: String,
|
|||||||
override fun definingScope(): INameScope = program.namespace
|
override fun definingScope(): INameScope = program.namespace
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(node is Statement && replacement is Statement)
|
require(node is Statement && replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -275,6 +303,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
override val statements = mutableListOf<Statement>()
|
override val statements = mutableListOf<Statement>()
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach { it.linkParents(this) }
|
modules.forEach { it.linkParents(this) }
|
||||||
@ -305,11 +334,19 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// special case: the do....until statement can also look INSIDE the anonymous scope
|
||||||
|
if(localContext.parent.parent is UntilLoop) {
|
||||||
|
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
||||||
|
if(symbolFromInnerScope!=null)
|
||||||
|
return symbolFromInnerScope
|
||||||
|
}
|
||||||
|
|
||||||
// lookup something from the module.
|
// lookup something from the module.
|
||||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
||||||
null -> null
|
null -> null
|
||||||
else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position)
|
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,6 +356,7 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
|||||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||||
override var statements = mutableListOf<Statement>()
|
override var statements = mutableListOf<Statement>()
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
val customTokensource = this.start.tokenSource as? CustomLexer
|
val customTokensource = this.start.tokenSource as? CustomLexer
|
||||||
val filename =
|
val filename =
|
||||||
when {
|
when {
|
||||||
customTokensource!=null -> customTokensource.modulePath.fileName.toString()
|
customTokensource!=null -> customTokensource.modulePath.toString()
|
||||||
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
||||||
else -> File(start.inputStream.sourceName).name
|
else -> File(start.inputStream.sourceName).name
|
||||||
}
|
}
|
||||||
@ -161,14 +161,15 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
if(vardecl!=null) return vardecl
|
if(vardecl!=null) return vardecl
|
||||||
|
|
||||||
assignment()?.let {
|
assignment()?.let {
|
||||||
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
|
return Assignment(it.assign_target().toAst(), it.expression().toAst(), it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
augassignment()?.let {
|
augassignment()?.let {
|
||||||
return Assignment(it.assign_target().toAst(),
|
// replace A += X with A = A + X
|
||||||
it.operator.text,
|
val target = it.assign_target().toAst()
|
||||||
it.expression().toAst(),
|
val oper = it.operator.text.substringBefore('=')
|
||||||
it.toPosition())
|
val expression = BinaryExpression(target.toExpression(), oper, it.expression().toAst(), it.expression().toPosition())
|
||||||
|
return Assignment(it.assign_target().toAst(), expression, it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
postincrdecr()?.let {
|
postincrdecr()?.let {
|
||||||
@ -205,21 +206,18 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
val forloop = forloop()?.toAst()
|
val forloop = forloop()?.toAst()
|
||||||
if(forloop!=null) return forloop
|
if(forloop!=null) return forloop
|
||||||
|
|
||||||
val repeatloop = repeatloop()?.toAst()
|
val untilloop = untilloop()?.toAst()
|
||||||
if(repeatloop!=null) return repeatloop
|
if(untilloop!=null) return untilloop
|
||||||
|
|
||||||
val whileloop = whileloop()?.toAst()
|
val whileloop = whileloop()?.toAst()
|
||||||
if(whileloop!=null) return whileloop
|
if(whileloop!=null) return whileloop
|
||||||
|
|
||||||
val foreverloop = foreverloop()?.toAst()
|
val repeatloop = repeatloop()?.toAst()
|
||||||
if(foreverloop!=null) return foreverloop
|
if(repeatloop!=null) return repeatloop
|
||||||
|
|
||||||
val breakstmt = breakstmt()?.toAst()
|
val breakstmt = breakstmt()?.toAst()
|
||||||
if(breakstmt!=null) return breakstmt
|
if(breakstmt!=null) return breakstmt
|
||||||
|
|
||||||
val continuestmt = continuestmt()?.toAst()
|
|
||||||
if(continuestmt!=null) return continuestmt
|
|
||||||
|
|
||||||
val whenstmt = whenstmt()?.toAst()
|
val whenstmt = whenstmt()?.toAst()
|
||||||
if(whenstmt!=null) return whenstmt
|
if(whenstmt!=null) return whenstmt
|
||||||
|
|
||||||
@ -247,7 +245,7 @@ private class AsmsubDecl(val name: String,
|
|||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<Register>)
|
val asmClobbers: Set<CpuRegister>)
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
||||||
val name = identifier().text
|
val name = identifier().text
|
||||||
@ -256,7 +254,7 @@ private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
|||||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
||||||
val normalReturntypes = returns.map { it.type }
|
val normalReturntypes = returns.map { it.type }
|
||||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, false) }
|
||||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||||
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
||||||
}
|
}
|
||||||
@ -265,7 +263,7 @@ private class AsmSubroutineParameter(name: String,
|
|||||||
type: DataType,
|
type: DataType,
|
||||||
val registerOrPair: RegisterOrPair?,
|
val registerOrPair: RegisterOrPair?,
|
||||||
val statusflag: Statusflag?,
|
val statusflag: Statusflag?,
|
||||||
val stack: Boolean,
|
// TODO implement: val stack: Boolean,
|
||||||
position: Position) : SubroutineParameter(name, type, position)
|
position: Position) : SubroutineParameter(name, type, position)
|
||||||
|
|
||||||
private class AsmSubroutineReturn(val type: DataType,
|
private class AsmSubroutineReturn(val type: DataType,
|
||||||
@ -274,24 +272,42 @@ private class AsmSubroutineReturn(val type: DataType,
|
|||||||
val stack: Boolean,
|
val stack: Boolean,
|
||||||
val position: Position)
|
val position: Position)
|
||||||
|
|
||||||
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
|
|
||||||
= this.register().asSequence().map { it.toAst() }.toSet()
|
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||||
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
|
= asmsub_return().map {
|
||||||
|
val register = it.identifier()?.toAst()
|
||||||
|
var registerorpair: RegisterOrPair? = null
|
||||||
|
var statusregister: Statusflag? = null
|
||||||
|
if(register!=null) {
|
||||||
|
when (val name = register.nameInSource.single()) {
|
||||||
|
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||||
|
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||||
|
else -> throw FatalAstException("invalid register or status flag in $it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmSubroutineReturn(
|
||||||
|
it.datatype().toAst(),
|
||||||
|
registerorpair,
|
||||||
|
statusregister,
|
||||||
|
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||||
= asmsub_param().map {
|
= asmsub_param().map {
|
||||||
val vardecl = it.vardecl()
|
val vardecl = it.vardecl()
|
||||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
||||||
AsmSubroutineParameter(vardecl.varname.text, datatype,
|
val register = it.identifier()?.toAst()
|
||||||
it.registerorpair()?.toAst(),
|
var registerorpair: RegisterOrPair? = null
|
||||||
it.statusregister()?.toAst(),
|
var statusregister: Statusflag? = null
|
||||||
!it.stack?.text.isNullOrEmpty(), toPosition())
|
if(register!=null) {
|
||||||
|
when (val name = register.nameInSource.single()) {
|
||||||
|
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||||
|
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||||
|
else -> throw FatalAstException("invalid register or status flag '$name'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
|
||||||
|
|
||||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||||
val void = this.VOID() != null
|
val void = this.VOID() != null
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
@ -350,23 +366,22 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
||||||
val register = register()?.toAst()
|
|
||||||
val identifier = scoped_identifier()
|
val identifier = scoped_identifier()
|
||||||
return when {
|
return when {
|
||||||
register!=null -> AssignTarget(register, null, null, null, toPosition())
|
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
|
||||||
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
|
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition())
|
||||||
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
|
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
||||||
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
|
||||||
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
|
private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
|
||||||
|
val names = this.identifier().map { it.toAst().nameInSource.single() }
|
||||||
|
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
||||||
|
|
||||||
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
||||||
ArrayIndex(expression().toAst(), toPosition())
|
ArrayIndex(expression().toAst(), toPosition())
|
||||||
|
|
||||||
@ -382,7 +397,7 @@ private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
fun makeLiteral(text: String, radix: Int): NumericLiteral {
|
||||||
val integer: Int
|
val integer: Int
|
||||||
var datatype = DataType.UBYTE
|
var datatype = DataType.UBYTE
|
||||||
when (radix) {
|
when (radix) {
|
||||||
@ -420,14 +435,14 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
|||||||
}
|
}
|
||||||
else -> throw FatalAstException("invalid radix")
|
else -> throw FatalAstException("invalid radix")
|
||||||
}
|
}
|
||||||
return NumericLiteral(integer, if (forceWord) DataType.UWORD else datatype)
|
return NumericLiteral(integer, datatype)
|
||||||
}
|
}
|
||||||
val terminal: TerminalNode = children[0] as TerminalNode
|
val terminal: TerminalNode = children[0] as TerminalNode
|
||||||
val integerPart = this.intpart.text
|
val integerPart = this.intpart.text
|
||||||
return when (terminal.symbol.type) {
|
return when (terminal.symbol.type) {
|
||||||
prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10, wordsuffix()!=null)
|
prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10)
|
||||||
prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16, wordsuffix()!=null)
|
prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16)
|
||||||
prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2, wordsuffix()!=null)
|
prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
|
||||||
else -> throw FatalAstException(terminal.text)
|
else -> throw FatalAstException(terminal.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,7 +471,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
litval.charliteral()!=null -> {
|
litval.charliteral()!=null -> {
|
||||||
try {
|
try {
|
||||||
val cc=litval.charliteral()
|
val cc=litval.charliteral()
|
||||||
NumericLiteralValue(DataType.UBYTE, CompilationTarget.encodeString(
|
NumericLiteralValue(DataType.UBYTE, CompilationTarget.instance.encodeString(
|
||||||
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
||||||
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
||||||
} catch (ce: CharConversionException) {
|
} catch (ce: CharConversionException) {
|
||||||
@ -469,18 +484,11 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
// the ConstantFold takes care of that and converts the type if needed.
|
// the ConstantFold takes care of that and converts the type if needed.
|
||||||
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
|
||||||
}
|
}
|
||||||
litval.structliteral()!=null -> {
|
|
||||||
val values = litval.structliteral().expression().map { it.toAst() }
|
|
||||||
StructLiteralValue(values, litval.toPosition())
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("invalid parsed literal")
|
else -> throw FatalAstException("invalid parsed literal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(register()!=null)
|
|
||||||
return RegisterExpr(register().toAst(), register().toPosition())
|
|
||||||
|
|
||||||
if(scoped_identifier()!=null)
|
if(scoped_identifier()!=null)
|
||||||
return scoped_identifier().toAst()
|
return scoped_identifier().toAst()
|
||||||
|
|
||||||
@ -572,19 +580,16 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
|||||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||||
|
|
||||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||||
val loopregister = register()?.toAst()
|
val loopvar = identifier().toAst()
|
||||||
val loopvar = identifier()?.toAst()
|
|
||||||
val iterable = expression()!!.toAst()
|
val iterable = expression()!!.toAst()
|
||||||
val scope =
|
val scope =
|
||||||
if(statement()!=null)
|
if(statement()!=null)
|
||||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||||
else
|
else
|
||||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||||
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
|
return ForLoop(loopvar, iterable, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
|
||||||
|
|
||||||
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
||||||
|
|
||||||
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
||||||
@ -595,19 +600,20 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
|||||||
return WhileLoop(condition, scope, toPosition())
|
return WhileLoop(condition, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.ForeverloopContext.toAst(): ForeverLoop {
|
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||||
|
val iterations = expression()?.toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||||
?: statement().toPosition())
|
?: statement().toPosition())
|
||||||
return ForeverLoop(scope, toPosition())
|
return RepeatLoop(iterations, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
private fun prog8Parser.UntilloopContext.toAst(): UntilLoop {
|
||||||
val untilCondition = expression().toAst()
|
val untilCondition = expression().toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||||
?: statement().toPosition())
|
?: statement().toPosition())
|
||||||
return RepeatLoop(scope, untilCondition, toPosition())
|
return UntilLoop(scope, untilCondition, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
||||||
@ -641,7 +647,21 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
internal fun escape(str: String): String {
|
||||||
|
val es2 = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
||||||
|
val es = str.map {
|
||||||
|
when(it) {
|
||||||
|
'\t' -> "\\t"
|
||||||
|
'\n' -> "\\n"
|
||||||
|
'\r' -> "\\r"
|
||||||
|
'"' -> "\\\""
|
||||||
|
in '\u8000'..'\u80ff' -> "\\x" + (it.toInt() - 0x8000).toString(16).padStart(2, '0')
|
||||||
|
in '\u0000'..'\u00ff' -> it.toString()
|
||||||
|
else -> "\\u" + it.toInt().toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return es.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
internal fun unescape(str: String, position: Position): String {
|
internal fun unescape(str: String, position: Position): String {
|
||||||
val result = mutableListOf<Char>()
|
val result = mutableListOf<Char>()
|
||||||
@ -655,9 +675,15 @@ internal fun unescape(str: String, position: Position): String {
|
|||||||
'n' -> '\n'
|
'n' -> '\n'
|
||||||
'r' -> '\r'
|
'r' -> '\r'
|
||||||
'"' -> '"'
|
'"' -> '"'
|
||||||
|
'\'' -> '\''
|
||||||
'u' -> {
|
'u' -> {
|
||||||
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||||
}
|
}
|
||||||
|
'x' -> {
|
||||||
|
// special hack 0x8000..0x80ff will be outputted verbatim without encoding
|
||||||
|
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||||
|
(0x8000 + hex).toChar()
|
||||||
|
}
|
||||||
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
|
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,10 +21,9 @@ enum class DataType {
|
|||||||
STRUCT; // pass by reference
|
STRUCT; // pass by reference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the type assignable to the given other type?
|
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||||
*/
|
*/
|
||||||
infix fun isAssignableTo(targetType: DataType) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
// what types are assignable to others without loss of precision?
|
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
||||||
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
||||||
@ -40,16 +39,20 @@ enum class DataType {
|
|||||||
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
||||||
|
|
||||||
infix fun largerThan(other: DataType) =
|
infix fun largerThan(other: DataType) =
|
||||||
when(this) {
|
when {
|
||||||
in ByteDatatypes -> false
|
this == other -> false
|
||||||
in WordDatatypes -> other in ByteDatatypes
|
this in ByteDatatypes -> false
|
||||||
|
this in WordDatatypes -> other in ByteDatatypes
|
||||||
|
this==STR && other==UWORD || this==UWORD && other==STR -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun equalsSize(other: DataType) =
|
infix fun equalsSize(other: DataType) =
|
||||||
when(this) {
|
when {
|
||||||
in ByteDatatypes -> other in ByteDatatypes
|
this == other -> true
|
||||||
in WordDatatypes -> other in WordDatatypes
|
this in ByteDatatypes -> other in ByteDatatypes
|
||||||
|
this in WordDatatypes -> other in WordDatatypes
|
||||||
|
this==STR && other==UWORD || this==UWORD && other==STR -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,14 +60,14 @@ enum class DataType {
|
|||||||
return when(this) {
|
return when(this) {
|
||||||
in ByteDatatypes -> 1
|
in ByteDatatypes -> 1
|
||||||
in WordDatatypes -> 2
|
in WordDatatypes -> 2
|
||||||
FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE
|
FLOAT -> CompilationTarget.instance.machine.FLOAT_MEM_SIZE
|
||||||
in PassByReferenceDatatypes -> 2
|
in PassByReferenceDatatypes -> CompilationTarget.instance.machine.POINTER_MEM_SIZE
|
||||||
else -> -9999999
|
else -> -9999999
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Register {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y
|
Y
|
||||||
@ -76,14 +79,23 @@ enum class RegisterOrPair {
|
|||||||
Y,
|
Y,
|
||||||
AX,
|
AX,
|
||||||
AY,
|
AY,
|
||||||
XY
|
XY;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { values().map { it.toString()} }
|
||||||
|
}
|
||||||
|
|
||||||
} // only used in parameter and return value specs in asm subroutines
|
} // only used in parameter and return value specs in asm subroutines
|
||||||
|
|
||||||
enum class Statusflag {
|
enum class Statusflag {
|
||||||
Pc,
|
Pc,
|
||||||
Pz,
|
Pz,
|
||||||
Pv,
|
Pv,
|
||||||
Pn
|
Pn;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val names by lazy { values().map { it.toString()} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class BranchCondition {
|
enum class BranchCondition {
|
||||||
@ -157,6 +169,7 @@ object ParentSentinel : Node {
|
|||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
|
fun toClickableStr(): String = "$file:$line:$startCol:"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val DUMMY = Position("<dummy>", 0, 0, 0)
|
val DUMMY = Position("<dummy>", 0, 0, 0)
|
||||||
|
@ -24,7 +24,7 @@ class ErrorReporter {
|
|||||||
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
||||||
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
||||||
}
|
}
|
||||||
val msg = "${it.position} ${it.severity} ${it.message}".trim()
|
val msg = "${it.position.toClickableStr()} ${it.severity} ${it.message}".trim()
|
||||||
if(msg !in alreadyReportedMessages) {
|
if(msg !in alreadyReportedMessages) {
|
||||||
System.err.println(msg)
|
System.err.println(msg)
|
||||||
alreadyReportedMessages.add(msg)
|
alreadyReportedMessages.add(msg)
|
||||||
|
@ -2,16 +2,16 @@ package prog8.ast.base
|
|||||||
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
|
||||||
class FatalAstException (override var message: String) : Exception(message)
|
open class FatalAstException (override var message: String) : Exception(message)
|
||||||
|
|
||||||
open class AstException (override var message: String) : Exception(message)
|
open class AstException (override var message: String) : Exception(message)
|
||||||
|
|
||||||
open class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
open class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Syntax error: $message"
|
override fun toString() = "${position.toClickableStr()} Syntax error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Error: $message"
|
override fun toString() = "${position.toClickableStr()} Error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||||
|
@ -3,9 +3,9 @@ package prog8.ast.base
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.target.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||||
@ -19,8 +19,8 @@ internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter) {
|
|||||||
fixer.applyModifications()
|
fixer.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.reorderStatements() {
|
internal fun Program.reorderStatements(errors: ErrorReporter) {
|
||||||
val reorder = StatementReorderer(this)
|
val reorder = StatementReorderer(this, errors)
|
||||||
reorder.visit(this)
|
reorder.visit(this)
|
||||||
reorder.applyModifications()
|
reorder.applyModifications()
|
||||||
}
|
}
|
||||||
@ -31,6 +31,11 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
|
|||||||
caster.applyModifications()
|
caster.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.verifyFunctionArgTypes() {
|
||||||
|
val fixer = VerifyFunctionArgTypes(this)
|
||||||
|
fixer.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Module.checkImportedValid() {
|
internal fun Module.checkImportedValid() {
|
||||||
val imr = ImportedModuleDirectiveRemover()
|
val imr = ImportedModuleDirectiveRemover()
|
||||||
imr.visit(this, this.parent)
|
imr.visit(this, this.parent)
|
||||||
@ -44,21 +49,60 @@ internal fun Program.checkRecursion(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
||||||
val checker = AstIdentifiersChecker(this, errors)
|
|
||||||
checker.visit(this)
|
val checker2 = AstIdentifiersChecker(this, errors)
|
||||||
|
checker2.visit(this)
|
||||||
|
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
val transforms = AstVariousTransforms(this)
|
||||||
|
transforms.visit(this)
|
||||||
|
transforms.applyModifications()
|
||||||
|
val lit2decl = LiteralsToAutoVars(this)
|
||||||
|
lit2decl.visit(this)
|
||||||
|
lit2decl.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
if (modules.map { it.name }.toSet().size != modules.size) {
|
if (modules.map { it.name }.toSet().size != modules.size) {
|
||||||
throw FatalAstException("modules should all be unique")
|
throw FatalAstException("modules should all be unique")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.makeForeverLoops() {
|
internal fun Program.variousCleanups() {
|
||||||
val checker = ForeverLoopsMaker()
|
val process = VariousCleanups()
|
||||||
checker.visit(this)
|
process.visit(this)
|
||||||
checker.applyModifications()
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
internal fun Program.moveMainAndStartToFirst() {
|
||||||
val flattener = FlattenAnonymousScopesAndNopRemover()
|
// the module containing the program entrypoint is moved to the first in the sequence.
|
||||||
flattener.visit(this)
|
// the "main" block containing the entrypoint is moved to the top in there,
|
||||||
|
// and finally the entrypoint subroutine "start" itself is moved to the top in that block.
|
||||||
|
|
||||||
|
val directives = modules[0].statements.filterIsInstance<Directive>()
|
||||||
|
val start = this.entrypoint()
|
||||||
|
if(start!=null) {
|
||||||
|
val mod = start.definingModule()
|
||||||
|
val block = start.definingBlock()
|
||||||
|
if(!modules.remove(mod))
|
||||||
|
throw FatalAstException("module wrong")
|
||||||
|
modules.add(0, mod)
|
||||||
|
mod.remove(block)
|
||||||
|
var afterDirective = mod.statements.indexOfFirst { it !is Directive }
|
||||||
|
if(afterDirective<0)
|
||||||
|
mod.statements.add(block)
|
||||||
|
else
|
||||||
|
mod.statements.add(afterDirective, block)
|
||||||
|
block.remove(start)
|
||||||
|
afterDirective = block.statements.indexOfFirst { it !is Directive }
|
||||||
|
if(afterDirective<0)
|
||||||
|
block.statements.add(start)
|
||||||
|
else
|
||||||
|
block.statements.add(afterDirective, start)
|
||||||
|
|
||||||
|
// overwrite the directives in the module containing the entrypoint
|
||||||
|
for(directive in directives) {
|
||||||
|
modules[0].statements.removeAll { it is Directive && it.directive == directive.directive }
|
||||||
|
modules[0].statements.add(0, directive)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@ import prog8.ast.*
|
|||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import prog8.functions.CannotEvaluateException
|
||||||
import prog8.functions.NotConstArgumentException
|
import prog8.functions.NotConstArgumentException
|
||||||
import prog8.functions.builtinFunctionReturnType
|
import prog8.functions.builtinFunctionReturnType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -16,35 +16,52 @@ import kotlin.math.abs
|
|||||||
|
|
||||||
|
|
||||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
|
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
|
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>")
|
||||||
|
|
||||||
sealed class Expression: Node {
|
sealed class Expression: Node {
|
||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
abstract fun referencesIdentifier(vararg scopedName: String): Boolean
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
|
||||||
|
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
|
||||||
|
|
||||||
infix fun isSameAs(other: Expression): Boolean {
|
infix fun isSameAs(other: Expression): Boolean {
|
||||||
if(this===other)
|
if(this===other)
|
||||||
return true
|
return true
|
||||||
when(this) {
|
return when(this) {
|
||||||
is RegisterExpr ->
|
|
||||||
return (other is RegisterExpr && other.register==register)
|
|
||||||
is IdentifierReference ->
|
is IdentifierReference ->
|
||||||
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
(other is IdentifierReference && other.nameInSource==nameInSource)
|
||||||
is PrefixExpression ->
|
is PrefixExpression ->
|
||||||
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
(other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||||
is BinaryExpression ->
|
is BinaryExpression ->
|
||||||
return (other is BinaryExpression && other.operator==operator
|
(other is BinaryExpression && other.operator==operator
|
||||||
&& other.left isSameAs left
|
&& other.left isSameAs left
|
||||||
&& other.right isSameAs right)
|
&& other.right isSameAs right)
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||||
}
|
}
|
||||||
else -> return other==this
|
is DirectMemoryRead -> {
|
||||||
|
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
|
||||||
|
}
|
||||||
|
is TypecastExpression -> {
|
||||||
|
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
|
||||||
|
}
|
||||||
|
is AddressOf -> {
|
||||||
|
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource)
|
||||||
|
}
|
||||||
|
is RangeExpr -> {
|
||||||
|
(other is RangeExpr && other.from==from && other.to==to && other.step==step)
|
||||||
|
}
|
||||||
|
is FunctionCall -> {
|
||||||
|
(other is FunctionCall && other.target.nameInSource == target.nameInSource
|
||||||
|
&& other.args.size == args.size
|
||||||
|
&& other.args.zip(args).all { it.first isSameAs it.second } )
|
||||||
|
}
|
||||||
|
else -> other==this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,11 +82,10 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val inferred = expression.inferType(program)
|
val inferred = expression.inferType(program)
|
||||||
return when(operator) {
|
return when(operator) {
|
||||||
@ -123,11 +139,10 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
override fun referencesIdentifier(vararg scopedName: String) = left.referencesIdentifier(*scopedName) || right.referencesIdentifier(*scopedName)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val leftDt = left.inferType(program)
|
val leftDt = left.inferType(program)
|
||||||
val rightDt = right.inferType(program)
|
val rightDt = right.inferType(program)
|
||||||
@ -237,11 +252,10 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
override fun referencesIdentifier(vararg scopedName: String) = identifier.referencesIdentifier(*scopedName)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val target = identifier.targetStatement(program.namespace)
|
val target = identifier.targetStatement(program.namespace)
|
||||||
@ -274,17 +288,18 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val cv = expression.constValue(program) ?: return null
|
val cv = expression.constValue(program) ?: return null
|
||||||
return cv.cast(type)
|
val cast = cv.cast(type)
|
||||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
return if(cast.isValid)
|
||||||
// return LiteralValue.fromNumber(value.numericValue(), value.type, position).cast(type)
|
cast.valueOrZero()
|
||||||
|
else
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -307,9 +322,8 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifier(vararg scopedName: String) = false
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -328,11 +342,10 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifier(vararg scopedName: String) = false
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
|
|
||||||
@ -385,10 +398,9 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
throw FatalAstException("can't replace here")
|
throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifier(vararg scopedName: String) = false
|
||||||
override fun constValue(program: Program) = this
|
override fun constValue(program: Program) = this
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -406,88 +418,66 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
|
|
||||||
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
||||||
|
|
||||||
fun cast(targettype: DataType): NumericLiteralValue {
|
class CastValue(val isValid: Boolean, private val value: NumericLiteralValue?) {
|
||||||
|
fun valueOrZero() = if(isValid) value!! else NumericLiteralValue(DataType.UBYTE, 0, Position.DUMMY)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cast(targettype: DataType): CastValue {
|
||||||
if(type==targettype)
|
if(type==targettype)
|
||||||
return this
|
return CastValue(true, this)
|
||||||
val numval = number.toDouble()
|
val numval = number.toDouble()
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(targettype== DataType.BYTE && numval <= 127)
|
if(targettype== DataType.BYTE && numval <= 127)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.WORD || targettype== DataType.UWORD)
|
if(targettype== DataType.WORD || targettype== DataType.UWORD)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(targettype== DataType.UBYTE && numval >= 0)
|
if(targettype== DataType.UBYTE && numval >= 0)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.UWORD && numval >= 0)
|
if(targettype== DataType.UWORD && numval >= 0)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.WORD)
|
if(targettype== DataType.WORD)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(targettype== DataType.BYTE && numval <= 127)
|
if(targettype== DataType.BYTE && numval <= 127)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.UBYTE && numval <= 255)
|
if(targettype== DataType.UBYTE && numval <= 255)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.WORD && numval <= 32767)
|
if(targettype== DataType.WORD && numval <= 32767)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
|
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.UBYTE && numval >= 0 && numval <= 255)
|
if(targettype== DataType.UBYTE && numval >= 0 && numval <= 255)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if(targettype== DataType.UWORD && numval >=0)
|
if(targettype== DataType.UWORD && numval >=0)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
|
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if (targettype == DataType.UBYTE && numval >=0 && numval <= 255)
|
if (targettype == DataType.UBYTE && numval >=0 && numval <= 255)
|
||||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||||
if (targettype == DataType.WORD && numval >= -32768 && numval <= 32767)
|
if (targettype == DataType.WORD && numval >= -32768 && numval <= 32767)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
|
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
throw ExpressionError("can't cast $type into $targettype", position)
|
return CastValue(false, null)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StructLiteralValue(var values: List<Expression>,
|
|
||||||
override val position: Position): Expression() {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent=parent
|
|
||||||
values.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
throw FatalAstException("can't replace here")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "struct{ ${values.joinToString(", ")} }"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,9 +498,8 @@ class StringLiteralValue(val value: String,
|
|||||||
throw FatalAstException("can't replace here")
|
throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifier(vararg scopedName: String) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -539,19 +528,18 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression)
|
require(replacement is Expression)
|
||||||
val idx = value.indexOf(node)
|
val idx = value.indexOfFirst { it===node }
|
||||||
value[idx] = replacement
|
value[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
override fun referencesIdentifier(vararg scopedName: String) = value.any { it.referencesIdentifier(*scopedName) }
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String = "$value"
|
override fun toString(): String = "$value"
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isUnknown) type else guessDatatype(program)
|
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isKnown) type else guessDatatype(program)
|
||||||
|
|
||||||
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
operator fun compareTo(other: ArrayLiteralValue): Int = throw ExpressionError("cannot order compare arrays", position)
|
||||||
override fun hashCode(): Int = Objects.hash(value, type)
|
override fun hashCode(): Int = Objects.hash(value, type)
|
||||||
@ -581,10 +569,17 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
||||||
return when {
|
return when {
|
||||||
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||||
|
DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W)
|
DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W)
|
||||||
DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B)
|
DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B)
|
||||||
DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB)
|
DataType.UBYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UB)
|
||||||
|
DataType.ARRAY_UW in dts ||
|
||||||
|
DataType.ARRAY_W in dts ||
|
||||||
|
DataType.ARRAY_UB in dts ||
|
||||||
|
DataType.ARRAY_B in dts ||
|
||||||
|
DataType.ARRAY_F in dts ||
|
||||||
|
DataType.STRUCT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
else -> InferredTypes.InferredType.unknown()
|
else -> InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -599,14 +594,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
if(num==null) {
|
if(num==null) {
|
||||||
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
|
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
|
||||||
if (elementType != DataType.UWORD || it !is AddressOf)
|
if (elementType != DataType.UWORD || it !is AddressOf)
|
||||||
return null
|
return null // can't cast a value of the array, abort
|
||||||
it
|
it
|
||||||
} else {
|
} else {
|
||||||
try {
|
val cast = num.cast(elementType)
|
||||||
num.cast(elementType)
|
if(cast.isValid)
|
||||||
} catch(x: ExpressionError) {
|
cast.valueOrZero()
|
||||||
return null
|
else
|
||||||
}
|
return null // can't cast a value of the array, abort
|
||||||
}
|
}
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
||||||
@ -640,11 +635,10 @@ class RangeExpr(var from: Expression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
override fun referencesIdentifier(vararg scopedName: String): Boolean = from.referencesIdentifier(*scopedName) || to.referencesIdentifier(*scopedName)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val fromDt=from.inferType(program)
|
val fromDt=from.inferType(program)
|
||||||
val toDt=to.inferType(program)
|
val toDt=to.inferType(program)
|
||||||
@ -655,7 +649,14 @@ class RangeExpr(var from: Expression,
|
|||||||
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
|
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
|
||||||
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||||
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
else -> {
|
||||||
|
val fdt = fromDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val tdt = toDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(fdt largerThan tdt)
|
||||||
|
InferredTypes.knownFor(ElementArrayTypes.getValue(fdt))
|
||||||
|
else
|
||||||
|
InferredTypes.knownFor(ElementArrayTypes.getValue(tdt))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -677,8 +678,8 @@ class RangeExpr(var from: Expression,
|
|||||||
val toString = to as? StringLiteralValue
|
val toString = to as? StringLiteralValue
|
||||||
if(fromString!=null && toString!=null ) {
|
if(fromString!=null && toString!=null ) {
|
||||||
// string range -> int range over character values
|
// string range -> int range over character values
|
||||||
fromVal = CompilationTarget.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
fromVal = CompilationTarget.instance.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
||||||
toVal = CompilationTarget.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
toVal = CompilationTarget.instance.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
||||||
} else {
|
} else {
|
||||||
val fromLv = from as? NumericLiteralValue
|
val fromLv = from as? NumericLiteralValue
|
||||||
val toLv = to as? NumericLiteralValue
|
val toLv = to as? NumericLiteralValue
|
||||||
@ -708,30 +709,6 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RegisterExpr(val register: Register, override val position: Position) : Expression(), IAssignable {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
throw FatalAstException("can't replace here")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
|
|
||||||
override fun toString(): String {
|
|
||||||
return "RegisterExpr(register=$register, pos=$position)"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
@ -744,6 +721,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
||||||
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
|
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
|
||||||
|
|
||||||
|
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
|
||||||
|
override fun hashCode() = nameInSource.hashCode()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
@ -768,18 +748,17 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
return "IdentifierRef($nameInSource)"
|
return "IdentifierRef($nameInSource)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
override fun referencesIdentifier(vararg scopedName: String): Boolean =
|
||||||
|
nameInSource.size==scopedName.size && nameInSource.toTypedArray().contentEquals(scopedName)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val targetStmt = targetStatement(program.namespace)
|
return when (val targetStmt = targetStatement(program.namespace)) {
|
||||||
return if(targetStmt is VarDecl) {
|
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||||
InferredTypes.knownFor(targetStmt.datatype)
|
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
|
||||||
} else {
|
else -> InferredTypes.InferredType.unknown()
|
||||||
InferredTypes.InferredType.unknown()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,6 +774,18 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
else -> throw FatalAstException("requires a reference value")
|
else -> throw FatalAstException("requires a reference value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun firstStructVarName(namespace: INameScope): String? {
|
||||||
|
// take the name of the first struct member of the structvariable instead
|
||||||
|
// if it's just a regular variable, return null.
|
||||||
|
val struct = memberOfStruct(namespace) ?: return null
|
||||||
|
val decl = targetVarDecl(namespace)!!
|
||||||
|
val firstStructMember = struct.nameOfFirstMember()
|
||||||
|
// find the flattened var that belongs to this first struct member
|
||||||
|
val firstVarName = listOf(decl.name, firstStructMember)
|
||||||
|
val firstVar = definingScope().lookup(firstVarName, this) as VarDecl
|
||||||
|
return firstVar.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCall(override var target: IdentifierReference,
|
class FunctionCall(override var target: IdentifierReference,
|
||||||
@ -812,7 +803,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
if(node===target)
|
if(node===target)
|
||||||
target=replacement as IdentifierReference
|
target=replacement as IdentifierReference
|
||||||
else {
|
else {
|
||||||
val idx = args.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -848,17 +839,20 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
// const-evaluating the builtin function call failed.
|
// const-evaluating the builtin function call failed.
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
catch(x: CannotEvaluateException) {
|
||||||
|
// const-evaluating the builtin function call failed.
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "FunctionCall(target=$target, pos=$position)"
|
return "FunctionCall(target=$target, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || args.any{it.referencesIdentifiers(*name)}
|
override fun referencesIdentifier(vararg scopedName: String): Boolean = target.referencesIdentifier(*scopedName) || args.any{it.referencesIdentifier(*scopedName)}
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val constVal = constValue(program ,false)
|
val constVal = constValue(program ,false)
|
||||||
@ -878,6 +872,12 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
return InferredTypes.void() // no return value
|
return InferredTypes.void() // no return value
|
||||||
if(stmt.returntypes.size==1)
|
if(stmt.returntypes.size==1)
|
||||||
return InferredTypes.knownFor(stmt.returntypes[0])
|
return InferredTypes.knownFor(stmt.returntypes[0])
|
||||||
|
|
||||||
|
// multiple return values. Can occur for asmsub routines. If there is exactly one register return value, take that.
|
||||||
|
val numRegisterReturns = stmt.asmReturnvaluesRegisters.count { it.registerOrPair!=null }
|
||||||
|
if(numRegisterReturns==1)
|
||||||
|
return InferredTypes.InferredType.known(DataType.UBYTE)
|
||||||
|
|
||||||
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
|
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
|
||||||
}
|
}
|
||||||
else -> return InferredTypes.unknown()
|
else -> return InferredTypes.unknown()
|
||||||
|
@ -7,7 +7,9 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -110,25 +112,19 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop) {
|
override fun visit(forLoop: ForLoop) {
|
||||||
if(forLoop.body.containsNoCodeNorVars())
|
|
||||||
errors.warn("for loop body is empty", forLoop.position)
|
|
||||||
|
|
||||||
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
||||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||||
errors.err("can only loop over an iterable type", forLoop.position)
|
errors.err("can only loop over an iterable type", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
if (forLoop.loopRegister != null) {
|
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)
|
||||||
// loop register
|
|
||||||
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt != DataType.STR)
|
|
||||||
errors.err("register can only loop over bytes", forLoop.position)
|
|
||||||
if(forLoop.loopRegister!=Register.A)
|
|
||||||
errors.err("it's only possible to use A as a loop register", forLoop.position)
|
|
||||||
} else {
|
|
||||||
// loop variable
|
|
||||||
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
|
|
||||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
fun checkLoopRangeValues() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
when (loopvar.datatype) {
|
when (loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||||
@ -153,6 +149,21 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
||||||
}
|
}
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
// check loop range values
|
||||||
|
val range = forLoop.iterable as? RangeExpr
|
||||||
|
if(range!=null) {
|
||||||
|
val from = range.from as? NumericLiteralValue
|
||||||
|
val to = range.to as? NumericLiteralValue
|
||||||
|
if(from != null)
|
||||||
|
checkValueTypeAndRange(loopvar.datatype, from)
|
||||||
|
else if(!range.from.inferType(program).istype(loopvar.datatype))
|
||||||
|
errors.err("range start value is incompatible with loop variable type", range.position)
|
||||||
|
if(to != null)
|
||||||
|
checkValueTypeAndRange(loopvar.datatype, to)
|
||||||
|
else if(!range.to.inferType(program).istype(loopvar.datatype))
|
||||||
|
errors.err("range end value is incompatible with loop variable type", range.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,27 +271,27 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val regCounts = mutableMapOf<Register, Int>().withDefault { 0 }
|
val regCounts = mutableMapOf<CpuRegister, Int>().withDefault { 0 }
|
||||||
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
|
val statusflagCounts = mutableMapOf<Statusflag, Int>().withDefault { 0 }
|
||||||
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
|
fun countRegisters(from: Iterable<RegisterOrStatusflag>) {
|
||||||
regCounts.clear()
|
regCounts.clear()
|
||||||
statusflagCounts.clear()
|
statusflagCounts.clear()
|
||||||
for(p in from) {
|
for(p in from) {
|
||||||
when(p.registerOrPair) {
|
when(p.registerOrPair) {
|
||||||
RegisterOrPair.A -> regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
RegisterOrPair.A -> regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
RegisterOrPair.X -> regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
RegisterOrPair.X -> regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
RegisterOrPair.Y -> regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
RegisterOrPair.Y -> regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
}
|
}
|
||||||
RegisterOrPair.AY -> {
|
RegisterOrPair.AY -> {
|
||||||
regCounts[Register.A]=regCounts.getValue(Register.A)+1
|
regCounts[CpuRegister.A]=regCounts.getValue(CpuRegister.A)+1
|
||||||
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
}
|
}
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
regCounts[Register.X]=regCounts.getValue(Register.X)+1
|
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
|
||||||
regCounts[Register.Y]=regCounts.getValue(Register.Y)+1
|
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
|
||||||
}
|
}
|
||||||
null ->
|
null ->
|
||||||
if(p.statusflag!=null)
|
if(p.statusflag!=null)
|
||||||
@ -316,43 +327,34 @@ internal class AstChecker(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
// Instead, their reference (address) should be passed (as an UWORD).
|
// Instead, their reference (address) should be passed (as an UWORD).
|
||||||
// The language has no typed pointers at this time.
|
|
||||||
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
||||||
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword for their address, or access the variable from the outer scope directly.")
|
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitStatements(subroutine.statements)
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
}
|
if(untilLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
|
errors.err("condition value should be an integer type", untilLoop.condition.position)
|
||||||
override fun visit(repeatLoop: RepeatLoop) {
|
super.visit(untilLoop)
|
||||||
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
|
|
||||||
errors.warn("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
|
|
||||||
if(repeatLoop.untilCondition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
|
||||||
errors.err("condition value should be an integer type", repeatLoop.untilCondition.position)
|
|
||||||
super.visit(repeatLoop)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop) {
|
override fun visit(whileLoop: WhileLoop) {
|
||||||
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
|
|
||||||
errors.warn("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
|
|
||||||
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||||
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
|
|
||||||
if(assignment.value is FunctionCall) {
|
if(assignment.value is FunctionCall) {
|
||||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||||
if (stmt is Subroutine && stmt.isAsmSubroutine) {
|
if (stmt is Subroutine) {
|
||||||
if(stmt.returntypes.size>1)
|
|
||||||
errors.err("It's not possible to store the multiple results of this asmsub call; you should use a small block of custom inline assembly for this.", assignment.value.position)
|
|
||||||
else {
|
|
||||||
val idt = assignment.target.inferType(program, assignment)
|
val idt = assignment.target.inferType(program, assignment)
|
||||||
if(!idt.isKnown || stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
|
if(!idt.isKnown) {
|
||||||
errors.err("return type mismatch", assignment.value.position)
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
}
|
||||||
|
if(stmt.returntypes.size <= 1 && stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
|
||||||
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,9 +363,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(targetIdent!=null) {
|
if(targetIdent!=null) {
|
||||||
val targetVar = targetIdent.targetVarDecl(program.namespace)
|
val targetVar = targetIdent.targetVarDecl(program.namespace)
|
||||||
if(targetVar?.struct != null) {
|
if(targetVar?.struct != null) {
|
||||||
val sourceStructLv = assignment.value as? StructLiteralValue
|
val sourceStructLv = assignment.value as? ArrayLiteralValue
|
||||||
if (sourceStructLv != null) {
|
if (sourceStructLv != null) {
|
||||||
if (sourceStructLv.values.size != targetVar.struct?.numberOfElements)
|
if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
|
||||||
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
||||||
} else {
|
} else {
|
||||||
val sourceIdent = assignment.value as? IdentifierReference
|
val sourceIdent = assignment.value as? IdentifierReference
|
||||||
@ -372,14 +374,28 @@ internal class AstChecker(private val program: Program,
|
|||||||
if (sourceVar?.struct != null) {
|
if (sourceVar?.struct != null) {
|
||||||
if (sourceVar.struct !== targetVar.struct)
|
if (sourceVar.struct !== targetVar.struct)
|
||||||
errors.err("assignment of different struct types", assignment.position)
|
errors.err("assignment of different struct types", assignment.position)
|
||||||
|
} else if(sourceVar?.isArray==true) {
|
||||||
|
if((sourceVar.value as ArrayLiteralValue).value.size != targetVar.struct?.numberOfElements)
|
||||||
|
errors.err("number of elements doesn't match struct definition", sourceVar.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(assignment.value.inferType(program) != assignment.target.inferType(program, assignment))
|
val targetDt = assignment.target.inferType(program, assignment)
|
||||||
errors.err("assignment value is of different type as the target", assignment.value.position)
|
val valueDt = assignment.value.inferType(program)
|
||||||
|
if(valueDt.isKnown && valueDt != targetDt) {
|
||||||
|
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
||||||
|
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||||
|
else
|
||||||
|
errors.err("value's type doesn't match target", assignment.value.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(assignment.value is TypecastExpression) {
|
||||||
|
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
|
||||||
|
errors.err("typecasting a float value in-place makes no sense", assignment.value.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(assignment)
|
super.visit(assignment)
|
||||||
}
|
}
|
||||||
@ -397,8 +413,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetIdentifier = assignTarget.identifier
|
val targetIdentifier = assignTarget.identifier
|
||||||
if (targetIdentifier != null) {
|
if (targetIdentifier != null) {
|
||||||
val targetName = targetIdentifier.nameInSource
|
val targetName = targetIdentifier.nameInSource
|
||||||
val targetSymbol = program.namespace.lookup(targetName, assignment)
|
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
|
||||||
when (targetSymbol) {
|
|
||||||
null -> {
|
null -> {
|
||||||
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
||||||
return
|
return
|
||||||
@ -423,9 +438,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
if (assignment is Assignment) {
|
if (assignment is Assignment) {
|
||||||
|
|
||||||
if (assignment.aug_op != null)
|
|
||||||
throw FatalAstException("augmented assignment should have been converted into normal assignment")
|
|
||||||
|
|
||||||
val targetDatatype = assignTarget.inferType(program, assignment)
|
val targetDatatype = assignTarget.inferType(program, assignment)
|
||||||
if (targetDatatype.isKnown) {
|
if (targetDatatype.isKnown) {
|
||||||
val constVal = assignment.value.constValue(program)
|
val constVal = assignment.value.constValue(program)
|
||||||
@ -433,12 +445,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRange(targetDatatype.typeOrElse(DataType.BYTE), constVal)
|
checkValueTypeAndRange(targetDatatype.typeOrElse(DataType.BYTE), constVal)
|
||||||
} else {
|
} else {
|
||||||
val sourceDatatype = assignment.value.inferType(program)
|
val sourceDatatype = assignment.value.inferType(program)
|
||||||
if (!sourceDatatype.isKnown) {
|
if (sourceDatatype.isUnknown) {
|
||||||
if (assignment.value is FunctionCall) {
|
if (assignment.value !is FunctionCall)
|
||||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
|
||||||
if (targetStmt != null)
|
|
||||||
errors.err("function call doesn't return a suitable value to use in assignment", assignment.value.position)
|
|
||||||
} else
|
|
||||||
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
|
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
|
||||||
} else {
|
} else {
|
||||||
checkAssignmentCompatible(targetDatatype.typeOrElse(DataType.BYTE), assignTarget,
|
checkAssignmentCompatible(targetDatatype.typeOrElse(DataType.BYTE), assignTarget,
|
||||||
@ -454,21 +462,20 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(variable==null)
|
if(variable==null)
|
||||||
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
|
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
|
||||||
else {
|
else {
|
||||||
if(variable.datatype !in ArrayDatatypes && variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
if(variable.datatype !in ArrayDatatypes
|
||||||
|
&& variable.type!=VarDeclType.MEMORY
|
||||||
|
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||||
}
|
}
|
||||||
super.visit(addressOf)
|
super.visit(addressOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
fun err(msg: String, position: Position?=null) {
|
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
||||||
errors.err(msg, position ?: decl.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true)
|
||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
}
|
|
||||||
|
|
||||||
// CONST can only occur on simple types (byte, word, float)
|
// CONST can only occur on simple types (byte, word, float)
|
||||||
if(decl.type== VarDeclType.CONST) {
|
if(decl.type== VarDeclType.CONST) {
|
||||||
@ -476,10 +483,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("const modifier can only be used on numeric types (byte, word, float)")
|
err("const modifier can only be used on numeric types (byte, word, float)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FLOATS
|
// FLOATS enabled?
|
||||||
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY) {
|
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY)
|
||||||
err("floating point used, but that is not enabled via options")
|
err("floating point used, but that is not enabled via options")
|
||||||
}
|
|
||||||
|
if(decl.datatype == DataType.FLOAT && (decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE))
|
||||||
|
errors.warn("floating point values won't be placed in Zeropage due to size constraints", decl.position)
|
||||||
|
|
||||||
// ARRAY without size specifier MUST have an iterable initializer value
|
// ARRAY without size specifier MUST have an iterable initializer value
|
||||||
if(decl.isArray && decl.arraysize==null) {
|
if(decl.isArray && decl.arraysize==null) {
|
||||||
@ -512,29 +521,21 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
when(decl.value) {
|
when(decl.value) {
|
||||||
null -> {
|
null -> {
|
||||||
// a vardecl without an initial value, don't bother with the rest
|
// a vardecl without an initial value, don't bother with it
|
||||||
return super.visit(decl)
|
|
||||||
}
|
}
|
||||||
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
|
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
|
||||||
is StringLiteralValue -> {
|
is StringLiteralValue -> {
|
||||||
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
|
||||||
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
|
||||||
}
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
|
||||||
}
|
|
||||||
is StructLiteralValue -> {
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
val struct = decl.struct!!
|
val struct = decl.struct!!
|
||||||
val structLv = decl.value as StructLiteralValue
|
val structLv = decl.value as ArrayLiteralValue
|
||||||
if(struct.numberOfElements != structLv.values.size) {
|
if(struct.numberOfElements != structLv.value.size) {
|
||||||
errors.err("struct value has incorrect number of elements", structLv.position)
|
errors.err("struct value has incorrect number of elements", structLv.position)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for(value in structLv.values.zip(struct.statements)) {
|
for(value in structLv.value.zip(struct.statements)) {
|
||||||
val memberdecl = value.second as VarDecl
|
val memberdecl = value.second as VarDecl
|
||||||
val constValue = value.first.constValue(program)
|
val constValue = value.first.constValue(program)
|
||||||
if(constValue==null) {
|
if(constValue==null) {
|
||||||
@ -548,19 +549,25 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errors.err("struct literal is wrong type to initialize this variable", decl.value!!.position)
|
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||||
|
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
if(decl.type==VarDeclType.CONST) {
|
||||||
|
err("const declaration needs a compile-time constant initializer value, or range")
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
val arraySize = decl.arraysize!!.size() ?: 1
|
val arraySize = decl.arraysize!!.constIndex() ?: 1
|
||||||
when(decl.datatype) {
|
when(decl.datatype) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||||
if(arraySize > 256)
|
if(arraySize > 256)
|
||||||
@ -575,20 +582,58 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.value !is NumericLiteralValue) {
|
if(decl.value is NumericLiteralValue) {
|
||||||
err("value of memory var decl is not a numeric literal (it is a ${decl.value!!.javaClass.simpleName}).", decl.value?.position)
|
|
||||||
} else {
|
|
||||||
val value = decl.value as NumericLiteralValue
|
val value = decl.value as NumericLiteralValue
|
||||||
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
||||||
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
err("value of memory mapped variable can only be a number, perhaps you meant to use an address pointer type instead?", decl.value?.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR && !declValue.inferType(program).istype(decl.datatype))
|
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||||
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
|
val valueIdt = declValue.inferType(program)
|
||||||
|
if(valueIdt.isUnknown)
|
||||||
|
throw AstException("invalid value type")
|
||||||
|
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(valueDt !in ArrayDatatypes)
|
||||||
|
err("initialisation of struct should be with array value", declValue.position)
|
||||||
|
} else if (!declValue.inferType(program).istype(decl.datatype)) {
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// array length limits
|
||||||
|
if(decl.isArray) {
|
||||||
|
val length = decl.arraysize!!.constIndex() ?: 1
|
||||||
|
when (decl.datatype) {
|
||||||
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
if(length==0 || length>256)
|
||||||
|
err("string and byte array length must be 1-256")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
if(length==0 || length>128)
|
||||||
|
err("word array length must be 1-128")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
if(length==0 || length>51)
|
||||||
|
err("float array length must be 1-51")
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// string assignment is not supported in a vard
|
||||||
|
if(decl.datatype==DataType.STR) {
|
||||||
|
if(decl.value==null)
|
||||||
|
err("string var must be initialized with a string literal")
|
||||||
|
else if (decl.type==VarDeclType.VAR && decl.value !is StringLiteralValue)
|
||||||
|
err("string var can only be initialized with a string literal")
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
@ -670,9 +715,17 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("this directive may only occur in a block or at module level")
|
err("this directive may only occur in a block or at module level")
|
||||||
if(directive.args.isEmpty())
|
if(directive.args.isEmpty())
|
||||||
err("missing option directive argument(s)")
|
err("missing option directive argument(s)")
|
||||||
else if(directive.args.map{it.name in setOf("enable_floats", "force_output")}.any { !it })
|
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it })
|
||||||
err("invalid option directive argument(s)")
|
err("invalid option directive argument(s)")
|
||||||
}
|
}
|
||||||
|
"%target" -> {
|
||||||
|
if(directive.parent !is Block && directive.parent !is Module)
|
||||||
|
err("this directive may only occur in a block or at module level")
|
||||||
|
if(directive.args.size != 1)
|
||||||
|
err("directive requires one argument")
|
||||||
|
if(directive.args.single().name !in setOf(C64Target.name, Cx16Target.name))
|
||||||
|
err("invalid compilation target")
|
||||||
|
}
|
||||||
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
||||||
}
|
}
|
||||||
super.visit(directive)
|
super.visit(directive)
|
||||||
@ -695,6 +748,22 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||||
|
if(e is IdentifierReference) {
|
||||||
|
val decl = e.targetVarDecl(program.namespace)!!
|
||||||
|
return decl.datatype in PassByReferenceDatatypes
|
||||||
|
}
|
||||||
|
return e is StringLiteralValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if(array.parent is VarDecl) {
|
||||||
|
if (!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
|
||||||
|
errors.err("array literal for variable initialization contains invalid types", array.position)
|
||||||
|
} else if(array.parent is ForLoop) {
|
||||||
|
if (!array.value.all { it.constValue(program) != null })
|
||||||
|
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(array)
|
super.visit(array)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,16 +773,26 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression) {
|
override fun visit(expr: PrefixExpression) {
|
||||||
if(expr.operator=="-") {
|
|
||||||
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if(expr.operator=="-") {
|
||||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||||
errors.err("can only take negative of a signed number type", expr.position)
|
errors.err("can only take negative of a signed number type", expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(expr.operator == "not") {
|
||||||
|
if(dt !in IntegerDatatypes)
|
||||||
|
errors.err("can only use boolean not on integer types", expr.position)
|
||||||
|
}
|
||||||
|
else if(expr.operator == "~") {
|
||||||
|
if(dt !in IntegerDatatypes)
|
||||||
|
errors.err("can only use bitwise invert on integer types", expr.position)
|
||||||
|
}
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
|
super.visit(expr)
|
||||||
|
|
||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightIDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
@ -735,7 +814,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
"**" -> {
|
"**" -> {
|
||||||
if(leftDt in IntegerDatatypes)
|
if(leftDt in IntegerDatatypes)
|
||||||
errors.err("power operator requires floating point", expr.position)
|
errors.err("power operator requires floating point operands", expr.position)
|
||||||
}
|
}
|
||||||
"and", "or", "xor" -> {
|
"and", "or", "xor" -> {
|
||||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||||
@ -751,21 +830,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
||||||
}
|
}
|
||||||
"<<", ">>" -> {
|
|
||||||
// for now, bit-shifts can only shift by a constant number
|
|
||||||
val constRight = expr.right.constValue(program)
|
|
||||||
if(constRight==null)
|
|
||||||
errors.err("bit-shift can only be done by a constant number (for now)", expr.right.position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftDt !in NumericDatatypes)
|
if(leftDt !in NumericDatatypes && leftDt != DataType.STR)
|
||||||
errors.err("left operand is not numeric", expr.left.position)
|
errors.err("left operand is not numeric or str", expr.left.position)
|
||||||
if(rightDt!in NumericDatatypes)
|
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
|
||||||
errors.err("right operand is not numeric", expr.right.position)
|
errors.err("right operand is not numeric or str", expr.right.position)
|
||||||
if(leftDt!=rightDt)
|
if(leftDt!=rightDt)
|
||||||
errors.err("left and right operands aren't the same type", expr.left.position)
|
errors.err("left and right operands aren't the same type", expr.left.position)
|
||||||
super.visit(expr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression) {
|
override fun visit(typecast: TypecastExpression) {
|
||||||
@ -823,6 +895,32 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
|
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program)
|
||||||
|
if(error!=null)
|
||||||
|
errors.err(error, functionCall.position)
|
||||||
|
|
||||||
|
// check the functions that return multiple returnvalues.
|
||||||
|
val stmt = functionCall.target.targetStatement(program.namespace)
|
||||||
|
if (stmt is Subroutine) {
|
||||||
|
if (stmt.returntypes.size > 1) {
|
||||||
|
// Currently, it's only possible to handle ONE (or zero) return values from a subroutine.
|
||||||
|
// asmsub routines can have multiple return values, for instance in 2 different registers.
|
||||||
|
// It's not (yet) possible to handle these multiple return values because assignments
|
||||||
|
// are only to a single unique target at the same time.
|
||||||
|
// EXCEPTION:
|
||||||
|
// if the asmsub returns multiple values and one of them is via a status register bit,
|
||||||
|
// it *is* possible to handle them by just actually assigning the register value and
|
||||||
|
// dealing with the status bit as just being that, the status bit after the call.
|
||||||
|
val (returnRegisters, returnStatusflags) = stmt.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }
|
||||||
|
if (returnRegisters.isEmpty() || returnRegisters.size == 1) {
|
||||||
|
if (returnStatusflags.any())
|
||||||
|
errors.warn("this asmsub also has one or more return 'values' in one of the status flags", functionCall.position)
|
||||||
|
} else {
|
||||||
|
errors.err("It's not possible to store the multiple result values of this asmsub call; you should use a small block of custom inline assembly for this.", functionCall.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,12 +943,18 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
if(functionCallStatement.target.nameInSource.last() in setOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||||
// in-place modification, can't be done on literals
|
// in-place modification, can't be done on literals
|
||||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||||
errors.err("can't use that as argument to a in-place modifying function", functionCallStatement.args.first().position)
|
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, functionCallStatement.definingScope(), program)
|
||||||
|
if(error!=null) {
|
||||||
|
errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -859,20 +963,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("cannot use arguments when calling a label", position)
|
errors.err("cannot use arguments when calling a label", position)
|
||||||
|
|
||||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||||
// it's a call to a builtin function.
|
|
||||||
val func = BuiltinFunctions.getValue(target.name)
|
|
||||||
if(args.size!=func.parameters.size)
|
|
||||||
errors.err("invalid number of arguments", position)
|
|
||||||
else {
|
|
||||||
val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
|
||||||
for (arg in args.withIndex().zip(func.parameters)) {
|
|
||||||
val argDt=arg.first.value.inferType(program)
|
|
||||||
if (argDt.isKnown
|
|
||||||
&& !(argDt.typeOrElse(DataType.STRUCT) isAssignableTo arg.second.possibleDatatypes)
|
|
||||||
&& (argDt.typeOrElse(DataType.STRUCT) != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) {
|
|
||||||
errors.err("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(target.name=="swap") {
|
if(target.name=="swap") {
|
||||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||||
val dt1 = args[0].inferType(program)
|
val dt1 = args[0].inferType(program)
|
||||||
@ -894,43 +984,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if(target is Subroutine) {
|
} else if(target is Subroutine) {
|
||||||
if(args.size!=target.parameters.size)
|
if(target.regXasResult())
|
||||||
errors.err("invalid number of arguments", position)
|
errors.warn("subroutine call return value in X register is discarded and replaced by 0", position)
|
||||||
else {
|
if(target.isAsmSubroutine) {
|
||||||
for (arg in args.withIndex().zip(target.parameters)) {
|
for (arg in args.withIndex().zip(target.parameters)) {
|
||||||
val argIDt = arg.first.value.inferType(program)
|
val argIDt = arg.first.value.inferType(program)
|
||||||
if(!argIDt.isKnown) {
|
if (!argIDt.isKnown)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
if(!(argDt isAssignableTo arg.second.type)) {
|
|
||||||
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
|
||||||
if(!(target.isAsmSubroutine && arg.second.type == DataType.STR && argDt == DataType.UWORD))
|
|
||||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(target.isAsmSubroutine) {
|
|
||||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
|
|
||||||
if (arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
|
|
||||||
errors.warn("calling a subroutine that expects X as a parameter is problematic, more so when providing complex arguments. If you see a compiler error/crash about this later, try to simplify this call", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the argument types match the register(pairs)
|
|
||||||
val asmParamReg = target.asmParameterRegisters[arg.first.index]
|
|
||||||
if(asmParamReg.statusflag!=null) {
|
|
||||||
if(argDt !in ByteDatatypes)
|
|
||||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for statusflag", position)
|
|
||||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
|
||||||
if(argDt !in ByteDatatypes)
|
|
||||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for single register", position)
|
|
||||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
|
||||||
if(argDt !in WordDatatypes + IterableDatatypes)
|
|
||||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be word type for register pair", position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -969,7 +1031,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(target is VarDecl) {
|
if(target is VarDecl) {
|
||||||
if(target.datatype !in IterableDatatypes)
|
if(target.datatype !in IterableDatatypes)
|
||||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
||||||
val arraysize = target.arraysize?.size()
|
val arraysize = target.arraysize?.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
// check out of bounds
|
// check out of bounds
|
||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
||||||
@ -1050,35 +1112,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope) {
|
|
||||||
visitStatements(scope.statements)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visitStatements(statements: List<Statement>) {
|
|
||||||
for((index, stmt) in statements.withIndex()) {
|
|
||||||
if(index < statements.lastIndex && statements[index+1] !is Subroutine) {
|
|
||||||
when {
|
|
||||||
stmt is FunctionCallStatement && stmt.target.nameInSource.last() == "exit" -> {
|
|
||||||
errors.warn("unreachable code, preceding exit call will never return", statements[index + 1].position)
|
|
||||||
}
|
|
||||||
stmt is Return && statements[index + 1] !is Subroutine -> {
|
|
||||||
errors.warn("unreachable code, preceding return statement", statements[index + 1].position)
|
|
||||||
}
|
|
||||||
stmt is Jump && statements[index + 1] !is Subroutine -> {
|
|
||||||
errors.warn("unreachable code, preceding jump statement", statements[index + 1].position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.accept(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||||
val targetStatement = target.targetStatement(program.namespace)
|
val targetStatement = target.targetStatement(program.namespace)
|
||||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||||
return targetStatement
|
return targetStatement
|
||||||
|
else if(targetStatement==null)
|
||||||
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||||
|
else
|
||||||
|
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,7 +1143,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(value.type.isUnknown)
|
if(value.type.isUnknown)
|
||||||
return err("attempt to check values of array with as yet unknown datatype")
|
return false
|
||||||
|
|
||||||
when (targetDt) {
|
when (targetDt) {
|
||||||
DataType.STR -> return err("string value expected")
|
DataType.STR -> return err("string value expected")
|
||||||
@ -1111,7 +1152,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(value.type.istype(targetDt)) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>256)
|
if(arraySpecSize<1 || arraySpecSize>256)
|
||||||
@ -1133,7 +1174,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(value.type.istype(targetDt)) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>128)
|
if(arraySpecSize<1 || arraySpecSize>128)
|
||||||
@ -1156,7 +1197,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||||
return err("float array length must be 1-51")
|
return err("float array length must be 1-51")
|
||||||
@ -1171,7 +1212,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
// check if the floating point values are all within range
|
// check if the floating point values are all within range
|
||||||
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
||||||
if(doubles.any { it < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.machine.FLOAT_MAX_POSITIVE })
|
if(doubles.any { it < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE })
|
||||||
return err("floating point value overflow")
|
return err("floating point value overflow")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -1248,7 +1289,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||||
is TypecastExpression -> {
|
is TypecastExpression -> {
|
||||||
val constVal = it.expression.constValue(program)
|
val constVal = it.expression.constValue(program)
|
||||||
constVal?.cast(it.type)?.number?.toInt() ?: -9999999
|
val cast = constVal?.cast(it.type)
|
||||||
|
if(cast==null || !cast.isValid)
|
||||||
|
-9999999
|
||||||
|
else
|
||||||
|
cast.valueOrZero().number.toInt()
|
||||||
}
|
}
|
||||||
else -> -9999999
|
else -> -9999999
|
||||||
}
|
}
|
||||||
@ -1293,8 +1338,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype== DataType.STR
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
if(sourceDatatype==DataType.STRUCT) {
|
if(sourceDatatype==DataType.STRUCT) {
|
||||||
val structLv = sourceValue as StructLiteralValue
|
val structLv = sourceValue as ArrayLiteralValue
|
||||||
val numValues = structLv.values.size
|
val numValues = structLv.value.size
|
||||||
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
|
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
|
||||||
return targetstruct.numberOfElements == numValues
|
return targetstruct.numberOfElements == numValues
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -10,99 +8,83 @@ import prog8.ast.statements.*
|
|||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter) : IAstVisitor {
|
||||||
internal class AstIdentifiersChecker(private val program: Program,
|
|
||||||
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
vardeclsToAdd.clear()
|
|
||||||
blocks.clear() // blocks may be redefined within a different module
|
blocks.clear() // blocks may be redefined within a different module
|
||||||
|
|
||||||
super.visit(module)
|
super.visit(module)
|
||||||
// add any new vardecls to the various scopes
|
|
||||||
for((where, decls) in vardeclsToAdd) {
|
|
||||||
where.statements.addAll(0, decls)
|
|
||||||
decls.forEach { it.linkParents(where as Node) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block): Statement {
|
override fun visit(block: Block) {
|
||||||
|
if(block.name in CompilationTarget.instance.machine.opcodeNames)
|
||||||
|
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
|
||||||
|
|
||||||
val existing = blocks[block.name]
|
val existing = blocks[block.name]
|
||||||
if(existing!=null)
|
if(existing!=null)
|
||||||
nameError(block.name, block.position, existing)
|
nameError(block.name, block.position, existing)
|
||||||
else
|
else
|
||||||
blocks[block.name] = block
|
blocks[block.name] = block
|
||||||
|
|
||||||
return super.visit(block)
|
super.visit(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun visit(directive: Directive) {
|
||||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
if(directive.directive=="%target") {
|
||||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
val compatibleTarget = directive.args.single().name
|
||||||
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
|
if (compatibleTarget != CompilationTarget.instance.name)
|
||||||
typecast.linkParents(functionCall.parent)
|
errors.err("module's compilation target ($compatibleTarget) differs from active target (${CompilationTarget.instance.name})", directive.position)
|
||||||
return super.visit(typecast)
|
|
||||||
}
|
|
||||||
return super.visit(functionCall)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
super.visit(directive)
|
||||||
// first, check if there are datatype errors on the vardecl
|
}
|
||||||
|
|
||||||
|
override fun visit(decl: VarDecl) {
|
||||||
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
||||||
|
|
||||||
// now check the identifier
|
|
||||||
if(decl.name in BuiltinFunctions)
|
if(decl.name in BuiltinFunctions)
|
||||||
// the builtin functions can't be redefined
|
|
||||||
errors.err("builtin function cannot be redefined", decl.position)
|
errors.err("builtin function cannot be redefined", decl.position)
|
||||||
|
|
||||||
if(decl.name in CompilationTarget.machine.opcodeNames)
|
if(decl.name in CompilationTarget.instance.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
|
||||||
// and include the original decl as well.
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
if(decl.datatype==DataType.STRUCT) {
|
||||||
if(decl.structHasBeenFlattened)
|
if (decl.structHasBeenFlattened)
|
||||||
return super.visit(decl) // don't do this multiple times
|
return super.visit(decl) // don't do this multiple times
|
||||||
|
|
||||||
if(decl.struct==null) {
|
if (decl.struct == null) {
|
||||||
errors.err("undefined struct type", decl.position)
|
errors.err("undefined struct type", decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes})
|
if (decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes })
|
||||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
||||||
|
|
||||||
if(decl.value is NumericLiteralValue) {
|
if (decl.value is NumericLiteralValue) {
|
||||||
errors.err("you cannot initialize a struct using a single value", decl.position)
|
errors.err("you cannot initialize a struct using a single value", decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.value != null && decl.value !is StructLiteralValue) {
|
if (decl.value != null && decl.value !is ArrayLiteralValue) {
|
||||||
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position)
|
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
val decls = decl.flattenStructMembers()
|
|
||||||
decls.add(decl)
|
|
||||||
val result = AnonymousScope(decls, decl.position)
|
|
||||||
result.linkParents(decl.parent)
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
return super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
override fun visit(subroutine: Subroutine) {
|
||||||
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
if(subroutine.name in CompilationTarget.instance.machine.opcodeNames) {
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
||||||
} else if(subroutine.name in BuiltinFunctions) {
|
} else if(subroutine.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
@ -116,14 +98,6 @@ internal class AstIdentifiersChecker(private val program: Program,
|
|||||||
if (existing != null && existing !== subroutine)
|
if (existing != null && existing !== subroutine)
|
||||||
nameError(subroutine.name, subroutine.position, existing)
|
nameError(subroutine.name, subroutine.position, existing)
|
||||||
|
|
||||||
// does the parameter redefine a variable declared elsewhere?
|
|
||||||
for(param in subroutine.parameters) {
|
|
||||||
val existingVar = subroutine.lookup(listOf(param.name), subroutine)
|
|
||||||
if (existingVar != null && existingVar.parent !== subroutine) {
|
|
||||||
nameError(param.name, param.position, existingVar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
||||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
@ -138,31 +112,16 @@ internal class AstIdentifiersChecker(private val program: Program,
|
|||||||
nameError(name, sub.position, subroutine)
|
nameError(name, sub.position, subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines and non-asm parameters)
|
|
||||||
// NOTE:
|
|
||||||
// - numeric types BYTE and WORD and FLOAT are passed by value;
|
|
||||||
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
|
|
||||||
if(subroutine.asmAddress==null) {
|
|
||||||
if(subroutine.asmParameterRegisters.isEmpty()) {
|
|
||||||
subroutine.parameters
|
|
||||||
.filter { it.name !in namesInSub }
|
|
||||||
.forEach {
|
|
||||||
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
|
||||||
vardecl.linkParents(subroutine)
|
|
||||||
subroutine.statements.add(0, vardecl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||||
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(subroutine)
|
|
||||||
|
super.visit(subroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(label: Label): Statement {
|
override fun visit(label: Label) {
|
||||||
if(label.name in CompilationTarget.machine.opcodeNames)
|
if(label.name in CompilationTarget.instance.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
||||||
|
|
||||||
if(label.name in BuiltinFunctions) {
|
if(label.name in BuiltinFunctions) {
|
||||||
@ -179,163 +138,24 @@ internal class AstIdentifiersChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(label)
|
|
||||||
|
super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
override fun visit(string: StringLiteralValue) {
|
||||||
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
if (string.value.length > 255)
|
||||||
// rather than reusing an already declared loopvar from an outer scope.
|
errors.err("string literal length max is 255", string.position)
|
||||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
|
||||||
// additional interation count variable in their scope.
|
super.visit(string)
|
||||||
if(forLoop.loopRegister!=null) {
|
|
||||||
if(forLoop.loopRegister == Register.X)
|
|
||||||
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
|
|
||||||
} else {
|
|
||||||
val loopVar = forLoop.loopVar
|
|
||||||
if (loopVar != null) {
|
|
||||||
val validName = forLoop.body.name.replace("<", "").replace(">", "").replace("-", "")
|
|
||||||
val loopvarName = "prog8_loopvar_$validName"
|
|
||||||
if (forLoop.iterable !is RangeExpr) {
|
|
||||||
val existing = if (forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(loopvarName), forLoop.body.statements.first())
|
|
||||||
if (existing == null) {
|
|
||||||
// create loop iteration counter variable (without value, to avoid an assignment)
|
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, loopvarName, null, null,
|
|
||||||
isArray = false, autogeneratedDontRemove = true, position = loopVar.position)
|
|
||||||
vardecl.linkParents(forLoop.body)
|
|
||||||
forLoop.body.statements.add(0, vardecl)
|
|
||||||
loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(forLoop)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget): AssignTarget {
|
override fun visit(structDecl: StructDecl) {
|
||||||
if(assignTarget.register== Register.X)
|
|
||||||
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
|
|
||||||
return super.visit(assignTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
|
||||||
val array = super.visit(arrayLiteral)
|
|
||||||
if(array is ArrayLiteralValue) {
|
|
||||||
val vardecl = array.parent as? VarDecl
|
|
||||||
// adjust the datatype of the array (to an educated guess)
|
|
||||||
if(vardecl!=null) {
|
|
||||||
val arrayDt = array.type
|
|
||||||
if(!arrayDt.istype(vardecl.datatype)) {
|
|
||||||
val cast = array.cast(vardecl.datatype)
|
|
||||||
if (cast != null) {
|
|
||||||
vardecl.value = cast
|
|
||||||
cast.linkParents(vardecl)
|
|
||||||
return cast
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val arrayDt = array.guessDatatype(program)
|
|
||||||
if(arrayDt.isKnown) {
|
|
||||||
// this array literal is part of an expression, turn it into an identifier reference
|
|
||||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
|
||||||
return if (litval2 != null) {
|
|
||||||
litval2.parent = array.parent
|
|
||||||
makeIdentifierFromRefLv(litval2)
|
|
||||||
} else array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(stringLiteral: StringLiteralValue): Expression {
|
|
||||||
val string = super.visit(stringLiteral)
|
|
||||||
if(string is StringLiteralValue) {
|
|
||||||
val vardecl = string.parent as? VarDecl
|
|
||||||
// intern the string; move it into the heap
|
|
||||||
if (string.value.length !in 1..255)
|
|
||||||
errors.err("string literal length must be between 1 and 255", string.position)
|
|
||||||
return if (vardecl != null)
|
|
||||||
string
|
|
||||||
else
|
|
||||||
makeIdentifierFromRefLv(string) // replace the literal string by a identifier reference.
|
|
||||||
}
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(array: ArrayLiteralValue): IdentifierReference {
|
|
||||||
// a referencetype literal value that's not declared as a variable
|
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
|
||||||
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
|
||||||
val scope = array.definingScope()
|
|
||||||
val variable = VarDecl.createAuto(array)
|
|
||||||
return replaceWithIdentifier(variable, scope, array.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeIdentifierFromRefLv(string: StringLiteralValue): IdentifierReference {
|
|
||||||
// a referencetype literal value that's not declared as a variable
|
|
||||||
// we need to introduce an auto-generated variable for this to be able to refer to the value
|
|
||||||
// note: if the var references the same literal value, it is not yet de-duplicated here.
|
|
||||||
val scope = string.definingScope()
|
|
||||||
val variable = VarDecl.createAuto(string)
|
|
||||||
return replaceWithIdentifier(variable, scope, string.parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun replaceWithIdentifier(variable: VarDecl, scope: INameScope, parent: Node): IdentifierReference {
|
|
||||||
val variable1 = addVarDecl(scope, variable)
|
|
||||||
// replace the reference literal by a identifier reference
|
|
||||||
val identifier = IdentifierReference(listOf(variable1.name), variable1.position)
|
|
||||||
identifier.parent = parent
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl): Statement {
|
|
||||||
for(member in structDecl.statements){
|
for(member in structDecl.statements){
|
||||||
val decl = member as? VarDecl
|
val decl = member as? VarDecl
|
||||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||||
errors.err("structs can only contain numerical types", decl.position)
|
errors.err("structs can only contain numerical types", decl.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(structDecl)
|
super.visit(structDecl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
|
||||||
return when {
|
|
||||||
expr.left is StringLiteralValue ->
|
|
||||||
processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr)
|
|
||||||
expr.right is StringLiteralValue ->
|
|
||||||
processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr)
|
|
||||||
else -> super.visit(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
|
||||||
val constvalue = operand.constValue(program)
|
|
||||||
if(constvalue!=null) {
|
|
||||||
if (expr.operator == "*") {
|
|
||||||
// repeat a string a number of times
|
|
||||||
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), string.altEncoding, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(expr.operator == "+" && operand is StringLiteralValue) {
|
|
||||||
// concatenate two strings
|
|
||||||
return StringLiteralValue("${string.value}${operand.value}", string.altEncoding, expr.position)
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addVarDecl(scope: INameScope, variable: VarDecl): VarDecl {
|
|
||||||
if(scope !in vardeclsToAdd)
|
|
||||||
vardeclsToAdd[scope] = mutableListOf()
|
|
||||||
val declList = vardeclsToAdd.getValue(scope)
|
|
||||||
val existing = declList.singleOrNull { it.name==variable.name }
|
|
||||||
return if(existing!=null) {
|
|
||||||
existing
|
|
||||||
} else {
|
|
||||||
declList.add(variable)
|
|
||||||
variable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
105
compiler/src/prog8/ast/processing/AstVariousTransforms.kt
Normal file
105
compiler/src/prog8/ast/processing/AstVariousTransforms.kt
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
|
// and include the original decl as well.
|
||||||
|
if(decl.datatype==DataType.STRUCT && !decl.structHasBeenFlattened) {
|
||||||
|
val decls = decl.flattenStructMembers()
|
||||||
|
decls.add(decl)
|
||||||
|
val result = AnonymousScope(decls, decl.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
decl, result, parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
// For non-kernel subroutines and non-asm parameters:
|
||||||
|
// inject subroutine params as local variables (if they're not there yet).
|
||||||
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
|
if(subroutine.asmAddress==null) {
|
||||||
|
if(subroutine.asmParameterRegisters.isEmpty() && subroutine.parameters.isNotEmpty()) {
|
||||||
|
val vars = subroutine.statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
|
||||||
|
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
|
||||||
|
return subroutine.parameters
|
||||||
|
.filter { it.name !in namesInSub }
|
||||||
|
.map {
|
||||||
|
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
||||||
|
IAstModification.InsertFirst(vardecl, subroutine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val leftStr = expr.left as? StringLiteralValue
|
||||||
|
val rightStr = expr.right as? StringLiteralValue
|
||||||
|
if(expr.operator == "+") {
|
||||||
|
val concatenatedString = concatString(expr)
|
||||||
|
if(concatenatedString!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, concatenatedString, parent))
|
||||||
|
}
|
||||||
|
else if(expr.operator == "*") {
|
||||||
|
if (leftStr!=null) {
|
||||||
|
val amount = expr.right.constValue(program)
|
||||||
|
if(amount!=null) {
|
||||||
|
val string = leftStr.value.repeat(amount.number.toInt())
|
||||||
|
val strval = StringLiteralValue(string, leftStr.altEncoding, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rightStr!=null) {
|
||||||
|
val amount = expr.right.constValue(program)
|
||||||
|
if(amount!=null) {
|
||||||
|
val string = rightStr.value.repeat(amount.number.toInt())
|
||||||
|
val strval = StringLiteralValue(string, rightStr.altEncoding, expr.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
||||||
|
val rightStrval = expr.right as? StringLiteralValue
|
||||||
|
val leftStrval = expr.left as? StringLiteralValue
|
||||||
|
return when {
|
||||||
|
expr.operator!="+" -> null
|
||||||
|
expr.left is BinaryExpression && rightStrval!=null -> {
|
||||||
|
val subStrVal = concatString(expr.left as BinaryExpression)
|
||||||
|
if(subStrVal==null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
StringLiteralValue("${subStrVal.value}${rightStrval.value}", subStrVal.altEncoding, rightStrval.position)
|
||||||
|
}
|
||||||
|
expr.right is BinaryExpression && leftStrval!=null -> {
|
||||||
|
val subStrVal = concatString(expr.right as BinaryExpression)
|
||||||
|
if(subStrVal==null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
StringLiteralValue("${leftStrval.value}${subStrVal.value}", subStrVal.altEncoding, leftStrval.position)
|
||||||
|
}
|
||||||
|
leftStrval!=null && rightStrval!=null -> {
|
||||||
|
StringLiteralValue("${leftStrval.value}${rightStrval.value}", leftStrval.altEncoding, leftStrval.position)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.*
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
@ -15,7 +12,7 @@ interface IAstModification {
|
|||||||
class Remove(val node: Node, val parent: Node) : IAstModification {
|
class Remove(val node: Node, val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
if(parent is INameScope) {
|
||||||
if (!parent.statements.remove(node))
|
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
||||||
throw FatalAstException("attempt to remove non-existing node $node")
|
throw FatalAstException("attempt to remove non-existing node $node")
|
||||||
} else {
|
} else {
|
||||||
throw FatalAstException("parent of a remove modification is not an INameScope")
|
throw FatalAstException("parent of a remove modification is not an INameScope")
|
||||||
@ -55,7 +52,19 @@ interface IAstModification {
|
|||||||
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
if(parent is INameScope) {
|
||||||
val idx = parent.statements.indexOf(after)+1
|
val idx = parent.statements.indexOfFirst { it===after } + 1
|
||||||
|
parent.statements.add(idx, stmt)
|
||||||
|
stmt.linkParents(parent)
|
||||||
|
} else {
|
||||||
|
throw FatalAstException("parent of an insert modification is not an INameScope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InsertBefore(val before: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
if(parent is INameScope) {
|
||||||
|
val idx = parent.statements.indexOfFirst { it===before }
|
||||||
parent.statements.add(idx, stmt)
|
parent.statements.add(idx, stmt)
|
||||||
stmt.linkParents(parent)
|
stmt.linkParents(parent)
|
||||||
} else {
|
} else {
|
||||||
@ -73,6 +82,7 @@ interface IAstModification {
|
|||||||
|
|
||||||
class SwapOperands(val expr: BinaryExpression): IAstModification {
|
class SwapOperands(val expr: BinaryExpression): IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
|
require(expr.operator in associativeOperators)
|
||||||
val tmp = expr.left
|
val tmp = expr.left
|
||||||
expr.left = expr.right
|
expr.left = expr.right
|
||||||
expr.right = tmp
|
expr.right = tmp
|
||||||
@ -91,13 +101,12 @@ abstract class AstWalker {
|
|||||||
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -113,13 +122,11 @@ abstract class AstWalker {
|
|||||||
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -135,13 +142,12 @@ abstract class AstWalker {
|
|||||||
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -157,13 +163,11 @@ abstract class AstWalker {
|
|||||||
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
@ -225,6 +229,7 @@ abstract class AstWalker {
|
|||||||
track(before(decl, parent), decl, parent)
|
track(before(decl, parent), decl, parent)
|
||||||
decl.value?.accept(this, decl)
|
decl.value?.accept(this, decl)
|
||||||
decl.arraysize?.accept(this, decl)
|
decl.arraysize?.accept(this, decl)
|
||||||
|
decl.struct?.accept(this, decl)
|
||||||
track(after(decl, parent), decl, parent)
|
track(after(decl, parent), decl, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,11 +321,6 @@ abstract class AstWalker {
|
|||||||
track(after(postIncrDecr, parent), postIncrDecr, parent)
|
track(after(postIncrDecr, parent), postIncrDecr, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(contStmt: Continue, parent: Node) {
|
|
||||||
track(before(contStmt, parent), contStmt, parent)
|
|
||||||
track(after(contStmt, parent), contStmt, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(breakStmt: Break, parent: Node) {
|
fun visit(breakStmt: Break, parent: Node) {
|
||||||
track(before(breakStmt, parent), breakStmt, parent)
|
track(before(breakStmt, parent), breakStmt, parent)
|
||||||
track(after(breakStmt, parent), breakStmt, parent)
|
track(after(breakStmt, parent), breakStmt, parent)
|
||||||
@ -328,7 +328,7 @@ abstract class AstWalker {
|
|||||||
|
|
||||||
fun visit(forLoop: ForLoop, parent: Node) {
|
fun visit(forLoop: ForLoop, parent: Node) {
|
||||||
track(before(forLoop, parent), forLoop, parent)
|
track(before(forLoop, parent), forLoop, parent)
|
||||||
forLoop.loopVar?.accept(this, forLoop)
|
forLoop.loopVar.accept(this, forLoop)
|
||||||
forLoop.iterable.accept(this, forLoop)
|
forLoop.iterable.accept(this, forLoop)
|
||||||
forLoop.body.accept(this, forLoop)
|
forLoop.body.accept(this, forLoop)
|
||||||
track(after(forLoop, parent), forLoop, parent)
|
track(after(forLoop, parent), forLoop, parent)
|
||||||
@ -341,19 +341,20 @@ abstract class AstWalker {
|
|||||||
track(after(whileLoop, parent), whileLoop, parent)
|
track(after(whileLoop, parent), whileLoop, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop, parent: Node) {
|
|
||||||
track(before(foreverLoop, parent), foreverLoop, parent)
|
|
||||||
foreverLoop.body.accept(this, foreverLoop)
|
|
||||||
track(after(foreverLoop, parent), foreverLoop, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop, parent: Node) {
|
fun visit(repeatLoop: RepeatLoop, parent: Node) {
|
||||||
track(before(repeatLoop, parent), repeatLoop, parent)
|
track(before(repeatLoop, parent), repeatLoop, parent)
|
||||||
repeatLoop.untilCondition.accept(this, repeatLoop)
|
repeatLoop.iterations?.accept(this, repeatLoop)
|
||||||
repeatLoop.body.accept(this, repeatLoop)
|
repeatLoop.body.accept(this, repeatLoop)
|
||||||
track(after(repeatLoop, parent), repeatLoop, parent)
|
track(after(repeatLoop, parent), repeatLoop, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(untilLoop: UntilLoop, parent: Node) {
|
||||||
|
track(before(untilLoop, parent), untilLoop, parent)
|
||||||
|
untilLoop.condition.accept(this, untilLoop)
|
||||||
|
untilLoop.body.accept(this, untilLoop)
|
||||||
|
track(after(untilLoop, parent), untilLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return, parent: Node) {
|
fun visit(returnStmt: Return, parent: Node) {
|
||||||
track(before(returnStmt, parent), returnStmt, parent)
|
track(before(returnStmt, parent), returnStmt, parent)
|
||||||
returnStmt.value?.accept(this, returnStmt)
|
returnStmt.value?.accept(this, returnStmt)
|
||||||
@ -410,11 +411,6 @@ abstract class AstWalker {
|
|||||||
track(after(inlineAssembly, parent), inlineAssembly, parent)
|
track(after(inlineAssembly, parent), inlineAssembly, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr, parent: Node) {
|
|
||||||
track(before(registerExpr, parent), registerExpr, parent)
|
|
||||||
track(after(registerExpr, parent), registerExpr, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
|
||||||
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
@ -444,11 +440,5 @@ abstract class AstWalker {
|
|||||||
structDecl.statements.forEach { it.accept(this, structDecl) }
|
structDecl.statements.forEach { it.accept(this, structDecl) }
|
||||||
track(after(structDecl, parent), structDecl, parent)
|
track(after(structDecl, parent), structDecl, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue, parent: Node) {
|
|
||||||
track(before(structLv, parent), structLv, parent)
|
|
||||||
structLv.values.forEach { it.accept(this, structLv) }
|
|
||||||
track(after(structLv, parent), structLv, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.statements.ForeverLoop
|
|
||||||
import prog8.ast.statements.RepeatLoop
|
|
||||||
import prog8.ast.statements.WhileLoop
|
|
||||||
|
|
||||||
|
|
||||||
internal class ForeverLoopsMaker: AstWalker() {
|
|
||||||
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
|
||||||
val numeric = repeatLoop.untilCondition as? NumericLiteralValue
|
|
||||||
if(numeric!=null && numeric.number.toInt() == 0) {
|
|
||||||
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent))
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
|
||||||
val numeric = whileLoop.condition as? NumericLiteralValue
|
|
||||||
if(numeric!=null && numeric.number.toInt() != 0) {
|
|
||||||
val forever = ForeverLoop(whileLoop.body, whileLoop.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
|
|
||||||
|
|
||||||
interface IAstModifyingVisitor {
|
|
||||||
fun visit(program: Program) {
|
|
||||||
program.modules.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(module: Module) {
|
|
||||||
module.statements = module.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(expr: PrefixExpression): Expression {
|
|
||||||
expr.expression = expr.expression.accept(this)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(expr: BinaryExpression): Expression {
|
|
||||||
expr.left = expr.left.accept(this)
|
|
||||||
expr.right = expr.right.accept(this)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(directive: Directive): Statement {
|
|
||||||
return directive
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(block: Block): Statement {
|
|
||||||
block.statements = block.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return block
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(decl: VarDecl): Statement {
|
|
||||||
decl.value = decl.value?.accept(this)
|
|
||||||
decl.arraysize?.accept(this)
|
|
||||||
return decl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine): Statement {
|
|
||||||
subroutine.statements = subroutine.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return subroutine
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(functionCall: FunctionCall): Expression {
|
|
||||||
val newtarget = functionCall.target.accept(this)
|
|
||||||
if(newtarget is IdentifierReference)
|
|
||||||
functionCall.target = newtarget
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot change class of function call target")
|
|
||||||
functionCall.args = functionCall.args.map { it.accept(this) }.toMutableList()
|
|
||||||
return functionCall
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
val newtarget = functionCallStatement.target.accept(this)
|
|
||||||
if(newtarget is IdentifierReference)
|
|
||||||
functionCallStatement.target = newtarget
|
|
||||||
else
|
|
||||||
throw FatalAstException("cannot change class of function call target")
|
|
||||||
functionCallStatement.args = functionCallStatement.args.map { it.accept(this) }.toMutableList()
|
|
||||||
return functionCallStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(identifier: IdentifierReference): Expression {
|
|
||||||
// note: this is an identifier that is used in an expression.
|
|
||||||
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
|
||||||
return identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(jump: Jump): Statement {
|
|
||||||
if(jump.identifier!=null) {
|
|
||||||
val ident = jump.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference && ident!==jump.identifier) {
|
|
||||||
return Jump(null, ident, null, jump.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return jump
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(ifStatement: IfStatement): Statement {
|
|
||||||
ifStatement.condition = ifStatement.condition.accept(this)
|
|
||||||
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
|
||||||
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
|
||||||
return ifStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(branchStatement: BranchStatement): Statement {
|
|
||||||
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
|
||||||
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
|
||||||
return branchStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(range: RangeExpr): Expression {
|
|
||||||
range.from = range.from.accept(this)
|
|
||||||
range.to = range.to.accept(this)
|
|
||||||
range.step = range.step.accept(this)
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(label: Label): Statement {
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(literalValue: NumericLiteralValue): NumericLiteralValue {
|
|
||||||
return literalValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(stringLiteral: StringLiteralValue): Expression {
|
|
||||||
return stringLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
|
||||||
for(av in arrayLiteral.value.withIndex()) {
|
|
||||||
val newvalue = av.value.accept(this)
|
|
||||||
arrayLiteral.value[av.index] = newvalue
|
|
||||||
}
|
|
||||||
return arrayLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(assignment: Assignment): Statement {
|
|
||||||
assignment.target = assignment.target.accept(this)
|
|
||||||
assignment.value = assignment.value.accept(this)
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(postIncrDecr: PostIncrDecr): Statement {
|
|
||||||
postIncrDecr.target = postIncrDecr.target.accept(this)
|
|
||||||
return postIncrDecr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(contStmt: Continue): Statement {
|
|
||||||
return contStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(breakStmt: Break): Statement {
|
|
||||||
return breakStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(forLoop: ForLoop): Statement {
|
|
||||||
when(val newloopvar = forLoop.loopVar?.accept(this)) {
|
|
||||||
is IdentifierReference -> forLoop.loopVar = newloopvar
|
|
||||||
null -> forLoop.loopVar = null
|
|
||||||
else -> throw FatalAstException("can't change class of loopvar")
|
|
||||||
}
|
|
||||||
forLoop.iterable = forLoop.iterable.accept(this)
|
|
||||||
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
|
||||||
return forLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whileLoop: WhileLoop): Statement {
|
|
||||||
whileLoop.condition = whileLoop.condition.accept(this)
|
|
||||||
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
|
||||||
return whileLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop): Statement {
|
|
||||||
foreverLoop.body = foreverLoop.body.accept(this) as AnonymousScope
|
|
||||||
return foreverLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop): Statement {
|
|
||||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
|
||||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
|
||||||
return repeatLoop
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(returnStmt: Return): Statement {
|
|
||||||
returnStmt.value = returnStmt.value?.accept(this)
|
|
||||||
return returnStmt
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): ArrayIndexedExpression {
|
|
||||||
val ident = arrayIndexedExpression.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference)
|
|
||||||
arrayIndexedExpression.identifier = ident
|
|
||||||
arrayIndexedExpression.arrayspec.accept(this)
|
|
||||||
return arrayIndexedExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(assignTarget: AssignTarget): AssignTarget {
|
|
||||||
when (val ident = assignTarget.identifier?.accept(this)) {
|
|
||||||
is IdentifierReference -> assignTarget.identifier = ident
|
|
||||||
null -> assignTarget.identifier = null
|
|
||||||
else -> throw FatalAstException("can't change class of assign target identifier")
|
|
||||||
}
|
|
||||||
assignTarget.arrayindexed = assignTarget.arrayindexed?.accept(this)
|
|
||||||
assignTarget.memoryAddress?.let { visit(it) }
|
|
||||||
return assignTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(scope: AnonymousScope): Statement {
|
|
||||||
scope.statements = scope.statements.map { it.accept(this) }.toMutableList()
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(typecast: TypecastExpression): Expression {
|
|
||||||
typecast.expression = typecast.expression.accept(this)
|
|
||||||
return typecast
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
memread.addressExpression = memread.addressExpression.accept(this)
|
|
||||||
return memread
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(memwrite: DirectMemoryWrite) {
|
|
||||||
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(addressOf: AddressOf): Expression {
|
|
||||||
val ident = addressOf.identifier.accept(this)
|
|
||||||
if(ident is IdentifierReference)
|
|
||||||
addressOf.identifier = ident
|
|
||||||
else
|
|
||||||
throw FatalAstException("can't change class of addressof identifier")
|
|
||||||
return addressOf
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(inlineAssembly: InlineAssembly): Statement {
|
|
||||||
return inlineAssembly
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr): Expression {
|
|
||||||
return registerExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): Statement {
|
|
||||||
return builtinFunctionStatementPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(nopStatement: NopStatement): Statement {
|
|
||||||
return nopStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whenStatement: WhenStatement): Statement {
|
|
||||||
whenStatement.condition = whenStatement.condition.accept(this)
|
|
||||||
whenStatement.choices.forEach { it.accept(this) }
|
|
||||||
return whenStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(whenChoice: WhenChoice) {
|
|
||||||
whenChoice.values = whenChoice.values?.map { it.accept(this) }
|
|
||||||
val stmt = whenChoice.statements.accept(this)
|
|
||||||
if(stmt is AnonymousScope)
|
|
||||||
whenChoice.statements = stmt
|
|
||||||
else {
|
|
||||||
whenChoice.statements = AnonymousScope(mutableListOf(stmt), stmt.position)
|
|
||||||
whenChoice.statements.linkParents(whenChoice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(structDecl: StructDecl): Statement {
|
|
||||||
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
|
||||||
return structDecl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue): Expression {
|
|
||||||
structLv.values = structLv.values.map { it.accept(this) }
|
|
||||||
return structLv
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,6 +33,7 @@ interface IAstVisitor {
|
|||||||
fun visit(decl: VarDecl) {
|
fun visit(decl: VarDecl) {
|
||||||
decl.value?.accept(this)
|
decl.value?.accept(this)
|
||||||
decl.arraysize?.accept(this)
|
decl.arraysize?.accept(this)
|
||||||
|
decl.struct?.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine) {
|
fun visit(subroutine: Subroutine) {
|
||||||
@ -95,14 +96,11 @@ interface IAstVisitor {
|
|||||||
postIncrDecr.target.accept(this)
|
postIncrDecr.target.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(contStmt: Continue) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(breakStmt: Break) {
|
fun visit(breakStmt: Break) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(forLoop: ForLoop) {
|
fun visit(forLoop: ForLoop) {
|
||||||
forLoop.loopVar?.accept(this)
|
forLoop.loopVar.accept(this)
|
||||||
forLoop.iterable.accept(this)
|
forLoop.iterable.accept(this)
|
||||||
forLoop.body.accept(this)
|
forLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
@ -112,13 +110,14 @@ interface IAstVisitor {
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(foreverLoop: ForeverLoop) {
|
fun visit(repeatLoop: RepeatLoop) {
|
||||||
foreverLoop.body.accept(this)
|
repeatLoop.iterations?.accept(this)
|
||||||
|
repeatLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop) {
|
fun visit(untilLoop: UntilLoop) {
|
||||||
repeatLoop.untilCondition.accept(this)
|
untilLoop.condition.accept(this)
|
||||||
repeatLoop.body.accept(this)
|
untilLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(returnStmt: Return) {
|
fun visit(returnStmt: Return) {
|
||||||
@ -159,9 +158,6 @@ interface IAstVisitor {
|
|||||||
fun visit(inlineAssembly: InlineAssembly) {
|
fun visit(inlineAssembly: InlineAssembly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(registerExpr: RegisterExpr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +177,4 @@ interface IAstVisitor {
|
|||||||
fun visit(structDecl: StructDecl) {
|
fun visit(structDecl: StructDecl) {
|
||||||
structDecl.statements.forEach { it.accept(this) }
|
structDecl.statements.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structLv: StructLiteralValue) {
|
|
||||||
structLv.values.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,12 @@ internal class ImportedModuleDirectiveRemover: AstWalker() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
||||||
if(directive.directive in moduleLevelDirectives) {
|
if(directive.directive in moduleLevelDirectives) {
|
||||||
return listOf(IAstModification.Remove(directive, parent))
|
return listOf(IAstModification.Remove(directive, parent))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
53
compiler/src/prog8/ast/processing/LiteralsToAutoVars.kt
Normal file
53
compiler/src/prog8/ast/processing/LiteralsToAutoVars.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(string.parent !is VarDecl) {
|
||||||
|
// replace the literal string by a identifier reference to a new local vardecl
|
||||||
|
val vardecl = VarDecl.createAuto(string)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(string, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl, string.definingScope() as Node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
val vardecl = array.parent as? VarDecl
|
||||||
|
if(vardecl!=null) {
|
||||||
|
// adjust the datatype of the array (to an educated guess)
|
||||||
|
val arrayDt = array.type
|
||||||
|
if(!arrayDt.istype(vardecl.datatype)) {
|
||||||
|
val cast = array.cast(vardecl.datatype)
|
||||||
|
if (cast != null && cast !== array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if(arrayDt.isKnown) {
|
||||||
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
if(litval2!=null) {
|
||||||
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl2, array.definingScope() as Node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,9 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
import prog8.functions.FSignature
|
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program) : AstWalker() {
|
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
// Reorders the statements in a way the compiler needs.
|
// Reorders the statements in a way the compiler needs.
|
||||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||||
// - library blocks are put last.
|
// - library blocks are put last.
|
||||||
@ -17,12 +14,12 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
// - in every scope, most directives and vardecls are moved to the top.
|
// - in every scope, most directives and vardecls are moved to the top.
|
||||||
// - the 'start' subroutine is moved to the top.
|
// - the 'start' subroutine is moved to the top.
|
||||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||||
// - (syntax desugaring) augmented assignment is turned into regular assignment.
|
|
||||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
||||||
|
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
||||||
// - sorts the choices in when statement.
|
// - sorts the choices in when statement.
|
||||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
||||||
@ -36,7 +33,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(module.statements)
|
reorderVardeclsAndDirectives(module.statements)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
||||||
@ -59,7 +56,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(block.statements)
|
reorderVardeclsAndDirectives(block.statements)
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
@ -71,26 +68,9 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
val declValue = decl.value
|
|
||||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
val declConstValue = declValue.constValue(program)
|
|
||||||
if(declConstValue==null) {
|
|
||||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
|
||||||
decl.value = null
|
|
||||||
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
val assign = Assignment(target, null, declValue, decl.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
|
||||||
IAstModification.InsertFirst(decl, decl.definingScope() as Node)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val choices = whenStatement.choiceValues(program).sortedBy {
|
val choices = whenStatement.choiceValues(program).sortedBy {
|
||||||
@ -98,124 +78,166 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
whenStatement.choices.clear()
|
whenStatement.choices.clear()
|
||||||
choices.mapTo(whenStatement.choices) { it.second }
|
choices.mapTo(whenStatement.choices) { it.second }
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
if(assignment.aug_op!=null) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(assignment, assignment.asDesugaredNonaugmented(), parent))
|
|
||||||
}
|
|
||||||
|
|
||||||
val valueType = assignment.value.inferType(program)
|
val valueType = assignment.value.inferType(program)
|
||||||
val targetType = assignment.target.inferType(program, assignment)
|
val targetType = assignment.target.inferType(program, assignment)
|
||||||
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
|
var assignments = emptyList<Assignment>()
|
||||||
val assignments = if (assignment.value is StructLiteralValue) {
|
|
||||||
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = { ..... } '
|
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
||||||
|
assignments = if (assignment.value is ArrayLiteralValue) {
|
||||||
|
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
|
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
||||||
|
assignments = if (assignment.value is ArrayLiteralValue) {
|
||||||
|
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
||||||
|
} else {
|
||||||
|
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(assignments.isNotEmpty()) {
|
if(assignments.isNotEmpty()) {
|
||||||
val modifications = mutableListOf<IAstModification>()
|
val modifications = mutableListOf<IAstModification>()
|
||||||
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, parent) }
|
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, parent) }
|
||||||
modifications.add(IAstModification.Remove(assignment, parent))
|
modifications.add(IAstModification.Remove(assignment, parent))
|
||||||
return modifications
|
return modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
return emptyList()
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
// rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if(binExpr!=null) {
|
||||||
|
if(binExpr.left isSameAs assignment.target) {
|
||||||
|
// A = A <operator> 5, unchanged
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
if(binExpr.operator in associativeOperators) {
|
||||||
// insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
if (binExpr.right isSameAs assignment.target) {
|
||||||
var parentStatement: Node = functionCall
|
// A = v <associative-operator> A ==> A = A <associative-operator> v
|
||||||
while(parentStatement !is Statement)
|
return listOf(IAstModification.SwapOperands(binExpr))
|
||||||
parentStatement = parentStatement.parent
|
}
|
||||||
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
|
|
||||||
if(targetStatement!=null) {
|
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||||
return addAddressOfExprIfNeeded(targetStatement, functionCall.args, functionCall)
|
if(leftBinExpr?.operator == binExpr.operator) {
|
||||||
|
return if(leftBinExpr.left isSameAs assignment.target) {
|
||||||
|
// A = (A <associative-operator> x) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
||||||
|
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
|
||||||
|
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
} else {
|
} else {
|
||||||
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
|
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
||||||
if(builtinFunc!=null)
|
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
|
||||||
return addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.args, functionCall)
|
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
}
|
}
|
||||||
return emptyList()
|
|
||||||
}
|
}
|
||||||
|
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
if(rightBinExpr?.operator == binExpr.operator) {
|
||||||
// insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
return if(rightBinExpr.left isSameAs assignment.target) {
|
||||||
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
|
// A = x <associative-operator> (A <same-operator> y) ==> A = A <associative-operator> (x <same-operator> y)
|
||||||
if(targetStatement!=null) {
|
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.right, binExpr.position)
|
||||||
return addAddressOfExprIfNeeded(targetStatement, functionCallStatement.args, functionCallStatement)
|
val newValue = BinaryExpression(rightBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
} else {
|
} else {
|
||||||
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
|
// A = x <associative-operator> (y <same-operator> A) ==> A = A <associative-operator> (x <same-operator> y)
|
||||||
if(builtinFunc!=null)
|
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.left, binExpr.position)
|
||||||
return addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.args, functionCallStatement)
|
val newValue = BinaryExpression(rightBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
|
||||||
|
// TODO use a pointer loop instead of individual assignments
|
||||||
|
|
||||||
|
val identifier = assign.target.identifier!!
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
|
||||||
|
val alv = assign.value as? ArrayLiteralValue
|
||||||
|
if(targetVar.arraysize==null) {
|
||||||
|
errors.err("array has no defined size", identifier.position)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, args: MutableList<Expression>, parent: IFunctionCall): Iterable<IAstModification> {
|
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
||||||
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
errors.err("element count mismatch", assign.position)
|
||||||
val replacements = mutableListOf<IAstModification>()
|
return emptyList()
|
||||||
for(argparam in subroutine.parameters.withIndex().zip(args)) {
|
|
||||||
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type == DataType.STR) {
|
|
||||||
if(argparam.second is AddressOf)
|
|
||||||
continue
|
|
||||||
val idref = argparam.second as? IdentifierReference
|
|
||||||
if(idref!=null) {
|
|
||||||
val variable = idref.targetVarDecl(program.namespace)
|
|
||||||
if(variable!=null && variable.datatype in IterableDatatypes) {
|
|
||||||
replacements += IAstModification.ReplaceNode(
|
|
||||||
args[argparam.first.index],
|
|
||||||
AddressOf(idref, idref.position),
|
|
||||||
parent as Node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return replacements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addAddressOfExprIfNeededForBuiltinFuncs(signature: FSignature, args: MutableList<Expression>, parent: IFunctionCall): Iterable<IAstModification> {
|
return alv.value.withIndex().map { (index, value)->
|
||||||
// val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, assign.position), assign.position), assign.position)
|
||||||
val replacements = mutableListOf<IAstModification>()
|
Assignment(AssignTarget(null, idx, null, assign.position), value, value.position)
|
||||||
for(arg in args.withIndex().zip(signature.parameters)) {
|
|
||||||
val argvalue = arg.first.value
|
|
||||||
val argDt = argvalue.inferType(program)
|
|
||||||
if(argDt.typeOrElse(DataType.UBYTE) in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) {
|
|
||||||
if(argvalue !is IdentifierReference)
|
|
||||||
throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue")
|
|
||||||
replacements += IAstModification.ReplaceNode(
|
|
||||||
args[arg.first.index],
|
|
||||||
AddressOf(argvalue, argvalue.position),
|
|
||||||
parent as Node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return replacements
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
|
||||||
|
// TODO use a pointer loop instead of individual assignments
|
||||||
|
|
||||||
|
val identifier = assign.target.identifier!!
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
|
||||||
|
val sourceIdent = assign.value as IdentifierReference
|
||||||
|
val sourceVar = sourceIdent.targetVarDecl(program.namespace)!!
|
||||||
|
if(!sourceVar.isArray) {
|
||||||
|
errors.err("value must be an array", sourceIdent.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
if(targetVar.arraysize==null) {
|
||||||
|
errors.err("array has no defined size", identifier.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val alv = sourceVar.value as? ArrayLiteralValue
|
||||||
|
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
||||||
|
errors.err("element count mismatch", assign.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return alv.value.withIndex().map { (index, value)->
|
||||||
|
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, assign.position), assign.position), assign.position)
|
||||||
|
Assignment(AssignTarget(null, idx, null, assign.position), value, value.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> {
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
|
|
||||||
val slv = structAssignment.value as? StructLiteralValue
|
val slv = structAssignment.value as? ArrayLiteralValue
|
||||||
if(slv==null || slv.values.size != struct.numberOfElements)
|
if(slv==null || slv.value.size != struct.numberOfElements) {
|
||||||
throw FatalAstException("element count mismatch")
|
errors.err("element count mismatch", structAssignment.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
return struct.statements.zip(slv.values).map { (targetDecl, sourceValue) ->
|
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
||||||
targetDecl as VarDecl
|
targetDecl as VarDecl
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||||
null, sourceValue, sourceValue.position)
|
sourceValue, sourceValue.position)
|
||||||
assign.linkParents(structAssignment)
|
assign.linkParents(structAssignment)
|
||||||
assign
|
assign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
|
||||||
|
// TODO use memcopy beyond a certain number of elements
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
@ -223,14 +245,16 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
when (structAssignment.value) {
|
when (structAssignment.value) {
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
if (sourceVar.struct == null)
|
when {
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
sourceVar.struct!=null -> {
|
||||||
// struct memberwise copy
|
// struct memberwise copy
|
||||||
val sourceStruct = sourceVar.struct!!
|
val sourceStruct = sourceVar.struct!!
|
||||||
if(sourceStruct!==targetVar.struct) {
|
if(sourceStruct!==targetVar.struct) {
|
||||||
// structs are not the same in assignment
|
// structs are not the same in assignment
|
||||||
return listOf() // error will be printed elsewhere
|
return listOf() // error will be printed elsewhere
|
||||||
}
|
}
|
||||||
|
if(struct.statements.size!=sourceStruct.statements.size)
|
||||||
|
return listOf() // error will be printed elsewhere
|
||||||
return struct.statements.zip(sourceStruct.statements).map { member ->
|
return struct.statements.zip(sourceStruct.statements).map { member ->
|
||||||
val targetDecl = member.first as VarDecl
|
val targetDecl = member.first as VarDecl
|
||||||
val sourceDecl = member.second as VarDecl
|
val sourceDecl = member.second as VarDecl
|
||||||
@ -240,13 +264,31 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
||||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
||||||
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
|
||||||
null, sourceIdref, member.second.position)
|
|
||||||
assign.linkParents(structAssignment)
|
assign.linkParents(structAssignment)
|
||||||
assign
|
assign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is StructLiteralValue -> {
|
sourceVar.isArray -> {
|
||||||
|
val array = (sourceVar.value as ArrayLiteralValue).value
|
||||||
|
if(struct.statements.size!=array.size)
|
||||||
|
return listOf() // error will be printed elsewhere
|
||||||
|
return struct.statements.zip(array).map {
|
||||||
|
val decl = it.first as VarDecl
|
||||||
|
val mangled = mangledStructMemberName(identifierName, decl.name)
|
||||||
|
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
|
val target = AssignTarget(targetName, null, null, structAssignment.position)
|
||||||
|
val assign = Assignment(target, it.second, structAssignment.position)
|
||||||
|
assign.linkParents(structAssignment)
|
||||||
|
assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw FatalAstException("can only assign arrays or structs to structs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ArrayLiteralValue -> {
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("strange struct value")
|
else -> throw FatalAstException("strange struct value")
|
||||||
|
@ -4,9 +4,7 @@ import prog8.ast.IFunctionCall
|
|||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.ErrorReporter
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
@ -18,6 +16,23 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
* (this includes function call arguments)
|
* (this includes function call arguments)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val declValue = decl.value
|
||||||
|
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
||||||
|
val valueDt = declValue.inferType(program)
|
||||||
|
if(!valueDt.istype(decl.datatype)) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
declValue,
|
||||||
|
TypecastExpression(declValue, decl.datatype, true, declValue.position),
|
||||||
|
decl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val leftDt = expr.left.inferType(program)
|
val leftDt = expr.left.inferType(program)
|
||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
@ -34,7 +49,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
@ -45,13 +60,41 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
if (valuetype != targettype) {
|
if (valuetype != targettype) {
|
||||||
|
if (valuetype isAssignableTo targettype) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
assignment.value,
|
assignment.value,
|
||||||
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||||
assignment))
|
assignment))
|
||||||
|
} else {
|
||||||
|
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> {
|
||||||
|
val cast = cvalue.cast(targettype)
|
||||||
|
return if(cast.isValid)
|
||||||
|
listOf(IAstModification.ReplaceNode(cvalue, cast.valueOrZero(), cvalue.parent))
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
val cvalue = assignment.value.constValue(program)
|
||||||
|
if(cvalue!=null) {
|
||||||
|
val number = cvalue.number.toDouble()
|
||||||
|
// more complex comparisons if the type is different, but the constant value is compatible
|
||||||
|
if (valuetype == DataType.BYTE && targettype == DataType.UBYTE) {
|
||||||
|
if(number>0)
|
||||||
|
return castLiteral(cvalue)
|
||||||
|
} else if (valuetype == DataType.WORD && targettype == DataType.UWORD) {
|
||||||
|
if(number>0)
|
||||||
|
return castLiteral(cvalue)
|
||||||
|
} else if (valuetype == DataType.UBYTE && targettype == DataType.BYTE) {
|
||||||
|
if(number<0x80)
|
||||||
|
return castLiteral(cvalue)
|
||||||
|
} else if (valuetype == DataType.UWORD && targettype == DataType.WORD) {
|
||||||
|
if(number<0x8000)
|
||||||
|
return castLiteral(cvalue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
@ -64,7 +107,9 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
|
|
||||||
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
||||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
return when(val sub = call.target.targetStatement(scope)) {
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
when(val sub = call.target.targetStatement(scope)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
@ -73,20 +118,32 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val requiredType = arg.first.type
|
val requiredType = arg.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
|
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
||||||
|
if(arg.second.value is IdentifierReference) {
|
||||||
|
modifications += IAstModification.ReplaceNode(
|
||||||
|
call.args[arg.second.index],
|
||||||
|
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||||
|
call as Node)
|
||||||
|
}
|
||||||
|
} else if(arg.second.value is NumericLiteralValue) {
|
||||||
|
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||||
|
if(cast.isValid)
|
||||||
|
modifications += IAstModification.ReplaceNode(
|
||||||
|
call.args[arg.second.index],
|
||||||
|
cast.valueOrZero(),
|
||||||
|
call as Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
|
||||||
}
|
}
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
val func = BuiltinFunctions.getValue(sub.name)
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
if(func.pure) {
|
|
||||||
// non-pure functions don't get automatic typecasts because sometimes they act directly on their parameters
|
|
||||||
for (arg in func.parameters.zip(call.args.withIndex())) {
|
for (arg in func.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
if (argItype.isKnown) {
|
if (argItype.isKnown) {
|
||||||
@ -95,95 +152,50 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
continue
|
continue
|
||||||
for (possibleType in arg.first.possibleDatatypes) {
|
for (possibleType in arg.first.possibleDatatypes) {
|
||||||
if (argtype isAssignableTo possibleType) {
|
if (argtype isAssignableTo possibleType) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
||||||
call as Node))
|
call as Node)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emptyList()
|
else -> { }
|
||||||
}
|
|
||||||
null -> emptyList()
|
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
// warn about any implicit type casts to Float, because that may not be intended
|
||||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
errors.warn("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
errors.warn("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// make sure the memory address is an uword
|
// make sure the memory address is an uword
|
||||||
val dt = memread.addressExpression.inferType(program)
|
val dt = memread.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||||
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||||
// make sure the memory address is an uword
|
// make sure the memory address is an uword
|
||||||
val dt = memwrite.addressExpression.inferType(program)
|
val dt = memwrite.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||||
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> {
|
|
||||||
// assignment of a struct literal value, some member values may need proper typecast
|
|
||||||
|
|
||||||
fun addTypecastsIfNeeded(struct: StructDecl): Iterable<IAstModification> {
|
|
||||||
val newValues = struct.statements.zip(structLv.values).map { (structMemberDecl, memberValue) ->
|
|
||||||
val memberDt = (structMemberDecl as VarDecl).datatype
|
|
||||||
val valueDt = memberValue.inferType(program)
|
|
||||||
if (valueDt.typeOrElse(memberDt) != memberDt)
|
|
||||||
TypecastExpression(memberValue, memberDt, true, memberValue.position)
|
|
||||||
else
|
|
||||||
memberValue
|
|
||||||
}
|
|
||||||
|
|
||||||
class StructLvValueReplacer(val targetStructLv: StructLiteralValue, val typecastValues: List<Expression>) : IAstModification {
|
|
||||||
override fun perform() {
|
|
||||||
targetStructLv.values = typecastValues
|
|
||||||
typecastValues.forEach { it.linkParents(targetStructLv) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if(structLv.values.zip(newValues).any { (v1, v2) -> v1 !== v2})
|
|
||||||
listOf(StructLvValueReplacer(structLv, newValues))
|
|
||||||
else
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val decl = structLv.parent as? VarDecl
|
|
||||||
if(decl != null) {
|
|
||||||
val struct = decl.struct
|
|
||||||
if(struct != null)
|
|
||||||
return addTypecastsIfNeeded(struct)
|
|
||||||
} else {
|
|
||||||
val assign = structLv.parent as? Assignment
|
|
||||||
if (assign != null) {
|
|
||||||
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
|
||||||
if(decl2 != null) {
|
|
||||||
val struct = decl2.struct
|
|
||||||
if(struct != null)
|
|
||||||
return addTypecastsIfNeeded(struct)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
@ -194,9 +206,11 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
if(subroutine.returntypes.size==1) {
|
if(subroutine.returntypes.size==1) {
|
||||||
val subReturnType = subroutine.returntypes.first()
|
val subReturnType = subroutine.returntypes.first()
|
||||||
if (returnValue.inferType(program).istype(subReturnType))
|
if (returnValue.inferType(program).istype(subReturnType))
|
||||||
return emptyList()
|
return noModifications
|
||||||
if (returnValue is NumericLiteralValue) {
|
if (returnValue is NumericLiteralValue) {
|
||||||
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
val cast = returnValue.cast(subroutine.returntypes.single())
|
||||||
|
if(cast.isValid)
|
||||||
|
returnStmt.value = cast.valueOrZero()
|
||||||
} else {
|
} else {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
returnValue,
|
returnValue,
|
||||||
@ -205,6 +219,6 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
44
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.NopStatement
|
||||||
|
|
||||||
|
|
||||||
|
internal class VariousCleanups: AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
return listOf(IAstModification.Remove(nopStatement, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(parent is INameScope)
|
||||||
|
listOf(ScopeFlatten(scope, parent as INameScope))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
val idx = into.statements.indexOf(scope)
|
||||||
|
if(idx>=0) {
|
||||||
|
into.statements.addAll(idx+1, scope.statements)
|
||||||
|
into.statements.remove(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(typecast.expression is NumericLiteralValue) {
|
||||||
|
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||||
|
if(value.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
83
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
83
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
|
override fun visit(functionCall: FunctionCall) {
|
||||||
|
val error = checkTypes(functionCall as IFunctionCall, functionCall.definingScope(), program)
|
||||||
|
if(error!=null)
|
||||||
|
throw CompilerException(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
|
val error = checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope(), program)
|
||||||
|
if (error!=null)
|
||||||
|
throw CompilerException(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun argTypeCompatible(argDt: DataType, paramDt: DataType): Boolean {
|
||||||
|
if(argDt==paramDt)
|
||||||
|
return true
|
||||||
|
|
||||||
|
// there are some exceptions that are considered compatible, such as STR <> UWORD
|
||||||
|
if(argDt==DataType.STR && paramDt==DataType.UWORD ||
|
||||||
|
argDt==DataType.UWORD && paramDt==DataType.STR)
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
|
||||||
|
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||||
|
val target = call.target.targetStatement(scope)
|
||||||
|
if (target is Subroutine) {
|
||||||
|
if(call.args.size != target.parameters.size)
|
||||||
|
return "invalid number of arguments"
|
||||||
|
val paramtypes = target.parameters.map { it.type }
|
||||||
|
val mismatch = argtypes.zip(paramtypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
|
||||||
|
if(mismatch>=0) {
|
||||||
|
val actual = argtypes[mismatch].toString()
|
||||||
|
val expected = paramtypes[mismatch].toString()
|
||||||
|
return "argument ${mismatch + 1} type mismatch, was: $actual expected: $expected"
|
||||||
|
}
|
||||||
|
if(target.isAsmSubroutine) {
|
||||||
|
if(target.asmReturnvaluesRegisters.size>1) {
|
||||||
|
// multiple return values will NOT work inside an expression.
|
||||||
|
// they MIGHT work in a regular assignment or just a function call statement.
|
||||||
|
val parent = if(call is Statement) call.parent else if(call is Expression) call.parent else null
|
||||||
|
if(call !is FunctionCallStatement && parent !is Assignment && parent !is VarDecl) {
|
||||||
|
return "can't use subroutine call that returns multiple return values here (try moving it into a separate assignment)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (target is BuiltinFunctionStatementPlaceholder) {
|
||||||
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
|
if(call.args.size != func.parameters.size)
|
||||||
|
return "invalid number of arguments"
|
||||||
|
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||||
|
for (x in argtypes.zip(paramtypes).withIndex()) {
|
||||||
|
val anyCompatible = x.value.second.any { argTypeCompatible(x.value.first, it) }
|
||||||
|
if (!anyCompatible) {
|
||||||
|
val actual = x.value.first.toString()
|
||||||
|
val expected = x.value.second.toString()
|
||||||
|
return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,11 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
|
|
||||||
sealed class Statement : Node {
|
sealed class Statement : Node {
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
|
|
||||||
@ -31,8 +30,6 @@ sealed class Statement : Node {
|
|||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val expensiveToInline: Boolean
|
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
fun definingBlock(): Block {
|
||||||
if(this is Block)
|
if(this is Block)
|
||||||
return this
|
return this
|
||||||
@ -44,14 +41,12 @@ sealed class Statement : Node {
|
|||||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
override val expensiveToInline = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
|
||||||
@ -62,8 +57,7 @@ class Block(override val name: String,
|
|||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val asmGenInfo = AsmGenInfo()
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -72,12 +66,11 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it ===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -90,7 +83,6 @@ class Block(override val name: String,
|
|||||||
|
|
||||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -98,7 +90,6 @@ data class Directive(val directive: String, val args: List<DirectiveArg>, overri
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -114,14 +105,12 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -132,7 +121,6 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
|
|
||||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -145,7 +133,6 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -154,40 +141,14 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return "ReturnFromIrq(pos=$position)"
|
|
||||||
}
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
|
||||||
}
|
|
||||||
|
|
||||||
class Continue(override val position: Position) : Statement() {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent=parent
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Break(override val position: Position) : Statement() {
|
class Break(override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -217,9 +178,6 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
var structHasBeenFlattened = false // set later
|
var structHasBeenFlattened = false // set later
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override val expensiveToInline
|
|
||||||
get() = value!=null && value !is NumericLiteralValue
|
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -258,8 +216,9 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
DataType.UWORD -> DataType.ARRAY_UW
|
DataType.UWORD -> DataType.ARRAY_UW
|
||||||
DataType.WORD -> DataType.ARRAY_W
|
DataType.WORD -> DataType.ARRAY_W
|
||||||
DataType.FLOAT -> DataType.ARRAY_F
|
DataType.FLOAT -> DataType.ARRAY_F
|
||||||
|
DataType.STR -> DataType.ARRAY_UW // use memory address of the string instead
|
||||||
else -> {
|
else -> {
|
||||||
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
|
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats/strings(ptrs)", position))
|
||||||
DataType.ARRAY_UB
|
DataType.ARRAY_UB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,12 +235,12 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression && node===value)
|
// TODO the check that node===value is too strict sometimes, but leaving it out allows for bugs to creep through ... :( Perhaps check when adding the replace if there is already a replace on the same node?
|
||||||
|
require(replacement is Expression)
|
||||||
value = replacement
|
value = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -294,7 +253,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
val result = struct!!.statements.withIndex().map {
|
val result = struct!!.statements.withIndex().map {
|
||||||
val member = it.value as VarDecl
|
val member = it.value as VarDecl
|
||||||
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null
|
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[it.index] else null
|
||||||
VarDecl(
|
VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
member.datatype,
|
member.datatype,
|
||||||
@ -306,8 +265,8 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
member.isArray,
|
member.isArray,
|
||||||
true,
|
true,
|
||||||
member.position
|
member.position
|
||||||
) as Statement
|
)
|
||||||
}.toMutableList()
|
}.toMutableList<Statement>()
|
||||||
structHasBeenFlattened = true
|
structHasBeenFlattened = true
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -315,7 +274,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
|
|
||||||
// a vardecl used only for subroutine parameters
|
// a vardecl used only for subroutine parameters
|
||||||
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
||||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.NOT_IN_ZEROPAGE, null, name, null, null, false, true, position)
|
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||||
|
|
||||||
|
|
||||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||||
@ -338,23 +297,20 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) {
|
|
||||||
index = index.accept(visitor)
|
|
||||||
}
|
|
||||||
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, parent)
|
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, this)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("ArrayIndex($index, pos=$position)")
|
return("ArrayIndex($index, pos=$position)")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
|
fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt()
|
||||||
|
|
||||||
|
infix fun isSameAs(other: ArrayIndex) = index.isSameAs(other.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = value !is NumericLiteralValue
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -371,41 +327,62 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
|
return("Assignment(target: $target, value: $value, pos=$position)")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asDesugaredNonaugmented(): Assignment {
|
/**
|
||||||
val augmented = aug_op ?: return this
|
* Is the assigment value an expression that references the assignment target itself?
|
||||||
|
* The expression can be a BinaryExpression, PrefixExpression or TypecastExpression (possibly with one sub-cast).
|
||||||
|
*/
|
||||||
|
val isAugmentable: Boolean
|
||||||
|
get() {
|
||||||
|
val binExpr = value as? BinaryExpression
|
||||||
|
if(binExpr!=null) {
|
||||||
|
if(binExpr.left isSameAs target)
|
||||||
|
return true // A = A <operator> Something
|
||||||
|
|
||||||
val leftOperand: Expression =
|
if(binExpr.operator in associativeOperators) {
|
||||||
when {
|
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
|
||||||
target.register != null -> RegisterExpr(target.register!!, target.position)
|
return true // A = v <associative-operator> A
|
||||||
target.identifier != null -> target.identifier!!
|
|
||||||
target.arrayindexed != null -> target.arrayindexed!!
|
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||||
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
|
if(leftBinExpr?.operator == binExpr.operator) {
|
||||||
else -> throw FatalAstException("strange this")
|
// one of these?
|
||||||
|
// A = (A <associative-operator> x) <same-operator> y
|
||||||
|
// A = (x <associative-operator> A) <same-operator> y
|
||||||
|
// A = (x <associative-operator> y) <same-operator> A
|
||||||
|
return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target || binExpr.right isSameAs target
|
||||||
|
}
|
||||||
|
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||||
|
if(rightBinExpr?.operator == binExpr.operator) {
|
||||||
|
// one of these?
|
||||||
|
// A = y <associative-operator> (A <same-operator> x)
|
||||||
|
// A = y <associative-operator> (x <same-operator> y)
|
||||||
|
// A = A <associative-operator> (x <same-operator> y)
|
||||||
|
return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target || binExpr.left isSameAs target
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val assignment =
|
val prefixExpr = value as? PrefixExpression
|
||||||
if(augmented=="setvalue") {
|
if(prefixExpr!=null)
|
||||||
Assignment(target, null, value, position)
|
return prefixExpr.expression isSameAs target
|
||||||
} else {
|
|
||||||
val expression = BinaryExpression(leftOperand, augmented.substringBeforeLast('='), value, position)
|
|
||||||
Assignment(target, null, expression, position)
|
|
||||||
}
|
|
||||||
assignment.linkParents(parent)
|
|
||||||
|
|
||||||
return assignment
|
val castExpr = value as? TypecastExpression
|
||||||
|
if(castExpr!=null) {
|
||||||
|
val subCast = castExpr.expression as? TypecastExpression
|
||||||
|
return if(subCast!=null) subCast.expression isSameAs target else castExpr.expression isSameAs target
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AssignTarget(val register: Register?,
|
data class AssignTarget(var identifier: IdentifierReference?,
|
||||||
var identifier: IdentifierReference?,
|
|
||||||
var arrayindexed: ArrayIndexedExpression?,
|
var arrayindexed: ArrayIndexedExpression?,
|
||||||
val memoryAddress: DirectMemoryWrite?,
|
val memoryAddress: DirectMemoryWrite?,
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
@ -420,112 +397,134 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
when {
|
when {
|
||||||
node===identifier -> identifier = replacement as IdentifierReference
|
node === identifier -> identifier = replacement as IdentifierReference
|
||||||
node===arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
|
node === arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
|
||||||
else -> throw FatalAstException("invalid replace")
|
else -> throw FatalAstException("invalid replace")
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromExpr(expr: Expression): AssignTarget {
|
fun fromExpr(expr: Expression): AssignTarget {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
is IdentifierReference -> AssignTarget(expr, null, null, expr.position)
|
||||||
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position)
|
||||||
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
|
is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
||||||
is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
|
||||||
else -> throw FatalAstException("invalid expression object $expr")
|
else -> throw FatalAstException("invalid expression object $expr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
|
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { // TODO why does this have the extra 'stmt' scope parameter???
|
||||||
if(register!=null)
|
if (identifier != null) {
|
||||||
return InferredTypes.knownFor(DataType.UBYTE)
|
|
||||||
|
|
||||||
if(identifier!=null) {
|
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(arrayindexed!=null) {
|
if (arrayindexed != null) {
|
||||||
return arrayindexed!!.inferType(program)
|
return arrayindexed!!.inferType(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(memoryAddress!=null)
|
if (memoryAddress != null)
|
||||||
return InferredTypes.knownFor(DataType.UBYTE)
|
return InferredTypes.knownFor(DataType.UBYTE)
|
||||||
|
|
||||||
return InferredTypes.unknown()
|
return InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun toExpression(): Expression {
|
||||||
|
return when {
|
||||||
|
identifier != null -> identifier!!
|
||||||
|
arrayindexed != null -> arrayindexed!!
|
||||||
|
memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
|
||||||
|
else -> throw FatalAstException("invalid assignmenttarget $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
infix fun isSameAs(value: Expression): Boolean {
|
infix fun isSameAs(value: Expression): Boolean {
|
||||||
return when {
|
return when {
|
||||||
this.memoryAddress!=null -> {
|
memoryAddress != null -> {
|
||||||
// if the target is a memory write, and the value is a memory read, they're the same if the address matches
|
// if the target is a memory write, and the value is a memory read, they're the same if the address matches
|
||||||
if(value is DirectMemoryRead)
|
if (value is DirectMemoryRead)
|
||||||
this.memoryAddress.addressExpression isSameAs value.addressExpression
|
this.memoryAddress.addressExpression isSameAs value.addressExpression
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
this.register!=null -> value is RegisterExpr && value.register==register
|
identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
|
||||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
arrayindexed != null -> {
|
||||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
if(value is ArrayIndexedExpression && value.identifier.nameInSource == arrayindexed!!.identifier.nameInSource)
|
||||||
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
arrayindexed!!.arrayspec isSameAs value.arrayspec
|
||||||
value.arrayspec.size()!=null &&
|
else
|
||||||
arrayindexed!!.arrayspec.size()!=null &&
|
false
|
||||||
value.arrayspec.size()==arrayindexed!!.arrayspec.size()
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
||||||
if(this===other)
|
if (this === other)
|
||||||
return true
|
return true
|
||||||
if(this.register!=null && other.register!=null)
|
if (this.identifier != null && other.identifier != null)
|
||||||
return this.register==other.register
|
return this.identifier!!.nameInSource == other.identifier!!.nameInSource
|
||||||
if(this.identifier!=null && other.identifier!=null)
|
if (this.memoryAddress != null && other.memoryAddress != null) {
|
||||||
return this.identifier!!.nameInSource==other.identifier!!.nameInSource
|
|
||||||
if(this.memoryAddress!=null && other.memoryAddress!=null) {
|
|
||||||
val addr1 = this.memoryAddress.addressExpression.constValue(program)
|
val addr1 = this.memoryAddress.addressExpression.constValue(program)
|
||||||
val addr2 = other.memoryAddress.addressExpression.constValue(program)
|
val addr2 = other.memoryAddress.addressExpression.constValue(program)
|
||||||
return addr1!=null && addr2!=null && addr1==addr2
|
return addr1 != null && addr2 != null && addr1 == addr2
|
||||||
}
|
}
|
||||||
if(this.arrayindexed!=null && other.arrayindexed!=null) {
|
if (this.arrayindexed != null && other.arrayindexed != null) {
|
||||||
if(this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) {
|
if (this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) {
|
||||||
val x1 = this.arrayindexed!!.arrayspec.index.constValue(program)
|
val x1 = this.arrayindexed!!.arrayspec.index.constValue(program)
|
||||||
val x2 = other.arrayindexed!!.arrayspec.index.constValue(program)
|
val x2 = other.arrayindexed!!.arrayspec.index.constValue(program)
|
||||||
return x1!=null && x2!=null && x1==x2
|
return x1 != null && x2 != null && x1 == x2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNotMemory(namespace: INameScope): Boolean {
|
fun isInRegularRAM(namespace: INameScope): Boolean {
|
||||||
if(this.register!=null)
|
when {
|
||||||
return true
|
this.memoryAddress != null -> {
|
||||||
if(this.memoryAddress!=null)
|
return when (this.memoryAddress.addressExpression) {
|
||||||
return false
|
is NumericLiteralValue -> {
|
||||||
if(this.arrayindexed!=null) {
|
CompilationTarget.instance.machine.isRegularRAMaddress((this.memoryAddress.addressExpression as NumericLiteralValue).number.toInt())
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val decl = (this.memoryAddress.addressExpression as IdentifierReference).targetVarDecl(namespace)
|
||||||
|
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
|
||||||
|
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
||||||
|
else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.arrayindexed != null -> {
|
||||||
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace)
|
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace)
|
||||||
if(targetStmt!=null)
|
return if (targetStmt?.type == VarDeclType.MEMORY) {
|
||||||
return targetStmt.type!= VarDeclType.MEMORY
|
val addr = targetStmt.value as? NumericLiteralValue
|
||||||
|
if (addr != null)
|
||||||
|
CompilationTarget.instance.machine.isRegularRAMaddress(addr.number.toInt())
|
||||||
|
else
|
||||||
|
false
|
||||||
|
} else true
|
||||||
}
|
}
|
||||||
if(this.identifier!=null) {
|
this.identifier != null -> {
|
||||||
val targetStmt = this.identifier!!.targetVarDecl(namespace)
|
val decl = this.identifier!!.targetVarDecl(namespace)!!
|
||||||
if(targetStmt!=null)
|
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
|
||||||
return targetStmt.type!= VarDeclType.MEMORY
|
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
||||||
|
else
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> return true
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -538,7 +537,6 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -552,7 +550,6 @@ class Jump(val address: Int?,
|
|||||||
val generatedLabel: String?, // used in code generation scenarios
|
val generatedLabel: String?, // used in code generation scenarios
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -560,7 +557,6 @@ class Jump(val address: Int?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -574,8 +570,6 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
val void: Boolean,
|
val void: Boolean,
|
||||||
override val position: Position) : Statement(), IFunctionCall {
|
override val position: Position) : Statement(), IFunctionCall {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
|
||||||
get() = args.any { it !is NumericLiteralValue }
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -587,13 +581,12 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
if(node===target)
|
if(node===target)
|
||||||
target = replacement as IdentifierReference
|
target = replacement as IdentifierReference
|
||||||
else {
|
else {
|
||||||
val idx = args.indexOf(node)
|
val idx = args.indexOfFirst { it===node }
|
||||||
args[idx] = replacement as Expression
|
args[idx] = replacement as Expression
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -604,14 +597,12 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
|
|
||||||
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -620,8 +611,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override val position: Position) : INameScope, Statement() {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline
|
override val asmGenInfo = AsmGenInfo()
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var sequenceNumber = 1
|
private var sequenceNumber = 1
|
||||||
@ -639,36 +629,25 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NopStatement(override val position: Position): Statement() {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = false
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun insteadOf(stmt: Statement): NopStatement {
|
|
||||||
val nop = NopStatement(stmt.position)
|
|
||||||
nop.parent = stmt.parent
|
|
||||||
return nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the subroutine class covers both the normal user-defined subroutines,
|
// the subroutine class covers both the normal user-defined subroutines,
|
||||||
@ -679,20 +658,14 @@ class Subroutine(override val name: String,
|
|||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<Register>,
|
val asmClobbers: Set<CpuRegister>,
|
||||||
val asmAddress: Int?,
|
val asmAddress: Int?,
|
||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
var keepAlways: Boolean = false
|
|
||||||
override val expensiveToInline
|
|
||||||
get() = statements.any { it.expensiveToInline }
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
val calledBy = mutableListOf<Node>()
|
override val asmGenInfo = AsmGenInfo()
|
||||||
val calls = mutableSetOf<Subroutine>()
|
|
||||||
|
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -703,12 +676,11 @@ class Subroutine(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -716,6 +688,9 @@ class Subroutine(override val name: String,
|
|||||||
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||||
|
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||||
|
|
||||||
fun amountOfRtsInAsm(): Int = statements
|
fun amountOfRtsInAsm(): Int = statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it is InlineAssembly }
|
.filter { it is InlineAssembly }
|
||||||
@ -743,8 +718,6 @@ class IfStatement(var condition: Expression,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -763,7 +736,6 @@ class IfStatement(var condition: Expression,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -774,8 +746,6 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
var elsepart: AnonymousScope,
|
var elsepart: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean
|
|
||||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -792,23 +762,20 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForLoop(val loopRegister: Register?,
|
class ForLoop(var loopVar: IdentifierReference,
|
||||||
var loopVar: IdentifierReference?,
|
|
||||||
var iterable: Expression,
|
var iterable: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent=parent
|
this.parent=parent
|
||||||
loopVar?.linkParents(this)
|
loopVar.linkParents(this)
|
||||||
iterable.linkParents(this)
|
iterable.linkParents(this)
|
||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
@ -823,26 +790,20 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
return "ForLoop(loopVar: $loopVar, iterable: $iterable, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loopVarDt(program: Program): InferredTypes.InferredType {
|
fun loopVarDt(program: Program) = loopVar.inferType(program)
|
||||||
val lv = loopVar
|
|
||||||
return if(loopRegister!=null) InferredTypes.InferredType.known(DataType.UBYTE)
|
|
||||||
else lv?.inferType(program) ?: InferredTypes.InferredType.unknown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhileLoop(var condition: Expression,
|
class WhileLoop(var condition: Expression,
|
||||||
var body: AnonymousScope,
|
var body: AnonymousScope,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -859,53 +820,52 @@ class WhileLoop(var condition: Expression,
|
|||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
|
class RepeatLoop(var iterations: Expression?, var body: AnonymousScope, override val position: Position) : Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
body.linkParents(this)
|
iterations?.linkParents(this)
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(replacement is AnonymousScope && node===body)
|
|
||||||
body = replacement
|
|
||||||
replacement.parent = this
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
class RepeatLoop(var body: AnonymousScope,
|
|
||||||
var untilCondition: Expression,
|
|
||||||
override val position: Position) : Statement() {
|
|
||||||
override lateinit var parent: Node
|
|
||||||
override val expensiveToInline = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
untilCondition.linkParents(this)
|
|
||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
when {
|
when {
|
||||||
node===untilCondition -> untilCondition = replacement as Expression
|
node===iterations -> iterations = replacement as Expression
|
||||||
|
node===body -> body = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
replacement.parent = this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
class UntilLoop(var body: AnonymousScope,
|
||||||
|
var condition: Expression,
|
||||||
|
override val position: Position) : Statement() {
|
||||||
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent = parent
|
||||||
|
condition.linkParents(this)
|
||||||
|
body.linkParents(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===condition -> condition = replacement as Expression
|
||||||
node===body -> body = replacement as AnonymousScope
|
node===body -> body = replacement as AnonymousScope
|
||||||
else -> throw FatalAstException("invalid replace")
|
else -> throw FatalAstException("invalid replace")
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
@ -914,7 +874,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
var choices: MutableList<WhenChoice>,
|
var choices: MutableList<WhenChoice>,
|
||||||
override val position: Position): Statement() {
|
override val position: Position): Statement() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -926,7 +885,7 @@ class WhenStatement(var condition: Expression,
|
|||||||
if(node===condition)
|
if(node===condition)
|
||||||
condition = replacement as Expression
|
condition = replacement as Expression
|
||||||
else {
|
else {
|
||||||
val idx = choices.indexOf(node)
|
val idx = choices.withIndex().find { it.value===node }!!.index
|
||||||
choices[idx] = replacement as WhenChoice
|
choices[idx] = replacement as WhenChoice
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -950,7 +909,6 @@ class WhenStatement(var condition: Expression,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,7 +934,6 @@ class WhenChoice(var values: List<Expression>?, // if null, this is t
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,7 +943,7 @@ class StructDecl(override val name: String,
|
|||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override val expensiveToInline: Boolean = true
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -995,7 +952,7 @@ class StructDecl(override val name: String,
|
|||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
require(replacement is Statement)
|
||||||
val idx = statements.indexOf(node)
|
val idx = statements.indexOfFirst { it===node }
|
||||||
statements[idx] = replacement
|
statements[idx] = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -1004,7 +961,6 @@ class StructDecl(override val name: String,
|
|||||||
get() = this.statements.size
|
get() = this.statements.size
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
||||||
@ -1029,6 +985,5 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
208
compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt
Normal file
208
compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package prog8.compiler
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
subroutineVariables.add(Pair(decl.name, decl))
|
||||||
|
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
// a numeric vardecl without an initial value is initialized with zero,
|
||||||
|
// unless there's already an assignment below, that initializes the value
|
||||||
|
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
|
||||||
|
if(nextAssign!=null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY)))
|
||||||
|
decl.value = null
|
||||||
|
else
|
||||||
|
decl.value = decl.zeroElementValue()
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
// Try to replace A = B <operator> Something by A= B, A = A <operator> Something
|
||||||
|
// this triggers the more efficent augmented assignment code generation more often.
|
||||||
|
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
|
||||||
|
if(!assignment.isAugmentable
|
||||||
|
&& assignment.target.identifier != null
|
||||||
|
&& assignment.target.isInRegularRAM(program.namespace)) {
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if (binExpr != null && binExpr.operator !in comparisonOperators) {
|
||||||
|
if (binExpr.left !is BinaryExpression) {
|
||||||
|
if (binExpr.right.referencesIdentifier(*assignment.target.identifier!!.nameInSource.toTypedArray())) {
|
||||||
|
// the right part of the expression contains the target variable itself.
|
||||||
|
// we can't 'split' it trivially because the variable will be changed halfway through.
|
||||||
|
if(binExpr.operator in associativeOperators) {
|
||||||
|
// A = <something-without-A> <associativeoperator> <otherthing-with-A>
|
||||||
|
// use the other part of the expression to split.
|
||||||
|
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.InsertBefore(assignment, assignRight, parent),
|
||||||
|
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
||||||
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.InsertBefore(assignment, assignLeft, parent),
|
||||||
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private val subroutineVariables = mutableListOf<Pair<String, VarDecl>>()
|
||||||
|
|
||||||
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
subroutineVariables.clear()
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||||
|
subroutineVariables.addAll(decls.map { Pair(it.name, it) })
|
||||||
|
|
||||||
|
val sub = scope.definingSubroutine()
|
||||||
|
if (sub != null) {
|
||||||
|
// move vardecls of the scope into the upper scope. Make sure the position remains the same!
|
||||||
|
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }
|
||||||
|
val replaceVardecls =numericVarsWithValue.map {
|
||||||
|
val initValue = it.value!! // assume here that value has always been set by now
|
||||||
|
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||||
|
val assign = Assignment(target, initValue, it.position)
|
||||||
|
initValue.parent = assign
|
||||||
|
IAstModification.ReplaceNode(it, assign, scope)
|
||||||
|
}
|
||||||
|
val moveVardeclsUp = decls.map { IAstModification.InsertFirst(it, sub) }
|
||||||
|
return replaceVardecls + moveVardeclsUp
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
val firstDeclarations = mutableMapOf<String, VarDecl>()
|
||||||
|
for(decl in subroutineVariables) {
|
||||||
|
val existing = firstDeclarations[decl.first]
|
||||||
|
if(existing!=null && existing !== decl.second) {
|
||||||
|
errors.err("variable ${decl.first} already defined in subroutine ${subroutine.name} at ${existing.position}", decl.second.position)
|
||||||
|
} else {
|
||||||
|
firstDeclarations[decl.first] = decl.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
||||||
|
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||||
|
val mods = mutableListOf<IAstModification>()
|
||||||
|
val returnStmt = Return(null, subroutine.position)
|
||||||
|
if (subroutine.asmAddress == null
|
||||||
|
&& subroutine.statements.isNotEmpty()
|
||||||
|
&& subroutine.amountOfRtsInAsm() == 0
|
||||||
|
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
|
||||||
|
&& subroutine.statements.last() !is Subroutine) {
|
||||||
|
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
|
||||||
|
val outerScope = subroutine.definingScope()
|
||||||
|
val outerStatements = outerScope.statements
|
||||||
|
val subroutineStmtIdx = outerStatements.indexOf(subroutine)
|
||||||
|
if (subroutineStmtIdx > 0
|
||||||
|
&& outerStatements[subroutineStmtIdx - 1] !is Jump
|
||||||
|
&& outerStatements[subroutineStmtIdx - 1] !is Subroutine
|
||||||
|
&& outerStatements[subroutineStmtIdx - 1] !is Return
|
||||||
|
&& outerScope !is Block) {
|
||||||
|
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node)
|
||||||
|
}
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
// see if we can remove superfluous typecasts (outside of expressions)
|
||||||
|
// such as casting byte<->ubyte, word<->uword
|
||||||
|
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
||||||
|
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||||
|
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
||||||
|
if(typecast.parent !is Expression) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Note: for various reasons (most importantly, code simplicity), the code generator assumes/requires
|
||||||
|
// that the types of assignment values and their target are the same,
|
||||||
|
// and that the types of both operands of a binaryexpression node are the same.
|
||||||
|
// So, it is not easily possible to remove the typecasts that are there to make these conditions true.
|
||||||
|
// The only place for now where we can do this is for:
|
||||||
|
// asmsub register pair parameter.
|
||||||
|
|
||||||
|
if(typecast.type in WordDatatypes) {
|
||||||
|
val fcall = typecast.parent as? IFunctionCall
|
||||||
|
if (fcall != null) {
|
||||||
|
val sub = fcall.target.targetStatement(program.namespace) as? Subroutine
|
||||||
|
if (sub != null && sub.isAsmSubroutine) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sourceDt in PassByReferenceDatatypes) {
|
||||||
|
if(typecast.type==DataType.UWORD) {
|
||||||
|
if(typecast.expression is IdentifierReference) {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
typecast,
|
||||||
|
AddressOf(typecast.expression as IdentifierReference, typecast.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
val binExpr = ifStatement.condition as? BinaryExpression
|
||||||
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
|
// if x -> if x!=0, if x+5 -> if x+5 != 0
|
||||||
|
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val binExpr = untilLoop.condition as? BinaryExpression
|
||||||
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
|
// until x -> until x!=0, until x+5 -> until x+5 != 0
|
||||||
|
val booleanExpr = BinaryExpression(untilLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val binExpr = whileLoop.condition as? BinaryExpression
|
||||||
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
|
// while x -> while x!=0, while x+5 -> while x+5 != 0
|
||||||
|
val booleanExpr = BinaryExpression(whileLoop.condition, "!=", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,8 @@ data class CompilationOptions(val output: OutputType,
|
|||||||
val launcher: LauncherType,
|
val launcher: LauncherType,
|
||||||
val zeropage: ZeropageType,
|
val zeropage: ZeropageType,
|
||||||
val zpReserved: List<IntRange>,
|
val zpReserved: List<IntRange>,
|
||||||
val floats: Boolean)
|
val floats: Boolean,
|
||||||
|
val noSysInit: Boolean)
|
||||||
|
|
||||||
|
|
||||||
class CompilerException(message: String?) : Exception(message)
|
class CompilerException(message: String?) : Exception(message)
|
||||||
|
@ -4,7 +4,10 @@ import prog8.ast.AstToSourceCode
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
|
import prog8.optimizer.*
|
||||||
import prog8.optimizer.UnusedCodeRemover
|
import prog8.optimizer.UnusedCodeRemover
|
||||||
import prog8.optimizer.constantFold
|
import prog8.optimizer.constantFold
|
||||||
import prog8.optimizer.optimizeStatements
|
import prog8.optimizer.optimizeStatements
|
||||||
@ -13,6 +16,7 @@ import prog8.parser.ModuleImporter
|
|||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import prog8.parser.moduleName
|
import prog8.parser.moduleName
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.system.exitProcess
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
|
||||||
@ -25,12 +29,22 @@ class CompilationResult(val success: Boolean,
|
|||||||
fun compileProgram(filepath: Path,
|
fun compileProgram(filepath: Path,
|
||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
writeAssembly: Boolean,
|
writeAssembly: Boolean,
|
||||||
|
compilationTarget: String,
|
||||||
outputDir: Path): CompilationResult {
|
outputDir: Path): CompilationResult {
|
||||||
var programName = ""
|
var programName = ""
|
||||||
lateinit var programAst: Program
|
lateinit var programAst: Program
|
||||||
lateinit var importedFiles: List<Path>
|
lateinit var importedFiles: List<Path>
|
||||||
val errors = ErrorReporter()
|
val errors = ErrorReporter()
|
||||||
|
|
||||||
|
when(compilationTarget) {
|
||||||
|
C64Target.name -> CompilationTarget.instance = C64Target
|
||||||
|
Cx16Target.name -> CompilationTarget.instance = Cx16Target
|
||||||
|
else -> {
|
||||||
|
System.err.println("invalid compilation target")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
@ -42,7 +56,7 @@ fun compileProgram(filepath: Path,
|
|||||||
optimizeAst(programAst, errors)
|
optimizeAst(programAst, errors)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst) // TODO
|
// printAst(programAst)
|
||||||
|
|
||||||
if(writeAssembly)
|
if(writeAssembly)
|
||||||
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
||||||
@ -78,8 +92,8 @@ fun compileProgram(filepath: Path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
println("Parsing...")
|
println("Compiler target: ${CompilationTarget.instance.name}. Parsing...")
|
||||||
val importer = ModuleImporter(errors)
|
val importer = ModuleImporter()
|
||||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||||
importer.importModule(programAst, filepath)
|
importer.importModule(programAst, filepath)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
@ -90,15 +104,12 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
|
|||||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||||
|
|
||||||
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries
|
// depending on the machine and compiler options we may have to include some libraries
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
|
CompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst)
|
||||||
importer.importLibraryModule(programAst, "c64lib")
|
|
||||||
importer.importLibraryModule(programAst, "c64utils")
|
|
||||||
}
|
|
||||||
|
|
||||||
// always import prog8lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule(programAst, "math")
|
importer.importLibraryModule(programAst, "math")
|
||||||
importer.importLibraryModule(programAst, "prog8lib")
|
importer.importLibraryModule(programAst, "prog8_lib")
|
||||||
errors.handle()
|
errors.handle()
|
||||||
return Triple(programAst, compilerOptions, importedFiles)
|
return Triple(programAst, compilerOptions, importedFiles)
|
||||||
}
|
}
|
||||||
@ -109,13 +120,12 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" }
|
|
||||||
as? Directive)?.args?.single()?.int ?: 0
|
|
||||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||||
val zpType: ZeropageType =
|
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
||||||
|
var zpType: ZeropageType =
|
||||||
if (zpoption == null)
|
if (zpoption == null)
|
||||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||||
else
|
else
|
||||||
@ -125,6 +135,12 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
ZeropageType.KERNALSAFE
|
ZeropageType.KERNALSAFE
|
||||||
// error will be printed by the astchecker
|
// error will be printed by the astchecker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (zpType==ZeropageType.FLOATSAFE && CompilationTarget.instance.name == Cx16Target.name) {
|
||||||
|
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
|
||||||
|
zpType = ZeropageType.BASICSAFE
|
||||||
|
}
|
||||||
|
|
||||||
val zpReserved = mainModule.statements
|
val zpReserved = mainModule.statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||||
@ -132,25 +148,34 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
.map { it[0].int!!..it[1].int!! }
|
.map { it[0].int!!..it[1].int!! }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
|
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
|
||||||
|
System.err.println("invalid output type $outputType")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
|
||||||
|
System.err.println("invalid launcher type $launcherType")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
|
||||||
return CompilationOptions(
|
return CompilationOptions(
|
||||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||||
zpType, zpReserved, floatsEnabled
|
zpType, zpReserved, floatsEnabled, noSysInit
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
// perform initial syntax checks and processings
|
// perform initial syntax checks and processings
|
||||||
println("Processing...")
|
println("Processing for target ${CompilationTarget.instance.name}...")
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.makeForeverLoops()
|
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.reorderStatements(errors)
|
||||||
programAst.reorderStatements()
|
errors.handle()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors)
|
programAst.checkValid(compilerOptions, errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
@ -163,42 +188,45 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = programAst.simplifyExpressions()
|
val optsDone1 = programAst.simplifyExpressions()
|
||||||
val optsDone2 = programAst.optimizeStatements(errors)
|
val optsDone2 = programAst.splitBinaryExpressions()
|
||||||
|
val optsDone3 = programAst.optimizeStatements(errors)
|
||||||
|
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
|
||||||
errors.handle()
|
errors.handle()
|
||||||
if (optsDone1 + optsDone2 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// because simplified statements and expressions could give rise to more constants that can be folded away:
|
|
||||||
programAst.constantFold(errors)
|
|
||||||
errors.handle()
|
|
||||||
|
|
||||||
val remover = UnusedCodeRemover()
|
val remover = UnusedCodeRemover(programAst, errors)
|
||||||
remover.visit(programAst)
|
remover.visit(programAst)
|
||||||
|
remover.applyModifications()
|
||||||
|
errors.handle()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.verifyFunctionArgTypes()
|
||||||
|
programAst.moveMainAndStartToFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
||||||
// asm generation directly from the Ast,
|
// asm generation directly from the Ast,
|
||||||
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
|
||||||
programAst.processAstBeforeAsmGeneration(errors)
|
programAst.processAstBeforeAsmGeneration(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
|
||||||
// printAst(programAst) // TODO
|
// printAst(programAst)
|
||||||
|
|
||||||
val assembly = CompilationTarget.asmGenerator(
|
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
||||||
|
val assembly = CompilationTarget.instance.asmGenerator(
|
||||||
programAst,
|
programAst,
|
||||||
errors,
|
errors,
|
||||||
zeropage,
|
CompilationTarget.instance.machine.zeropage,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
outputDir).compileToAssembly(optimize)
|
outputDir).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
|
@ -8,6 +8,12 @@ class ZeropageDepletedError(message: String) : Exception(message)
|
|||||||
|
|
||||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||||
|
|
||||||
|
abstract val SCRATCH_B1 : Int // temp storage for a single byte
|
||||||
|
abstract val SCRATCH_REG : Int // temp storage for a register
|
||||||
|
abstract val SCRATCH_W1 : Int // temp storage 1 for a word $fb+$fc
|
||||||
|
abstract val SCRATCH_W2 : Int // temp storage 2 for a word $fb+$fc
|
||||||
|
|
||||||
|
|
||||||
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
||||||
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
@ -16,7 +22,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
|
||||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
||||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
||||||
|
|
||||||
if(options.zeropage==ZeropageType.DONTUSE)
|
if(options.zeropage==ZeropageType.DONTUSE)
|
||||||
throw CompilerException("zero page usage has been disabled")
|
throw CompilerException("zero page usage has been disabled")
|
||||||
@ -39,13 +45,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
|
|
||||||
if(free.size > 0) {
|
if(free.size > 0) {
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
|
||||||
if(loneByte(candidate))
|
if(loneByte(candidate))
|
||||||
return makeAllocation(candidate, 1, datatype, scopedname)
|
return makeAllocation(candidate, 1, datatype, scopedname)
|
||||||
}
|
}
|
||||||
return makeAllocation(free[0], 1, datatype, scopedname)
|
return makeAllocation(free[0], 1, datatype, scopedname)
|
||||||
}
|
}
|
||||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
|
||||||
if (sequentialFree(candidate, size))
|
if (sequentialFree(candidate, size))
|
||||||
return makeAllocation(candidate, size, datatype, scopedname)
|
return makeAllocation(candidate, size, datatype, scopedname)
|
||||||
}
|
}
|
||||||
@ -64,12 +70,4 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
|
|
||||||
private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free
|
private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free
|
||||||
private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList())
|
private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList())
|
||||||
|
|
||||||
enum class ExitProgramStrategy {
|
|
||||||
CLEAN_EXIT,
|
|
||||||
SYSTEM_RESET
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract val exitProgramStrategy: ExitProgramStrategy
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.BinaryExpression
|
|
||||||
import prog8.ast.expressions.Expression
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.processing.AstWalker
|
|
||||||
import prog8.ast.processing.IAstModification
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
|
|
||||||
|
|
||||||
class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
if (decl.value == null && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
// a numeric vardecl without an initial value is initialized with zero.
|
|
||||||
decl.value = decl.zeroElementValue()
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
|
||||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
|
||||||
val sub = scope.definingSubroutine()
|
|
||||||
if (sub != null) {
|
|
||||||
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
|
|
||||||
var conflicts = false
|
|
||||||
decls.forEach {
|
|
||||||
val existing = existingVariables[it.name]
|
|
||||||
if (existing != null) {
|
|
||||||
errors.err("variable ${it.name} already defined in subroutine ${sub.name} at ${existing.position}", it.position)
|
|
||||||
conflicts = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!conflicts) {
|
|
||||||
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }
|
|
||||||
return numericVarsWithValue.map {
|
|
||||||
val initValue = it.value!! // assume here that value has always been set by now
|
|
||||||
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
|
||||||
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
|
||||||
val assign = Assignment(target, null, initValue, it.position)
|
|
||||||
initValue.parent = assign
|
|
||||||
IAstModification.InsertFirst(assign, scope)
|
|
||||||
} + decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +
|
|
||||||
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
|
||||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
|
||||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
|
||||||
val mods = mutableListOf<IAstModification>()
|
|
||||||
val returnStmt = Return(null, subroutine.position)
|
|
||||||
if (subroutine.asmAddress == null
|
|
||||||
&& subroutine.statements.isNotEmpty()
|
|
||||||
&& subroutine.amountOfRtsInAsm() == 0
|
|
||||||
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
|
|
||||||
&& subroutine.statements.last() !is Subroutine) {
|
|
||||||
mods += IAstModification.InsertLast(returnStmt, subroutine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
|
|
||||||
val outerScope = subroutine.definingScope()
|
|
||||||
val outerStatements = outerScope.statements
|
|
||||||
val subroutineStmtIdx = outerStatements.indexOf(subroutine)
|
|
||||||
if (subroutineStmtIdx > 0
|
|
||||||
&& outerStatements[subroutineStmtIdx - 1] !is Jump
|
|
||||||
&& outerStatements[subroutineStmtIdx - 1] !is Subroutine
|
|
||||||
&& outerStatements[subroutineStmtIdx - 1] !is Return
|
|
||||||
&& outerScope !is Block) {
|
|
||||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mods
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
|
||||||
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
|
|
||||||
// also to put code generation stuff together, single value assignment (A = 5) is converted to a special
|
|
||||||
// augmented form as wel (with the operator "setvalue")
|
|
||||||
if (assignment.aug_op == null) {
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
|
||||||
if (binExpr != null) {
|
|
||||||
if (assignment.target.isSameAs(binExpr.left)) {
|
|
||||||
assignment.value = binExpr.right
|
|
||||||
assignment.aug_op = binExpr.operator + "="
|
|
||||||
assignment.value.parent = assignment
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assignment.aug_op = "setvalue"
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
|
||||||
// see if we can remove superfluous typecasts (outside of expressions)
|
|
||||||
// such as casting byte<->ubyte, word<->uword
|
|
||||||
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
|
||||||
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
|
||||||
if(typecast.parent !is Expression) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,15 +4,44 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.ErrorReporter
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
internal interface CompilationTarget {
|
internal interface CompilationTarget {
|
||||||
|
val name: String
|
||||||
|
val machine: IMachineDefinition
|
||||||
|
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||||
|
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||||
|
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var name: String
|
lateinit var instance: CompilationTarget
|
||||||
lateinit var machine: IMachineDefinition
|
|
||||||
lateinit var encodeString: (str: String, altEncoding: Boolean) -> List<Short>
|
|
||||||
lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> String
|
|
||||||
lateinit var asmGenerator: (Program, ErrorReporter, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal object C64Target: CompilationTarget {
|
||||||
|
override val name = "c64"
|
||||||
|
override val machine = C64MachineDefinition
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||||
|
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
|
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||||
|
AsmGen(program, errors, zp, options, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object Cx16Target: CompilationTarget {
|
||||||
|
override val name = "cx16"
|
||||||
|
override val machine = CX16MachineDefinition
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||||
|
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
|
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||||
|
AsmGen(program, errors, zp, options, path)
|
||||||
|
}
|
||||||
|
@ -1,15 +1,39 @@
|
|||||||
package prog8.compiler.target
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.parser.ModuleImporter
|
||||||
|
|
||||||
|
|
||||||
interface IMachineDefinition {
|
internal interface IMachineFloat {
|
||||||
|
fun toDouble(): Double
|
||||||
|
fun makeFloatFillAsm(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum class CpuType {
|
||||||
|
CPU6502,
|
||||||
|
CPU65c02
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IMachineDefinition {
|
||||||
val FLOAT_MAX_NEGATIVE: Double
|
val FLOAT_MAX_NEGATIVE: Double
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
val FLOAT_MEM_SIZE: Int
|
val FLOAT_MEM_SIZE: Int
|
||||||
|
val POINTER_MEM_SIZE: Int
|
||||||
|
val ESTACK_LO: Int
|
||||||
|
val ESTACK_HI: Int
|
||||||
|
val BASIC_LOAD_ADDRESS : Int
|
||||||
|
val RAW_LOAD_ADDRESS : Int
|
||||||
|
|
||||||
val opcodeNames: Set<String>
|
val opcodeNames: Set<String>
|
||||||
|
var zeropage: Zeropage
|
||||||
|
val cpu: CpuType
|
||||||
|
|
||||||
fun getZeropage(compilerOptions: CompilationOptions): Zeropage
|
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||||
|
fun getFloat(num: Number): IMachineFloat
|
||||||
|
fun getFloatRomConst(number: Double): String?
|
||||||
|
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program)
|
||||||
|
fun launchEmulator(programName: String)
|
||||||
|
fun isRegularRAMaddress(address: Int): Boolean
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package prog8.compiler.target.c64
|
|||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.OutputType
|
import prog8.compiler.OutputType
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import prog8.compiler.target.generatedLabelPrefix
|
import prog8.compiler.target.generatedLabelPrefix
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -14,20 +15,20 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
|||||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions) {
|
override fun assemble(options: CompilationOptions) {
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
val outFile = when (options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
println("\nCreating C-64 prg.")
|
println("\nCreating prg for target ${CompilationTarget.instance.name}.")
|
||||||
prgFile
|
prgFile
|
||||||
}
|
}
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
command.add("--nostart")
|
command.add("--nostart")
|
||||||
println("\nCreating raw binary.")
|
println("\nCreating raw binary for target ${CompilationTarget.instance.name}.")
|
||||||
binFile
|
binFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,99 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.ast.Program
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.*
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.ZeropageType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
import prog8.compiler.target.IMachineDefinition
|
||||||
import java.awt.Color
|
import prog8.compiler.target.IMachineFloat
|
||||||
import java.awt.image.BufferedImage
|
import prog8.parser.ModuleImporter
|
||||||
import javax.imageio.ImageIO
|
import java.io.IOException
|
||||||
|
import java.math.RoundingMode
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
object C64MachineDefinition: IMachineDefinition {
|
internal object C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
// 5-byte cbm MFLPT format limitations:
|
// 5-byte cbm MFLPT format limitations:
|
||||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
override val FLOAT_MEM_SIZE = 5
|
override val FLOAT_MEM_SIZE = 5
|
||||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
override val POINTER_MEM_SIZE = 2
|
||||||
const val RAW_LOAD_ADDRESS = 0xc000
|
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||||
|
override val RAW_LOAD_ADDRESS = 0xc000
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
// and some heavily used string constants derived from the two values above
|
// and some heavily used string constants derived from the two values above
|
||||||
const val ESTACK_LO_VALUE = 0xce00 // $ce00-$ceff inclusive
|
override val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
||||||
const val ESTACK_HI_VALUE = 0xcf00 // $cf00-$cfff inclusive
|
override val ESTACK_HI = 0xcf00 // $ce00-$ceff inclusive
|
||||||
const val ESTACK_LO_HEX = "\$ce00"
|
|
||||||
const val ESTACK_LO_PLUS1_HEX = "\$ce01"
|
|
||||||
const val ESTACK_LO_PLUS2_HEX = "\$ce02"
|
|
||||||
const val ESTACK_HI_HEX = "\$cf00"
|
|
||||||
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
|
|
||||||
const val ESTACK_HI_PLUS2_HEX = "\$cf02"
|
|
||||||
|
|
||||||
override fun getZeropage(compilerOptions: CompilationOptions) = C64Zeropage(compilerOptions)
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
|
override fun getFloatRomConst(number: Double): String? {
|
||||||
|
// try to match the ROM float constants to save memory
|
||||||
|
val mflpt5 = Mflpt5.fromNumber(number)
|
||||||
|
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
||||||
|
when {
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_ZERO_const" // not a ROM const
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_ONE_const" // not a ROM const
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "floats.FL_PIVAL"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_N32768"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FONE"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "floats.FL_SQRHLF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "floats.FL_SQRTWO"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "floats.FL_NEGHLF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "floats.FL_LOG2"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "floats.FL_TENC"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "floats.FL_NZMIL"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FHALF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "floats.FL_LOGEB2"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "floats.FL_PIHALF"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "floats.FL_TWOPI"
|
||||||
|
floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "floats.FL_FR4"
|
||||||
|
else -> {
|
||||||
|
// attempt to correct for a few rounding issues
|
||||||
|
when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
|
||||||
|
3.1415926536 -> return "floats.FL_PIVAL"
|
||||||
|
1.4142135624 -> return "floats.FL_SQRTWO"
|
||||||
|
0.7071067812 -> return "floats.FL_SQRHLF"
|
||||||
|
0.6931471806 -> return "floats.FL_LOG2"
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||||
|
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
importer.importLibraryModule(program, "syslib")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(programName: String) {
|
||||||
|
for(emulator in listOf("x64sc", "x64")) {
|
||||||
|
println("\nStarting C-64 emulator $emulator...")
|
||||||
|
val cmdline = listOf(emulator, "-silent", "-moncommands", "$programName.vice-mon-list",
|
||||||
|
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".prg")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(x: IOException) {
|
||||||
|
continue // try the next emulator executable
|
||||||
|
}
|
||||||
|
process.waitFor()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isRegularRAMaddress(address: Int): Boolean = address<0xa000 || address in 0xc000..0xcfff
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = C64Zeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||||
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
||||||
@ -45,20 +107,12 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
||||||
|
|
||||||
|
|
||||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
internal class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
companion object {
|
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
||||||
const val SCRATCH_B1 = 0x02
|
override val SCRATCH_REG = 0x03 // temp storage for a register
|
||||||
const val SCRATCH_REG = 0x03 // temp storage for a register
|
override val SCRATCH_W1 = 0xfb // temp storage 1 for a word $fb+$fc
|
||||||
const val SCRATCH_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
|
||||||
const val SCRATCH_W1 = 0xfb // $fb+$fc
|
|
||||||
const val SCRATCH_W2 = 0xfd // $fd+$fe
|
|
||||||
}
|
|
||||||
|
|
||||||
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
|
|
||||||
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
|
|
||||||
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -68,7 +122,7 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
if (options.zeropage == ZeropageType.FULL) {
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
free.addAll(0x04..0xf9)
|
free.addAll(0x04..0xf9)
|
||||||
free.add(0xff)
|
free.add(0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
@ -90,7 +144,7 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
// remove the zero page locations used for floating point operations from the free list
|
// remove the zero page locations used for floating point operations from the free list
|
||||||
free.removeAll(listOf(
|
free.removeAll(listOf(
|
||||||
0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
@ -109,19 +163,17 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
free.clear()
|
free.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(SCRATCH_B1 !in free)
|
require(SCRATCH_B1 !in free)
|
||||||
assert(SCRATCH_REG !in free)
|
require(SCRATCH_REG !in free)
|
||||||
assert(SCRATCH_REG_X !in free)
|
require(SCRATCH_W1 !in free)
|
||||||
assert(SCRATCH_W1 !in free)
|
require(SCRATCH_W2 !in free)
|
||||||
assert(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
for (reserved in options.zpReserved)
|
||||||
reserve(reserved)
|
reserve(reserved)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short): IMachineFloat {
|
||||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
@ -168,7 +220,7 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toDouble(): Double {
|
override fun toDouble(): Double {
|
||||||
if (this == zero) return 0.0
|
if (this == zero) return 0.0
|
||||||
val exp = b0 - 128
|
val exp = b0 - 128
|
||||||
val sign = (b1.toInt() and 0x80) > 0
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
@ -176,91 +228,14 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||||
return if (sign) -result else result
|
return if (sign) -result else result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
object Charset {
|
override fun makeFloatFillAsm(): String {
|
||||||
private val normalImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-normal.png"))
|
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||||
private val shiftedImg = ImageIO.read(javaClass.getResource("/charset/c64/charset-shifted.png"))
|
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||||
|
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||||
private fun scanChars(img: BufferedImage): Array<BufferedImage> {
|
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||||
|
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||||
val transparent = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
|
return "$b0, $b1, $b2, $b3, $b4"
|
||||||
transparent.createGraphics().drawImage(img, 0, 0, null)
|
|
||||||
|
|
||||||
val black = Color(0, 0, 0).rgb
|
|
||||||
val nopixel = Color(0, 0, 0, 0).rgb
|
|
||||||
for (y in 0 until transparent.height) {
|
|
||||||
for (x in 0 until transparent.width) {
|
|
||||||
val col = transparent.getRGB(x, y)
|
|
||||||
if (col == black)
|
|
||||||
transparent.setRGB(x, y, nopixel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val numColumns = transparent.width / 8
|
|
||||||
val charImages = (0..255).map {
|
|
||||||
val charX = it % numColumns
|
|
||||||
val charY = it / numColumns
|
|
||||||
transparent.getSubimage(charX * 8, charY * 8, 8, 8)
|
|
||||||
}
|
|
||||||
return charImages.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
val normalChars = scanChars(normalImg)
|
|
||||||
val shiftedChars = scanChars(shiftedImg)
|
|
||||||
|
|
||||||
private val coloredNormalChars = mutableMapOf<Short, Array<BufferedImage>>()
|
|
||||||
|
|
||||||
fun getColoredChar(screenCode: Short, color: Short): BufferedImage {
|
|
||||||
val colorIdx = (color % colorPalette.size).toShort()
|
|
||||||
val chars = coloredNormalChars[colorIdx]
|
|
||||||
if (chars != null)
|
|
||||||
return chars[screenCode.toInt()]
|
|
||||||
|
|
||||||
val coloredChars = mutableListOf<BufferedImage>()
|
|
||||||
val transparent = Color(0, 0, 0, 0).rgb
|
|
||||||
val rgb = colorPalette[colorIdx.toInt()].rgb
|
|
||||||
for (c in normalChars) {
|
|
||||||
val colored = c.copy()
|
|
||||||
for (y in 0 until colored.height)
|
|
||||||
for (x in 0 until colored.width) {
|
|
||||||
if (colored.getRGB(x, y) != transparent) {
|
|
||||||
colored.setRGB(x, y, rgb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coloredChars.add(colored)
|
|
||||||
}
|
|
||||||
coloredNormalChars[colorIdx] = coloredChars.toTypedArray()
|
|
||||||
return coloredNormalChars.getValue(colorIdx)[screenCode.toInt()]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BufferedImage.copy(): BufferedImage {
|
|
||||||
val bcopy = BufferedImage(this.width, this.height, this.type)
|
|
||||||
val g = bcopy.graphics
|
|
||||||
g.drawImage(this, 0, 0, null)
|
|
||||||
g.dispose()
|
|
||||||
return bcopy
|
|
||||||
}
|
|
||||||
|
|
||||||
val colorPalette = listOf( // this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
|
||||||
Color(0x000000), // 0 = black
|
|
||||||
Color(0xFFFFFF), // 1 = white
|
|
||||||
Color(0x813338), // 2 = red
|
|
||||||
Color(0x75cec8), // 3 = cyan
|
|
||||||
Color(0x8e3c97), // 4 = purple
|
|
||||||
Color(0x56ac4d), // 5 = green
|
|
||||||
Color(0x2e2c9b), // 6 = blue
|
|
||||||
Color(0xedf171), // 7 = yellow
|
|
||||||
Color(0x8e5029), // 8 = orange
|
|
||||||
Color(0x553800), // 9 = brown
|
|
||||||
Color(0xc46c71), // 10 = light red
|
|
||||||
Color(0x4a4a4a), // 11 = dark grey
|
|
||||||
Color(0x7b7b7b), // 12 = medium grey
|
|
||||||
Color(0xa9ff9f), // 13 = light green
|
|
||||||
Color(0x706deb), // 14 = light blue
|
|
||||||
Color(0xb2b2b2) // 15 = light grey
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1054,14 +1054,19 @@ object Petscii {
|
|||||||
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
||||||
return text.map {
|
return text.map {
|
||||||
val petscii = lookup[it]
|
val petscii = lookup[it]
|
||||||
petscii?.toShort() ?: if(it=='\u0000')
|
petscii?.toShort() ?: when (it) {
|
||||||
0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
else {
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(it.toInt() - 0x8000).toShort()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})")
|
throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
||||||
val decodeTable = if(lowercase) decodingPetsciiLowercase else decodingPetsciiUppercase
|
val decodeTable = if(lowercase) decodingPetsciiLowercase else decodingPetsciiUppercase
|
||||||
@ -1072,14 +1077,19 @@ object Petscii {
|
|||||||
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
||||||
return text.map{
|
return text.map{
|
||||||
val screencode = lookup[it]
|
val screencode = lookup[it]
|
||||||
screencode?.toShort() ?: if(it=='\u0000')
|
screencode?.toShort() ?: when (it) {
|
||||||
0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
else {
|
in '\u8000'..'\u80ff' -> {
|
||||||
|
// special case: take the lower 8 bit hex value directly
|
||||||
|
(it.toInt() - 0x8000).toShort()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
val case = if (lowercase) "lower" else "upper"
|
||||||
throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})")
|
throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
||||||
val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
|
val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,5 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||||
|
|
||||||
@ -87,10 +84,10 @@ private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>):
|
|||||||
// the repeated lda can be removed
|
// the repeated lda can be removed
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
for(lines in linesByFour) {
|
||||||
if(lines[0].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x" &&
|
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||||
lines[1].value.trim().startsWith("cmp ") &&
|
lines[1].value.trim().startsWith("cmp ") &&
|
||||||
lines[2].value.trim().startsWith("beq ") &&
|
lines[2].value.trim().startsWith("beq ") &&
|
||||||
lines[3].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x") {
|
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,10 +99,10 @@ private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<S
|
|||||||
// this is a lot harder for word values because the instruction sequence varies.
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
for(lines in linesByFour) {
|
||||||
if(lines[0].value.trim()=="sta $ESTACK_LO_HEX,x" &&
|
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||||
lines[1].value.trim()=="dex" &&
|
lines[1].value.trim()=="dex" &&
|
||||||
lines[2].value.trim()=="inx" &&
|
lines[2].value.trim()=="inx" &&
|
||||||
lines[3].value.trim()=="lda $ESTACK_LO_HEX,x") {
|
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||||
mods.add(Modification(lines[1].index, true, null))
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
mods.add(Modification(lines[3].index, true, null))
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
@ -154,7 +151,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||||
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("jsr c64flt.copy_float")) {
|
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||||
|
(seventh.startsWith("jsr floats.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
val nineth = pair[8].value.trimStart()
|
val nineth = pair[8].value.trimStart()
|
||||||
val tenth = pair[9].value.trimStart()
|
val tenth = pair[9].value.trimStart()
|
||||||
@ -164,7 +162,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
val fourteenth = pair[13].value.trimStart()
|
val fourteenth = pair[13].value.trimStart()
|
||||||
|
|
||||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") && fourteenth.startsWith("jsr c64flt.copy_float")) {
|
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||||
|
(fourteenth.startsWith("jsr floats.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||||
|
|
||||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
// identical float init
|
// identical float init
|
||||||
@ -180,6 +179,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// TODO not sure if this is correct in all situations....:
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByFour) {
|
for (pair in linesByFour) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,19 +2,16 @@ package prog8.compiler.target.c64.codegen
|
|||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ByteDatatypes
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.FSignature
|
import prog8.functions.FSignature
|
||||||
|
|
||||||
@ -39,6 +36,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
when (functionName) {
|
when (functionName) {
|
||||||
"msb" -> funcMsb(fcall)
|
"msb" -> funcMsb(fcall)
|
||||||
|
"lsb" -> funcLsb(fcall)
|
||||||
"mkword" -> funcMkword(fcall, func)
|
"mkword" -> funcMkword(fcall, func)
|
||||||
"abs" -> funcAbs(fcall, func)
|
"abs" -> funcAbs(fcall, func)
|
||||||
"swap" -> funcSwap(fcall)
|
"swap" -> funcSwap(fcall)
|
||||||
@ -50,8 +48,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"ln", "log2", "sqrt", "rad",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
||||||
"lsl" -> funcLsl(fcall)
|
|
||||||
"lsr" -> funcLsr(fcall)
|
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
"ror" -> funcRor(fcall)
|
"ror" -> funcRor(fcall)
|
||||||
@ -61,7 +57,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"rsave" -> {
|
"rsave" -> {
|
||||||
// save cpu status flag and all registers A, X, Y.
|
// save cpu status flag and all registers A, X, Y.
|
||||||
// see http://6502.org/tutorials/register_preservation.html
|
// see http://6502.org/tutorials/register_preservation.html
|
||||||
asmgen.out(" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}")
|
asmgen.out(" php | sta P8ZP_SCRATCH_REG | pha | txa | pha | tya | pha | lda P8ZP_SCRATCH_REG")
|
||||||
}
|
}
|
||||||
"rrestore" -> {
|
"rrestore" -> {
|
||||||
// restore all registers and cpu status flag
|
// restore all registers and cpu status flag
|
||||||
@ -82,15 +78,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.size()
|
val numElements = decl.arraysize!!.constIndex()
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_b
|
jsr prog8_lib.reverse_b
|
||||||
""")
|
""")
|
||||||
@ -99,8 +95,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_w
|
jsr prog8_lib.reverse_w
|
||||||
""")
|
""")
|
||||||
@ -109,8 +105,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_f
|
jsr prog8_lib.reverse_f
|
||||||
""")
|
""")
|
||||||
@ -124,17 +120,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.size()
|
val numElements = decl.arraysize!!.constIndex()
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
sta ${C64Zeropage.SCRATCH_B1}
|
sta P8ZP_SCRATCH_B1
|
||||||
""")
|
""")
|
||||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
||||||
}
|
}
|
||||||
@ -142,10 +138,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
sta ${C64Zeropage.SCRATCH_B1}
|
sta P8ZP_SCRATCH_B1
|
||||||
""")
|
""")
|
||||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||||
}
|
}
|
||||||
@ -176,15 +172,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
|
|
||||||
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
|
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -198,7 +187,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -227,23 +216,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ ror ${'$'}ffff ; modified
|
+ ror ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" ror a")
|
|
||||||
Register.X -> asmgen.out(" txa | ror a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | ror a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable")
|
asmgen.out(" ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -257,7 +239,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable+1 | ror $variable")
|
asmgen.out(" ror $variable+1 | ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -287,15 +269,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
|
|
||||||
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -309,7 +284,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -338,23 +313,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ rol ${'$'}ffff ; modified
|
+ rol ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" rol a")
|
|
||||||
Register.X -> asmgen.out(" txa | rol a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable")
|
asmgen.out(" rol $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -368,7 +336,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable | rol $variable+1")
|
asmgen.out(" rol $variable | rol $variable+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -378,149 +346,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcLsr(fcall: IFunctionCall) {
|
|
||||||
val what = fcall.args.single()
|
|
||||||
val dt = what.inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when (what) {
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" lsr a")
|
|
||||||
Register.X -> asmgen.out(" txa | lsr a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
|
||||||
asmgen.out(" lsr ${number.toHex()}")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(what.addressExpression)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
sta (+) + 1
|
|
||||||
lda $ESTACK_HI_HEX,x
|
|
||||||
sta (+) + 2
|
|
||||||
+ lsr ${'$'}ffff ; modified
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsr_array_ub")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
when (what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsr_array_b")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lda $variable | asl a | ror $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when (what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsr_array_uw")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lsr $variable+1 | ror $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
when (what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsr_array_w")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lda $variable+1 | asl a | ror $variable+1 | ror $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcLsl(fcall: IFunctionCall) {
|
|
||||||
val what = fcall.args.single()
|
|
||||||
val dt = what.inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
when (what) {
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when (what.register) {
|
|
||||||
Register.A -> asmgen.out(" asl a")
|
|
||||||
Register.X -> asmgen.out(" txa | asl a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | asl a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
|
||||||
asmgen.out(" asl ${number.toHex()}")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(what.addressExpression)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
sta (+) + 1
|
|
||||||
lda $ESTACK_HI_HEX,x
|
|
||||||
sta (+) + 2
|
|
||||||
+ asl ${'$'}ffff ; modified
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsl_array_b")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
when (what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.lsl_array_w")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" asl $variable | rol $variable+1")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr c64flt.func_$functionName")
|
asmgen.out(" jsr floats.func_$functionName")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
|
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
|
||||||
@ -531,7 +359,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
||||||
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
||||||
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.sign_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,7 +370,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -555,26 +383,257 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcStrlen(fcall: IFunctionCall) {
|
private fun funcStrlen(fcall: IFunctionCall) {
|
||||||
outputPushAddressOfIdentifier(fcall.args[0])
|
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||||
asmgen.out(" jsr prog8_lib.func_strlen")
|
val type = fcall.args[0].inferType(program)
|
||||||
|
when {
|
||||||
|
type.istype(DataType.STR) -> asmgen.out("""
|
||||||
|
lda #<$name
|
||||||
|
ldy #>$name
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
type.istype(DataType.UWORD) -> asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
else -> throw AssemblyError("strlen requires str or uword arg")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSwap(fcall: IFunctionCall) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
val first = fcall.args[0]
|
val first = fcall.args[0]
|
||||||
val second = fcall.args[1]
|
val second = fcall.args[1]
|
||||||
|
|
||||||
|
// optimized simple case: swap two variables
|
||||||
|
if(first is IdentifierReference && second is IdentifierReference) {
|
||||||
|
val firstName = asmgen.asmVariableName(first)
|
||||||
|
val secondName = asmgen.asmVariableName(second)
|
||||||
|
val dt = first.inferType(program)
|
||||||
|
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
||||||
|
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $firstName
|
||||||
|
lda $secondName
|
||||||
|
sta $firstName
|
||||||
|
sty $secondName
|
||||||
|
ldy $firstName+1
|
||||||
|
lda $secondName+1
|
||||||
|
sta $firstName+1
|
||||||
|
sty $secondName+1
|
||||||
|
""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(dt.istype(DataType.FLOAT)) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$firstName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>$firstName
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<$secondName
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$secondName
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optimized simple case: swap two memory locations
|
||||||
|
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||||
|
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||||
|
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||||
|
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
||||||
|
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
||||||
|
|
||||||
|
when {
|
||||||
|
addr1!=null && addr2!=null -> {
|
||||||
|
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr1!=null && name2!=null -> {
|
||||||
|
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name1!=null && addr2 != null -> {
|
||||||
|
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name1!=null && name2!=null -> {
|
||||||
|
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
||||||
|
val indexValue1 = first.arrayspec.index as? NumericLiteralValue
|
||||||
|
val indexName1 = first.arrayspec.index as? IdentifierReference
|
||||||
|
val indexValue2 = second.arrayspec.index as? NumericLiteralValue
|
||||||
|
val indexName2 = second.arrayspec.index as? IdentifierReference
|
||||||
|
val arrayVarName1 = asmgen.asmVariableName(first.identifier)
|
||||||
|
val arrayVarName2 = asmgen.asmVariableName(second.identifier)
|
||||||
|
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
|
if(indexValue1!=null && indexValue2!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, indexValue1, arrayVarName2, indexValue2)
|
||||||
|
return
|
||||||
|
} else if(indexName1!=null && indexName2!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, indexName1, arrayVarName2, indexName2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all other types of swap() calls are done via the evaluation stack
|
||||||
|
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||||
|
return when (expr) {
|
||||||
|
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variableAsmName = asmgen.asmVariableName(expr))
|
||||||
|
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine(), array = expr)
|
||||||
|
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine(), memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
||||||
|
else -> throw AssemblyError("invalid expression object $expr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
asmgen.translateExpression(first)
|
asmgen.translateExpression(first)
|
||||||
asmgen.translateExpression(second)
|
asmgen.translateExpression(second)
|
||||||
// pop in reverse order
|
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val firstTarget = AssignTarget.fromExpr(first)
|
val assignFirst = AsmAssignment(
|
||||||
val secondTarget = AssignTarget.fromExpr(second)
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
|
||||||
asmgen.assignFromEvalResult(firstTarget)
|
targetFromExpr(first, datatype),
|
||||||
asmgen.assignFromEvalResult(secondTarget)
|
false, first.position
|
||||||
|
)
|
||||||
|
val assignSecond = AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
|
||||||
|
targetFromExpr(second, datatype),
|
||||||
|
false, second.position
|
||||||
|
)
|
||||||
|
asmgen.translateNormalAssignment(assignFirst)
|
||||||
|
asmgen.translateNormalAssignment(assignSecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||||
|
val index1 = indexValue1.number.toInt() * elementDt.memorySize()
|
||||||
|
val index2 = indexValue2.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1+$index1
|
||||||
|
ldy $arrayVarName2+$index2
|
||||||
|
sta $arrayVarName2+$index2
|
||||||
|
sty $arrayVarName1+$index1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1+$index1
|
||||||
|
ldy $arrayVarName2+$index2
|
||||||
|
sta $arrayVarName2+$index2
|
||||||
|
sty $arrayVarName1+$index1
|
||||||
|
lda $arrayVarName1+$index1+1
|
||||||
|
ldy $arrayVarName2+$index2+1
|
||||||
|
sta $arrayVarName2+$index2+1
|
||||||
|
sty $arrayVarName1+$index1+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||||
|
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||||
|
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
ldx $idxAsmName1
|
||||||
|
ldy $idxAsmName2
|
||||||
|
lda $arrayVarName1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda $arrayVarName1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
lda $arrayVarName1+1,x
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2+1,y
|
||||||
|
sta $arrayVarName1+1,x
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2+1,y
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #>$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName1
|
||||||
|
adc #<$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W1+1
|
||||||
|
+ lda #>$arrayVarName2
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName2
|
||||||
|
adc #<$arrayVarName2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W2+1
|
||||||
|
+ jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
@ -583,14 +642,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
// trick: push the args in reverse order (msb first, lsb second) this saves some instructions
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
asmgen.translateExpression(fcall.args[1])
|
||||||
|
asmgen.translateExpression(fcall.args[0])
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta P8ESTACK_HI+1,x")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMsb(fcall: IFunctionCall) {
|
private fun funcMsb(fcall: IFunctionCall) {
|
||||||
@ -598,39 +659,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("should have been const-folded")
|
throw AssemblyError("msb(const) should have been const-folded away")
|
||||||
if (arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(arg)
|
asmgen.translateExpression(arg)
|
||||||
asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" lda P8ESTACK_HI+1,x | sta P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcLsb(fcall: IFunctionCall) {
|
||||||
|
val arg = fcall.args.single()
|
||||||
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
|
throw AssemblyError("lsb required word argument")
|
||||||
|
if (arg is NumericLiteralValue)
|
||||||
|
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||||
|
if (arg is IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(arg)
|
||||||
|
// just ignore any high-byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
||||||
arg as IdentifierReference
|
arg as IdentifierReference
|
||||||
val identifierName = asmgen.asmIdentifierName(arg)
|
val identifierName = asmgen.asmVariableName(arg)
|
||||||
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.size()!!
|
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.constIndex()!!
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$identifierName
|
lda #<$identifierName
|
||||||
sta $ESTACK_LO_HEX,x
|
sta P8ESTACK_LO,x
|
||||||
lda #>$identifierName
|
lda #>$identifierName
|
||||||
sta $ESTACK_HI_HEX,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
lda #$size
|
lda #$size
|
||||||
sta $ESTACK_LO_HEX,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun outputPushAddressOfIdentifier(arg: Expression) {
|
|
||||||
val identifierName = asmgen.asmIdentifierName(arg as IdentifierReference)
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$identifierName
|
|
||||||
sta $ESTACK_LO_HEX,x
|
|
||||||
lda #>$identifierName
|
|
||||||
sta $ESTACK_HI_HEX,x
|
|
||||||
dex
|
dex
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,23 +2,17 @@ package prog8.compiler.target.c64.codegen
|
|||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpr
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
// todo choose more efficient comparisons to avoid needless lda's
|
|
||||||
// todo optimize common case step == 2 / -2
|
|
||||||
|
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
internal fun translate(stmt: ForLoop) {
|
internal fun translate(stmt: ForLoop) {
|
||||||
@ -37,118 +31,68 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable}")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
val continueLabel = asmgen.makeLabel("for_continue")
|
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||||
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
asmgen.loopContinueLabels.push(continueLabel)
|
|
||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
if (stepsize==1 || stepsize==-1) {
|
if (stepsize==1 || stepsize==-1) {
|
||||||
|
|
||||||
// bytes, step 1 or -1
|
// bytes array, step 1 or -1
|
||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
if (stmt.loopRegister != null) {
|
|
||||||
// loop register over range
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda ${ESTACK_LO_HEX},x
|
|
||||||
sta $loopLabel+1
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel lda $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
beq $endLabel
|
|
||||||
$incdec $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$endLabel inx""")
|
|
||||||
} else {
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO_HEX},x
|
lda P8ESTACK_LO,x
|
||||||
sta $varname
|
sta $varname
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
sta $modifiedLabel+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel lda $varname
|
lda $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
$incdec $varname
|
$incdec $varname
|
||||||
jmp $loopLabel
|
jmp $loopLabel
|
||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
}
|
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
|
|
||||||
// bytes, step >= 2 or <= -2
|
// bytes, step >= 2 or <= -2
|
||||||
|
|
||||||
if (stmt.loopRegister != null) {
|
|
||||||
// loop register over range
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.translateExpression(range.to)
|
|
||||||
asmgen.translateExpression(range.from)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda ${ESTACK_LO_HEX},x
|
|
||||||
sta $loopLabel+1
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel lda $loopLabel+1""")
|
|
||||||
if(stepsize>0) {
|
|
||||||
asmgen.out("""
|
|
||||||
clc
|
|
||||||
adc #$stepsize
|
|
||||||
sta $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
bcc $loopLabel
|
|
||||||
beq $loopLabel""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
sec
|
|
||||||
sbc #${stepsize.absoluteValue}
|
|
||||||
sta $loopLabel+1
|
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
bcs $loopLabel""")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
$endLabel inx""")
|
|
||||||
} else {
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda ${ESTACK_LO_HEX},x
|
lda P8ESTACK_LO,x
|
||||||
sta $varname
|
sta $varname
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
sta $modifiedLabel+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel lda $varname""")
|
lda $varname""")
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
clc
|
clc
|
||||||
adc #$stepsize
|
adc #$stepsize
|
||||||
sta $varname
|
sta $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
bcc $loopLabel
|
bcc $loopLabel
|
||||||
beq $loopLabel""")
|
beq $loopLabel""")
|
||||||
} else {
|
} else {
|
||||||
@ -156,14 +100,13 @@ $continueLabel lda $varname""")
|
|||||||
sec
|
sec
|
||||||
sbc #${stepsize.absoluteValue}
|
sbc #${stepsize.absoluteValue}
|
||||||
sta $varname
|
sta $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
bcs $loopLabel""")
|
bcs $loopLabel""")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
when {
|
when {
|
||||||
|
|
||||||
@ -171,47 +114,52 @@ $endLabel inx""")
|
|||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
assignLoopvar(stmt, range)
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
null, range.from, range.position)
|
asmgen.out("""
|
||||||
assignLoopvar.linkParents(stmt)
|
lda P8ESTACK_HI+1,x
|
||||||
asmgen.translate(assignLoopvar)
|
sta $modifiedLabel+1
|
||||||
asmgen.out(loopLabel)
|
lda P8ESTACK_LO+1,x
|
||||||
|
sta $modifiedLabel2+1
|
||||||
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
cmp $ESTACK_HI_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel""")
|
||||||
if(stepsize==1) {
|
if(stepsize==1) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne +
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1
|
||||||
|
jmp $loopLabel
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname""")
|
+ dec $varname
|
||||||
|
jmp $loopLabel""")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out(endLabel)
|
||||||
+ jmp $loopLabel
|
asmgen.out(" inx")
|
||||||
$endLabel inx""")
|
|
||||||
}
|
}
|
||||||
stepsize > 0 -> {
|
stepsize > 0 -> {
|
||||||
|
|
||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
|
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
asmgen.out("""
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
lda P8ESTACK_HI+1,x
|
||||||
null, range.from, range.position)
|
sta $modifiedLabel+1
|
||||||
assignLoopvar.linkParents(stmt)
|
lda P8ESTACK_LO+1,x
|
||||||
asmgen.translate(assignLoopvar)
|
sta $modifiedLabel2+1
|
||||||
|
""")
|
||||||
|
assignLoopvar(stmt, range)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
|
||||||
@ -224,12 +172,11 @@ $endLabel inx""")
|
|||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>$stepsize
|
adc #>$stepsize
|
||||||
sta $varname+1
|
sta $varname+1
|
||||||
lda $ESTACK_HI_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
cmp $varname+1
|
bcc $loopLabel
|
||||||
bcc $endLabel
|
bne $endLabel
|
||||||
bne $loopLabel
|
$modifiedLabel2 lda #0 ; modified
|
||||||
lda $varname
|
cmp $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bcs $loopLabel
|
bcs $loopLabel
|
||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
@ -242,9 +189,9 @@ $endLabel inx""")
|
|||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>$stepsize
|
adc #>$stepsize
|
||||||
sta $varname+1
|
sta $varname+1
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel2 lda #0 ; modified
|
||||||
cmp $varname
|
cmp $varname
|
||||||
lda $ESTACK_HI_PLUS1_HEX,x
|
$modifiedLabel lda #0 ; modified
|
||||||
sbc $varname+1
|
sbc $varname+1
|
||||||
bvc +
|
bvc +
|
||||||
eor #$80
|
eor #$80
|
||||||
@ -256,11 +203,14 @@ $endLabel inx""")
|
|||||||
|
|
||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
asmgen.out("""
|
||||||
val assignLoopvar = Assignment(AssignTarget(null, stmt.loopVar, null, null, stmt.loopVar!!.position),
|
lda P8ESTACK_HI+1,x
|
||||||
null, range.from, range.position)
|
sta $modifiedLabel+1
|
||||||
assignLoopvar.linkParents(stmt)
|
lda P8ESTACK_LO+1,x
|
||||||
asmgen.translate(assignLoopvar)
|
sta $modifiedLabel2+1
|
||||||
|
""")
|
||||||
|
assignLoopvar(stmt, range)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
|
||||||
@ -273,11 +223,11 @@ $endLabel inx""")
|
|||||||
lda $varname+1
|
lda $varname+1
|
||||||
sbc #>${stepsize.absoluteValue}
|
sbc #>${stepsize.absoluteValue}
|
||||||
sta $varname+1
|
sta $varname+1
|
||||||
cmp $ESTACK_HI_PLUS1_HEX,x
|
$modifiedLabel cmp #0 ; modified
|
||||||
bcc $endLabel
|
bcc $endLabel
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
bcs $loopLabel
|
bcs $loopLabel
|
||||||
$endLabel inx""")
|
$endLabel inx""")
|
||||||
} else {
|
} else {
|
||||||
@ -291,9 +241,9 @@ $endLabel inx""")
|
|||||||
sbc #>${stepsize.absoluteValue}
|
sbc #>${stepsize.absoluteValue}
|
||||||
sta $varname+1
|
sta $varname+1
|
||||||
pla
|
pla
|
||||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
$modifiedLabel2 cmp #0 ; modified
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
sbc $ESTACK_HI_PLUS1_HEX,x
|
$modifiedLabel sbc #0 ; modified
|
||||||
bvc +
|
bvc +
|
||||||
eor #$80
|
eor #$80
|
||||||
+ bpl $loopLabel
|
+ bpl $loopLabel
|
||||||
@ -306,99 +256,104 @@ $endLabel inx""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
asmgen.loopContinueLabels.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
val continueLabel = asmgen.makeLabel("for_continue")
|
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
asmgen.loopContinueLabels.push(continueLabel)
|
val iterableName = asmgen.asmVariableName(ident)
|
||||||
val iterableName = asmgen.asmIdentifierName(ident)
|
|
||||||
val decl = ident.targetVarDecl(program.namespace)!!
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$iterableName
|
lda #<$iterableName
|
||||||
ldy #>$iterableName
|
ldy #>$iterableName
|
||||||
sta $loopLabel+1
|
sta $loopLabel+1
|
||||||
sty $loopLabel+2
|
sty $loopLabel+2
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
beq $endLabel""")
|
beq $endLabel
|
||||||
if(stmt.loopVar!=null)
|
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel inc $loopLabel+1
|
inc $loopLabel+1
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $loopLabel+2
|
inc $loopLabel+2
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
val length = decl.arraysize!!.constIndex()!!
|
||||||
val length = decl.arraysize!!.size()!!
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
if(stmt.loopRegister!=null && stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$iterableName
|
|
||||||
ldy #>$iterableName
|
|
||||||
sta $modifiedLabel+1
|
|
||||||
sty $modifiedLabel+2
|
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $counterLabel
|
$loopLabel sty $indexVar
|
||||||
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
lda $iterableName,y
|
||||||
if(stmt.loopVar!=null)
|
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar!!)}")
|
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
if(length<=255) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel ldy $counterLabel
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
cpy #${length and 255}
|
cpy #$length
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
jmp $loopLabel
|
bne $loopLabel""")
|
||||||
$counterLabel .byte 0
|
} else {
|
||||||
$endLabel""")
|
// length is 256
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
bne $loopLabel
|
||||||
|
beq $endLabel""")
|
||||||
|
}
|
||||||
|
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||||
|
// allocate index var on ZP
|
||||||
|
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||||
|
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
$indexVar .byte 0""")
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter in such cases
|
val length = decl.arraysize!!.constIndex()!! * 2
|
||||||
val length = decl.arraysize!!.size()!! * 2
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
if(stmt.loopRegister!=null)
|
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||||
throw AssemblyError("can't use register to loop over words")
|
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
|
||||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
|
||||||
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
|
||||||
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar!!)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$iterableName
|
|
||||||
ldy #>$iterableName
|
|
||||||
sta $modifiedLabel+1
|
|
||||||
sty $modifiedLabel+2
|
|
||||||
lda #<$iterableName+1
|
|
||||||
ldy #>$iterableName+1
|
|
||||||
sta $modifiedLabel2+1
|
|
||||||
sty $modifiedLabel2+2
|
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $counterLabel
|
$loopLabel sty $indexVar
|
||||||
$modifiedLabel lda ${65535.toHex()},y ; modified
|
lda $iterableName,y
|
||||||
sta $loopvarName
|
sta $loopvarName
|
||||||
$modifiedLabel2 lda ${65535.toHex()},y ; modified
|
lda $iterableName+1,y
|
||||||
sta $loopvarName+1""")
|
sta $loopvarName+1""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
if(length<=127) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel ldy $counterLabel
|
ldy $indexVar
|
||||||
iny
|
iny
|
||||||
iny
|
iny
|
||||||
cpy #${length and 255}
|
cpy #$length
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
jmp $loopLabel
|
bne $loopLabel""")
|
||||||
$counterLabel .byte 0
|
} else {
|
||||||
$endLabel""")
|
// length is 128 words, 256 bytes
|
||||||
|
asmgen.out("""
|
||||||
|
ldy $indexVar
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
bne $loopLabel
|
||||||
|
beq $endLabel""")
|
||||||
|
}
|
||||||
|
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||||
|
// allocate index var on ZP
|
||||||
|
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||||
|
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
$indexVar .byte 0""")
|
||||||
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
throw AssemblyError("for loop with floating point variables is not supported")
|
throw AssemblyError("for loop with floating point variables is not supported")
|
||||||
@ -406,237 +361,100 @@ $endLabel""")
|
|||||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||||
}
|
}
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
asmgen.loopContinueLabels.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||||
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter in such cases
|
if (range.isEmpty() || range.step==0)
|
||||||
if (range.isEmpty())
|
throw AssemblyError("empty range or step 0")
|
||||||
throw AssemblyError("empty range")
|
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||||
|
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||||
|
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
|
||||||
|
}
|
||||||
|
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
|
||||||
|
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||||
|
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
// not one of the easy cases, generate more complex code...
|
||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
val continueLabel = asmgen.makeLabel("for_continue")
|
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
asmgen.loopContinueLabels.push(continueLabel)
|
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
val counterLabel = asmgen.makeLabel("for_counter")
|
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||||
if(stmt.loopRegister!=null) {
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
|
|
||||||
// loop register over range
|
|
||||||
|
|
||||||
if(stmt.loopRegister!= Register.A)
|
|
||||||
throw AssemblyError("can only use A")
|
|
||||||
when {
|
|
||||||
range.step==1 -> {
|
|
||||||
// step = 1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${range.first}
|
|
||||||
sta $loopLabel+1
|
|
||||||
lda #${range.last-range.first+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel lda #0 ; modified""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
inc $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
range.step==-1 -> {
|
|
||||||
// step = -1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${range.first}
|
|
||||||
sta $loopLabel+1
|
|
||||||
lda #${range.first-range.last+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel lda #0 ; modified """)
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
dec $loopLabel+1
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
range.step >= 2 -> {
|
|
||||||
// step >= 2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.last-range.first) / range.step + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
$loopLabel pha""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel pla
|
|
||||||
dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
clc
|
|
||||||
adc #${range.step}
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// step <= -2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
$loopLabel pha""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel pla
|
|
||||||
dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
sec
|
|
||||||
sbc #${range.step.absoluteValue}
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
|
||||||
when {
|
|
||||||
range.step==1 -> {
|
|
||||||
// step = 1
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
lda #${range.last-range.first+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
when (range.step) {
|
||||||
|
0, 1, -1 -> {
|
||||||
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
if(range.last==255) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel dec $counterLabel
|
inc $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
inc $varname
|
inc $varname
|
||||||
jmp $loopLabel
|
bne $loopLabel""")
|
||||||
$counterLabel .byte 0
|
} else {
|
||||||
$endLabel""")
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #${range.last}
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel""")
|
||||||
}
|
}
|
||||||
range.step==-1 -> {
|
}
|
||||||
// step = -1
|
-2 -> {
|
||||||
asmgen.out("""
|
when (range.last) {
|
||||||
lda #${range.first}
|
0 -> asmgen.out("""
|
||||||
sta $varname
|
lda $varname
|
||||||
lda #${range.first-range.last+1 and 255}
|
|
||||||
sta $counterLabel
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel
|
dec $varname
|
||||||
$counterLabel .byte 0
|
jmp $loopLabel""")
|
||||||
$endLabel""")
|
1 -> asmgen.out("""
|
||||||
}
|
dec $varname
|
||||||
range.step >= 2 -> {
|
|
||||||
// step >= 2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.last-range.first) / range.step + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
sta $varname
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
bne $loopLabel""")
|
||||||
|
else -> asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
|
cmp #${range.last}
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// step <= -3 or >= 3
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #${range.last}
|
||||||
|
beq $endLabel
|
||||||
clc
|
clc
|
||||||
adc #${range.step}
|
adc #${range.step}
|
||||||
sta $varname
|
sta $varname
|
||||||
jmp $loopLabel
|
jmp $loopLabel""")
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// step <= -2
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
|
||||||
sta $counterLabel
|
|
||||||
lda #${range.first}
|
|
||||||
sta $varname
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel dec $counterLabel
|
|
||||||
beq $endLabel
|
|
||||||
lda $varname
|
|
||||||
sec
|
|
||||||
sbc #${range.step.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
jmp $loopLabel
|
|
||||||
$counterLabel .byte 0
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// loop over word range via loopvar
|
// loop over word range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar!!)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
when {
|
when (range.step) {
|
||||||
range.step == 1 -> {
|
0, 1, -1 -> {
|
||||||
// word, step = 1
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
val lastValue = range.last+1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${range.first}
|
|
||||||
ldy #>${range.first}
|
|
||||||
sta $varname
|
|
||||||
sty $varname+1
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel inc $varname
|
|
||||||
bne +
|
|
||||||
inc $varname+1
|
|
||||||
+ lda $varname
|
|
||||||
cmp #<$lastValue
|
|
||||||
bne +
|
|
||||||
lda $varname+1
|
|
||||||
cmp #>$lastValue
|
|
||||||
beq $endLabel
|
|
||||||
+ jmp $loopLabel
|
|
||||||
$endLabel""")
|
|
||||||
}
|
}
|
||||||
range.step == -1 -> {
|
else -> {
|
||||||
// word, step = 1
|
// word, step >= 2 or <= -2
|
||||||
val lastValue = range.last-1
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${range.first}
|
|
||||||
ldy #>${range.first}
|
|
||||||
sta $varname
|
|
||||||
sty $varname+1
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel lda $varname
|
|
||||||
bne +
|
|
||||||
dec $varname+1
|
|
||||||
+ dec $varname
|
|
||||||
lda $varname
|
|
||||||
cmp #<$lastValue
|
|
||||||
bne +
|
|
||||||
lda $varname+1
|
|
||||||
cmp #>$lastValue
|
|
||||||
beq $endLabel
|
|
||||||
+ jmp $loopLabel
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
range.step >= 2 -> {
|
|
||||||
// word, step >= 2
|
|
||||||
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
||||||
val lastValue = range.last+range.step
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${range.first}
|
lda #<${range.first}
|
||||||
ldy #>${range.first}
|
ldy #>${range.first}
|
||||||
@ -645,48 +463,21 @@ $endLabel""")
|
|||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
$continueLabel clc
|
|
||||||
lda $varname
|
lda $varname
|
||||||
|
cmp #<${range.last}
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>${range.last}
|
||||||
|
bne +
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
clc
|
||||||
adc #<${range.step}
|
adc #<${range.step}
|
||||||
sta $varname
|
sta $varname
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>${range.step}
|
adc #>${range.step}
|
||||||
sta $varname+1
|
sta $varname+1
|
||||||
lda $varname
|
jmp $loopLabel
|
||||||
cmp #<$lastValue
|
|
||||||
bne +
|
|
||||||
lda $varname+1
|
|
||||||
cmp #>$lastValue
|
|
||||||
beq $endLabel
|
|
||||||
+ jmp $loopLabel
|
|
||||||
$endLabel""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// step <= -2
|
|
||||||
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
|
||||||
val lastValue = range.last+range.step
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${range.first}
|
|
||||||
ldy #>${range.first}
|
|
||||||
sta $varname
|
|
||||||
sty $varname+1
|
|
||||||
$loopLabel""")
|
|
||||||
asmgen.translate(stmt.body)
|
|
||||||
asmgen.out("""
|
|
||||||
$continueLabel sec
|
|
||||||
lda $varname
|
|
||||||
sbc #<${range.step.absoluteValue}
|
|
||||||
sta $varname
|
|
||||||
lda $varname+1
|
|
||||||
sbc #>${range.step.absoluteValue}
|
|
||||||
sta $varname+1
|
|
||||||
lda $varname
|
|
||||||
cmp #<$lastValue
|
|
||||||
bne +
|
|
||||||
lda $varname+1
|
|
||||||
cmp #>$lastValue
|
|
||||||
beq $endLabel
|
|
||||||
+ jmp $loopLabel
|
|
||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -694,7 +485,134 @@ $endLabel""")
|
|||||||
else -> throw AssemblyError("range expression can only be byte or word")
|
else -> throw AssemblyError("range expression can only be byte or word")
|
||||||
}
|
}
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
asmgen.loopContinueLabels.pop()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
if (range.last == 255) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc $varname
|
||||||
|
bne $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #${range.last}
|
||||||
|
beq $endLabel
|
||||||
|
inc $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${range.first}
|
||||||
|
sta $varname
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
when (range.last) {
|
||||||
|
0 -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
asmgen.out("""
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #${range.last}
|
||||||
|
beq $endLabel
|
||||||
|
dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #<${range.last}
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>${range.last}
|
||||||
|
bne +
|
||||||
|
beq $endLabel
|
||||||
|
+ inc $varname
|
||||||
|
bne $loopLabel
|
||||||
|
inc $varname+1
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||||
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${range.first}
|
||||||
|
ldy #>${range.first}
|
||||||
|
sta $varname
|
||||||
|
sty $varname+1
|
||||||
|
$loopLabel""")
|
||||||
|
asmgen.translate(stmt.body)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $varname
|
||||||
|
cmp #<${range.last}
|
||||||
|
bne +
|
||||||
|
lda $varname+1
|
||||||
|
cmp #>${range.last}
|
||||||
|
bne +
|
||||||
|
beq $endLabel
|
||||||
|
+ lda $varname
|
||||||
|
bne +
|
||||||
|
dec $varname+1
|
||||||
|
+ dec $varname
|
||||||
|
jmp $loopLabel
|
||||||
|
$endLabel""")
|
||||||
|
asmgen.loopEndLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
|
||||||
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variableAsmName=asmgen.asmVariableName(stmt.loopVar))
|
||||||
|
val src = AsmAssignSource.fromAstSource(range.from, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
val assign = AsmAssignment(src, target, false, range.position)
|
||||||
|
asmgen.translateNormalAssignment(assign)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,110 +1,196 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.RegisterOrStatusflag
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.codegen.assignment.*
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.toHex
|
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
internal fun translateFunctionCall(stmt: IFunctionCall, preserveStatusRegisterAfterCall: Boolean) {
|
||||||
// output the code to setup the parameters and perform the actual call
|
// output the code to setup the parameters and perform the actual call
|
||||||
// does NOT output the code to deal with the result values!
|
// does NOT output the code to deal with the result values!
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
if(Register.X in sub.asmClobbers)
|
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
|
||||||
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
if(saveX)
|
||||||
|
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine())
|
||||||
|
|
||||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
val subName = asmgen.asmSymbolName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
|
// via variables
|
||||||
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
translateFuncArguments(arg.first, arg.second, sub)
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// via registers
|
||||||
|
if(sub.parameters.size==1) {
|
||||||
|
// just a single parameter, no risk of clobbering registers
|
||||||
|
argumentViaRegister(sub, sub.parameters.withIndex().single(), stmt.args[0])
|
||||||
|
} else {
|
||||||
|
// multiple register arguments, risk of register clobbering.
|
||||||
|
// evaluate arguments onto the stack, and load the registers from the evaluated values on the stack.
|
||||||
|
when {
|
||||||
|
stmt.args.all {it is AddressOf ||
|
||||||
|
it is NumericLiteralValue ||
|
||||||
|
it is StringLiteralValue ||
|
||||||
|
it is ArrayLiteralValue ||
|
||||||
|
it is IdentifierReference} -> {
|
||||||
|
// no risk of clobbering for these simple argument types. Optimize the register loading.
|
||||||
|
for(arg in sub.parameters.withIndex().zip(stmt.args)) {
|
||||||
|
argumentViaRegister(sub, arg.first, arg.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||||
|
registerArgsViaStackEvaluation(stmt, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
if(Register.X in sub.asmClobbers)
|
if(preserveStatusRegisterAfterCall) {
|
||||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
asmgen.out(" php\t; save status flags from call")
|
||||||
|
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
|
||||||
|
// must take care of popping this value again at the end!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFuncArguments(parameter: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
|
if(saveX)
|
||||||
val sourceIDt = value.inferType(program)
|
asmgen.restoreRegister(CpuRegister.X, preserveStatusRegisterAfterCall)
|
||||||
if(!sourceIDt.isKnown)
|
}
|
||||||
throw AssemblyError("arg type unknown")
|
|
||||||
val sourceDt = sourceIDt.typeOrElse(DataType.STRUCT)
|
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
if(!argumentTypeCompatible(sourceDt, parameter.value.type))
|
// this is called when one or more of the arguments are 'complex' and
|
||||||
throw AssemblyError("argument type incompatible")
|
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
|
||||||
// pass parameter via a variable
|
if(sub.parameters.isEmpty())
|
||||||
val paramVar = parameter.value
|
return
|
||||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
|
||||||
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
||||||
target.linkParents(value.parent)
|
for (arg in stmt.args.reversed())
|
||||||
when (value) {
|
asmgen.translateExpression(arg)
|
||||||
is NumericLiteralValue -> {
|
|
||||||
// optimize when the argument is a constant literal
|
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
when(parameter.value.type) {
|
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
|
||||||
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
asmgen.out(" inx") // align estack pointer
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
|
||||||
else -> throw AssemblyError("weird parameter datatype")
|
for(argi in stmt.args.zip(sub.asmParameterRegisters).withIndex()) {
|
||||||
|
when {
|
||||||
|
argi.value.second.stack -> TODO("asmsub @stack parameter")
|
||||||
|
argi.value.second.statusflag == Statusflag.Pc -> {
|
||||||
|
require(argForCarry == null)
|
||||||
|
argForCarry = argi
|
||||||
|
}
|
||||||
|
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
argi.value.second.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
||||||
|
require(argForXregister==null)
|
||||||
|
argForXregister = argi
|
||||||
|
}
|
||||||
|
argi.value.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
||||||
|
require(argForAregister == null)
|
||||||
|
argForAregister = argi
|
||||||
|
}
|
||||||
|
argi.value.second.registerOrPair == RegisterOrPair.Y -> {
|
||||||
|
asmgen.out(" ldy P8ESTACK_LO+${argi.index},x")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird argument")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
|
||||||
// optimize when the argument is a variable
|
if(argForCarry!=null) {
|
||||||
when (parameter.value.type) {
|
asmgen.out("""
|
||||||
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
lda P8ESTACK_LO+${argForCarry.index},x
|
||||||
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
beq +
|
||||||
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
sec
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?")
|
bcs ++
|
||||||
else -> throw AssemblyError("weird parameter datatype")
|
+ clc
|
||||||
|
+ php""") // push the status flags
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argForAregister!=null) {
|
||||||
|
when(argForAregister.value.second.registerOrPair) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" lda P8ESTACK_LO+${argForAregister.index},x")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda P8ESTACK_LO+${argForAregister.index},x | ldy P8ESTACK_HI+${argForAregister.index},x")
|
||||||
|
else -> throw AssemblyError("weird arg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
asmgen.assignFromRegister(target, value.register)
|
if(argForXregister!=null) {
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
if(argForAregister!=null)
|
||||||
when(value.addressExpression) {
|
asmgen.out(" pha")
|
||||||
is NumericLiteralValue -> {
|
when(argForXregister.value.second.registerOrPair) {
|
||||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
RegisterOrPair.X -> asmgen.out(" lda P8ESTACK_LO+${argForXregister.index},x | tax")
|
||||||
asmgen.assignFromMemoryByte(target, address, null)
|
RegisterOrPair.AX -> asmgen.out(" ldy P8ESTACK_LO+${argForXregister.index},x | lda P8ESTACK_HI+${argForXregister.index},x | tax | tya")
|
||||||
}
|
RegisterOrPair.XY -> asmgen.out(" ldy P8ESTACK_HI+${argForXregister.index},x | lda P8ESTACK_LO+${argForXregister.index},x | tax")
|
||||||
is IdentifierReference -> {
|
else -> throw AssemblyError("weird arg")
|
||||||
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value.addressExpression)
|
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
|
||||||
asmgen.assignFromRegister(target, Register.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
asmgen.assignFromEvalResult(target)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if(argForAregister!=null)
|
||||||
|
asmgen.out(" pla")
|
||||||
} else {
|
} else {
|
||||||
// pass parameter via a register parameter
|
repeat(sub.parameters.size - 1) { asmgen.out(" inx") } // unwind stack
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argForCarry!=null)
|
||||||
|
asmgen.out(" plp") // set the carry flag back to correct value
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
|
// pass parameter via a regular variable (not via registers)
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
|
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
|
||||||
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variableAsmName = varName)
|
||||||
|
val source = AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(tgt)
|
||||||
|
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
||||||
|
asmgen.translateNormalAssignment(asgn)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
|
// pass argument via a register parameter
|
||||||
|
val valueIDt = value.inferType(program)
|
||||||
|
if(!valueIDt.isKnown)
|
||||||
|
throw AssemblyError("arg type unknown")
|
||||||
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
val statusflag = paramRegister.statusflag
|
val statusflag = paramRegister.statusflag
|
||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
val stack = paramRegister.stack
|
val stack = paramRegister.stack
|
||||||
|
val requiredDt = parameter.value.type
|
||||||
|
if(requiredDt!=valueDt) {
|
||||||
|
if(valueDt largerThan requiredDt)
|
||||||
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
|
}
|
||||||
when {
|
when {
|
||||||
stack -> {
|
stack -> {
|
||||||
// push arg onto the stack
|
// push arg onto the stack
|
||||||
// note: argument order is reversed (first argument will be deepest on the stack)
|
// note: argument order is reversed (first argument will be deepest on the stack)
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
|
if(requiredDt!=valueDt)
|
||||||
|
asmgen.signExtendStackLsb(valueDt)
|
||||||
}
|
}
|
||||||
statusflag!=null -> {
|
statusflag!=null -> {
|
||||||
|
if(requiredDt!=valueDt)
|
||||||
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
if (statusflag == Statusflag.Pc) {
|
if (statusflag == Statusflag.Pc) {
|
||||||
// this param needs to be set last, right before the jsr
|
// this param needs to be set last, right before the jsr
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
@ -114,115 +200,65 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(if(carrySet) " sec" else " clc")
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
val sourceName = asmgen.asmVariableName(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
|
pha
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+ pla
|
||||||
""")
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when(value.register) {
|
|
||||||
Register.A -> asmgen.out(" cmp #0")
|
|
||||||
Register.X -> asmgen.out(" txa")
|
|
||||||
Register.Y -> asmgen.out(" tya")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
beq +
|
|
||||||
sec
|
|
||||||
bcs ++
|
|
||||||
+ clc
|
|
||||||
+
|
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
pha
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+
|
+ pla
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else throw AssemblyError("can only use Carry as status flag parameter")
|
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
}
|
}
|
||||||
register!=null && register.name.length==1 -> {
|
|
||||||
when (value) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteConstant(target, value.number.toShort())
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
|
|
||||||
target.linkParents(value.parent)
|
|
||||||
asmgen.assignFromByteVariable(target, value)
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
// via register or register pair
|
||||||
when(register) {
|
val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen)
|
||||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
if(requiredDt largerThan valueDt) {
|
||||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1")
|
||||||
else -> throw AssemblyError("cannot assign to register pair")
|
val scratchTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, sub, scratchVar)
|
||||||
|
val source = AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(source, scratchTarget, false, value.position))
|
||||||
|
asmgen.signExtendVariableLsb(scratchVar, valueDt)
|
||||||
|
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UWORD, scratchVar)
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
|
if(value is IdentifierReference) {
|
||||||
|
val addr = AddressOf(value, Position.DUMMY)
|
||||||
|
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
}
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||||
register!=null && register.name.length==2 -> {
|
|
||||||
// register pair as a 16-bit value (only possible for subroutine parameters)
|
|
||||||
when (value) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
// optimize when the argument is a constant literal
|
|
||||||
val hex = value.number.toHex()
|
|
||||||
when (register) {
|
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is AddressOf -> {
|
|
||||||
// optimize when the argument is an address of something
|
|
||||||
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
|
||||||
when (register) {
|
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
|
||||||
when (register) {
|
|
||||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
|
||||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(value)
|
|
||||||
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
|
||||||
throw AssemblyError("can't use X register here - use a variable")
|
|
||||||
else if (register == RegisterOrPair.AY)
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||||
if(argType isAssignableTo paramType)
|
if(argType isAssignableTo paramType)
|
||||||
return true
|
return true
|
||||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||||
|
@ -4,10 +4,8 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.RegisterExpr
|
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
@ -17,28 +15,11 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
val targetIdent = stmt.target.identifier
|
val targetIdent = stmt.target.identifier
|
||||||
val targetMemory = stmt.target.memoryAddress
|
val targetMemory = stmt.target.memoryAddress
|
||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
val targetRegister = stmt.target.register
|
val scope = stmt.definingSubroutine()
|
||||||
when {
|
when {
|
||||||
targetRegister!=null -> {
|
|
||||||
when(targetRegister) {
|
|
||||||
Register.A -> {
|
|
||||||
if(incr)
|
|
||||||
asmgen.out(" clc | adc #1 ")
|
|
||||||
else
|
|
||||||
asmgen.out(" sec | sbc #1 ")
|
|
||||||
}
|
|
||||||
Register.X -> {
|
|
||||||
if(incr) asmgen.out(" inx") else asmgen.out(" dex")
|
|
||||||
}
|
|
||||||
Register.Y -> {
|
|
||||||
if(incr) asmgen.out(" iny") else asmgen.out(" dey")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmIdentifierName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
val dt = stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)
|
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
|
||||||
when (dt) {
|
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -53,7 +34,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$what | ldy #>$what")
|
asmgen.out(" lda #<$what | ldy #>$what")
|
||||||
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
@ -65,87 +46,91 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val what = asmgen.asmIdentifierName(addressExpr)
|
val what = asmgen.asmVariableName(addressExpr)
|
||||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||||
if(incr)
|
if(incr)
|
||||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||||
else
|
else
|
||||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target type $targetMemory")
|
else -> {
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta (+) + 1
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta (+) + 2
|
||||||
|
""")
|
||||||
|
if(incr)
|
||||||
|
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||||
|
else
|
||||||
|
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val what = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.identifier)
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
|
||||||
when(index) {
|
when(index) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what+$indexValue" else " dec $what+$indexValue")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
asmgen.out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+")
|
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $what+$indexValue
|
lda $asmArrayvarname+$indexValue
|
||||||
bne +
|
bne +
|
||||||
dec $what+$indexValue+1
|
dec $asmArrayvarname+$indexValue+1
|
||||||
+ dec $what+$indexValue
|
+ dec $asmArrayvarname+$indexValue
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$what+$indexValue | ldy #>$what+$indexValue")
|
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
||||||
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
|
asmgen.out(" tax")
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $asmArrayvarname,x
|
||||||
|
bne +
|
||||||
|
dec $asmArrayvarname+1,x
|
||||||
|
+ dec $asmArrayvarname
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>$asmArrayvarname
|
||||||
|
clc
|
||||||
|
adc #<$asmArrayvarname
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr floats.inc_var_f""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird array elt dt")
|
||||||
|
}
|
||||||
|
asmgen.restoreRegister(CpuRegister.X, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target type ${stmt.target}")
|
else -> throw AssemblyError("weird target type ${stmt.target}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) {
|
|
||||||
asmgen.out(" stx ${C64Zeropage.SCRATCH_REG_X} | tax")
|
|
||||||
when(arrayDt) {
|
|
||||||
DataType.STR,
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
|
||||||
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
if(incr)
|
|
||||||
asmgen.out(" inc $arrayVarName,x | bne + | inc $arrayVarName+1,x |+")
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
lda $arrayVarName,x
|
|
||||||
bne +
|
|
||||||
dec $arrayVarName+1,x
|
|
||||||
+ dec $arrayVarName
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
asmgen.out(" lda #<$arrayVarName | ldy #>$arrayVarName")
|
|
||||||
asmgen.out(if(incr) " jsr c64flt.inc_indexed_var_f" else " jsr c64flt.dec_indexed_var_f")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird array dt")
|
|
||||||
}
|
|
||||||
asmgen.out(" ldx ${C64Zeropage.SCRATCH_REG_X}")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,187 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen.assignment
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
|
||||||
|
|
||||||
|
internal enum class TargetStorageKind {
|
||||||
|
VARIABLE,
|
||||||
|
ARRAY,
|
||||||
|
MEMORY,
|
||||||
|
REGISTER,
|
||||||
|
STACK
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum class SourceStorageKind {
|
||||||
|
LITERALNUMBER,
|
||||||
|
VARIABLE,
|
||||||
|
ARRAY,
|
||||||
|
MEMORY,
|
||||||
|
REGISTER,
|
||||||
|
STACK, // value is already present on stack
|
||||||
|
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||||
|
private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
|
val datatype: DataType,
|
||||||
|
val scope: Subroutine?,
|
||||||
|
private val variableAsmName: String? = null,
|
||||||
|
val array: ArrayIndexedExpression? = null,
|
||||||
|
val memory: DirectMemoryWrite? = null,
|
||||||
|
val register: RegisterOrPair? = null,
|
||||||
|
val origAstTarget: AssignTarget? = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
|
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
||||||
|
val asmVarname: String
|
||||||
|
get() = if(array==null)
|
||||||
|
variableAsmName!!
|
||||||
|
else
|
||||||
|
asmgen.asmVariableName(array.identifier)
|
||||||
|
|
||||||
|
lateinit var origAssign: AsmAssignment
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(register!=null && datatype !in IntegerDatatypes)
|
||||||
|
throw AssemblyError("register must be integer type")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
||||||
|
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
|
||||||
|
when {
|
||||||
|
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
|
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
|
||||||
|
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine(), memory = memoryAddress, origAstTarget = this)
|
||||||
|
else -> throw AssemblyError("weird target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromRegisters(registers: RegisterOrPair, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||||
|
when(registers) {
|
||||||
|
RegisterOrPair.A,
|
||||||
|
RegisterOrPair.X,
|
||||||
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, scope, register = registers)
|
||||||
|
RegisterOrPair.AX,
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||||
|
private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
|
val datatype: DataType,
|
||||||
|
private val variableAsmName: String? = null,
|
||||||
|
val array: ArrayIndexedExpression? = null,
|
||||||
|
val memory: DirectMemoryRead? = null,
|
||||||
|
val register: CpuRegister? = null,
|
||||||
|
val number: NumericLiteralValue? = null,
|
||||||
|
val expression: Expression? = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
|
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
||||||
|
|
||||||
|
val asmVarname: String
|
||||||
|
get() = if(array==null)
|
||||||
|
variableAsmName!!
|
||||||
|
else
|
||||||
|
asmgen.asmVariableName(array.identifier)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||||
|
val cv = value.constValue(program)
|
||||||
|
if(cv!=null)
|
||||||
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
|
|
||||||
|
return when(value) {
|
||||||
|
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv)
|
||||||
|
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||||
|
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value))
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
|
}
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if(value is FunctionCall) {
|
||||||
|
// functioncall.
|
||||||
|
val asmSub = value.target.targetStatement(program.namespace)
|
||||||
|
if(asmSub is Subroutine && asmSub.isAsmSubroutine) {
|
||||||
|
when (asmSub.asmReturnvaluesRegisters.count { rr -> rr.registerOrPair!=null }) {
|
||||||
|
0 -> throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
1 -> {
|
||||||
|
// assignment generation itself must make sure the status register is correct after the subroutine call, if status register is involved!
|
||||||
|
val reg = asmSub.asmReturnvaluesRegisters.single { rr->rr.registerOrPair!=null }.registerOrPair!!
|
||||||
|
val dt = when(reg) {
|
||||||
|
RegisterOrPair.A,
|
||||||
|
RegisterOrPair.X,
|
||||||
|
RegisterOrPair.Y -> DataType.UBYTE
|
||||||
|
RegisterOrPair.AX,
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
RegisterOrPair.XY -> DataType.UWORD
|
||||||
|
}
|
||||||
|
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't translate multiple return values in assignment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withAdjustedDt(newType: DataType) =
|
||||||
|
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||||
|
|
||||||
|
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
|
||||||
|
// allow some signed/unsigned relaxations
|
||||||
|
if(target.datatype!=datatype) {
|
||||||
|
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||||
|
return withAdjustedDt(target.datatype)
|
||||||
|
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
|
||||||
|
return withAdjustedDt(target.datatype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class AsmAssignment(val source: AsmAssignSource,
|
||||||
|
val target: AsmAssignTarget,
|
||||||
|
val isAugmentable: Boolean,
|
||||||
|
val position: Position) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
|
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
||||||
|
require(source.datatype.memorySize() <= target.datatype.memorySize()) {
|
||||||
|
"source storage size must be less or equal to target datatype storage size"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
118
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
118
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package prog8.compiler.target.cx16
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.compiler.*
|
||||||
|
import prog8.compiler.target.CpuType
|
||||||
|
import prog8.compiler.target.IMachineDefinition
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
|
import prog8.parser.ModuleImporter
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
internal object CX16MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU65c02
|
||||||
|
|
||||||
|
// 5-byte cbm MFLPT format limitations:
|
||||||
|
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||||
|
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
|
override val FLOAT_MEM_SIZE = 5
|
||||||
|
override val POINTER_MEM_SIZE = 2
|
||||||
|
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||||
|
override val RAW_LOAD_ADDRESS = 0x8000
|
||||||
|
|
||||||
|
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||||
|
// and some heavily used string constants derived from the two values above
|
||||||
|
override val ESTACK_LO = 0x0400 // $0400-$04ff inclusive
|
||||||
|
override val ESTACK_HI = 0x0500 // $0500-$05ff inclusive
|
||||||
|
|
||||||
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
|
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
|
override fun getFloatRomConst(number: Double): String? = null // Cx16 has no pulblic ROM float locations
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||||
|
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
|
importer.importLibraryModule(program, "syslib")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun launchEmulator(programName: String) {
|
||||||
|
for(emulator in listOf("x16emu")) {
|
||||||
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
|
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
|
||||||
|
"-run", "-prg", programName + ".prg")
|
||||||
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
|
val process: Process
|
||||||
|
try {
|
||||||
|
process=processb.start()
|
||||||
|
} catch(x: IOException) {
|
||||||
|
continue // try the next emulator executable
|
||||||
|
}
|
||||||
|
process.waitFor()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isRegularRAMaddress(address: Int): Boolean = address < 0x9f00 || address in 0xa000..0xbfff
|
||||||
|
|
||||||
|
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||||
|
zeropage = CX16Zeropage(compilerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||||
|
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
|
||||||
|
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||||
|
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
|
||||||
|
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||||
|
"inc", "inx", "iny", "jmp", "jsr",
|
||||||
|
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
|
||||||
|
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
|
||||||
|
"sec", "sed", "sei",
|
||||||
|
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
|
||||||
|
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
|
||||||
|
"rmb", "smb", "stp", "wai")
|
||||||
|
|
||||||
|
|
||||||
|
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
|
override val SCRATCH_B1 = 0x79 // temp storage for a single byte
|
||||||
|
override val SCRATCH_REG = 0x7a // temp storage for a register
|
||||||
|
override val SCRATCH_W1 = 0x7c // temp storage 1 for a word $7c+$7d
|
||||||
|
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
|
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
|
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||||
|
|
||||||
|
when (options.zeropage) {
|
||||||
|
ZeropageType.FULL -> {
|
||||||
|
free.addAll(0x22..0xff)
|
||||||
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
|
}
|
||||||
|
ZeropageType.KERNALSAFE -> {
|
||||||
|
free.addAll(0x22..0x7f)
|
||||||
|
free.addAll(0xa9..0xff)
|
||||||
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
|
}
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(0x22..0x7f)
|
||||||
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
|
}
|
||||||
|
ZeropageType.DONTUSE -> {
|
||||||
|
free.clear() // don't use zeropage at all
|
||||||
|
}
|
||||||
|
else -> throw CompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
|
}
|
||||||
|
|
||||||
|
require(SCRATCH_B1 !in free)
|
||||||
|
require(SCRATCH_REG !in free)
|
||||||
|
require(SCRATCH_W1 !in free)
|
||||||
|
require(SCRATCH_W2 !in free)
|
||||||
|
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package prog8.functions
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.StructDecl
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
@ -25,8 +27,6 @@ val BuiltinFunctions = mapOf(
|
|||||||
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"rol2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"rol2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"ror2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"lsl" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
|
||||||
"lsr" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
|
||||||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
// these few have a return value depending on the argument(s):
|
// these few have a return value depending on the argument(s):
|
||||||
@ -35,6 +35,7 @@ val BuiltinFunctions = mapOf(
|
|||||||
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
|
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||||
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||||
@ -62,7 +63,7 @@ val BuiltinFunctions = mapOf(
|
|||||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||||
"lsb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
"lsb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||||
"msb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
"msb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||||
"mkword" to FSignature(true, listOf(FParam("lsb", setOf(DataType.UBYTE)), FParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
"mkword" to FSignature(true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||||
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
|
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
|
||||||
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
||||||
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
||||||
@ -87,12 +88,25 @@ val BuiltinFunctions = mapOf(
|
|||||||
FParam("address", IterableDatatypes + DataType.UWORD),
|
FParam("address", IterableDatatypes + DataType.UWORD),
|
||||||
FParam("numwords", setOf(DataType.UWORD)),
|
FParam("numwords", setOf(DataType.UWORD)),
|
||||||
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||||
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen)
|
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
|
||||||
|
"substr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("start", setOf(DataType.UBYTE)),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null),
|
||||||
|
"leftstr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null),
|
||||||
|
"rightstr" to FSignature(false, listOf(
|
||||||
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
|
FParam("length", setOf(DataType.UBYTE))), null)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
|
||||||
|
|
||||||
fun builtinMin(array: List<Number>): Number = array.minBy { it.toDouble() }!!
|
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
||||||
|
|
||||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
||||||
|
|
||||||
@ -172,6 +186,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||||
|
class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg")
|
||||||
|
|
||||||
|
|
||||||
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||||
@ -226,54 +241,88 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
|
// 1 arg, type = anything, result type = ubyte
|
||||||
|
if(args.size!=1)
|
||||||
|
throw SyntaxError("sizeof requires one argument", position)
|
||||||
|
if(args[0] !is IdentifierReference)
|
||||||
|
throw SyntaxError("sizeof argument should be an identifier", position)
|
||||||
|
|
||||||
|
val dt = args[0].inferType(program)
|
||||||
|
if(dt.isKnown) {
|
||||||
|
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
||||||
|
?: throw CannotEvaluateException("sizeof", "no target")
|
||||||
|
|
||||||
|
fun structSize(target: StructDecl) =
|
||||||
|
NumericLiteralValue(DataType.UBYTE, target.statements.map { (it as VarDecl).datatype.memorySize() }.sum(), position)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||||
|
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||||
|
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||||
|
numericLiteral(elementDt.memorySize() * length, position)
|
||||||
|
}
|
||||||
|
dt.istype(DataType.STRUCT) -> {
|
||||||
|
when (target) {
|
||||||
|
is VarDecl -> structSize(target.struct!!)
|
||||||
|
is StructDecl -> structSize(target)
|
||||||
|
else -> throw CompilerException("weird struct type $target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||||
|
else -> NumericLiteralValue(DataType.UBYTE, dt.typeOrElse(DataType.STRUCT).memorySize(), position)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw SyntaxError("sizeof invalid argument type", position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("strlen requires one argument", position)
|
throw SyntaxError("strlen requires one argument", position)
|
||||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val argument=args[0]
|
||||||
if(argument.type != DataType.STR)
|
if(argument is StringLiteralValue)
|
||||||
|
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
||||||
|
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
|
||||||
|
if(vardecl!=null) {
|
||||||
|
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
||||||
throw SyntaxError("strlen must have string argument", position)
|
throw SyntaxError("strlen must have string argument", position)
|
||||||
|
if(vardecl.autogeneratedDontRemove && vardecl.value!=null) {
|
||||||
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NotConstArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("len requires one argument", position)
|
throw SyntaxError("len requires one argument", position)
|
||||||
val constArg = args[0].constValue(program)
|
|
||||||
if(constArg!=null)
|
|
||||||
throw SyntaxError("len of weird argument ${args[0]}", position)
|
|
||||||
|
|
||||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||||
var arraySize = directMemVar?.arraysize?.size()
|
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||||
if(arraySize != null)
|
if(arraySize != null)
|
||||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||||
if(args[0] is ArrayLiteralValue)
|
if(args[0] is ArrayLiteralValue)
|
||||||
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
||||||
if(args[0] !is IdentifierReference)
|
if(args[0] !is IdentifierReference)
|
||||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
throw SyntaxError("len argument should be an identifier", position)
|
||||||
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)!!
|
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)
|
||||||
|
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||||
|
|
||||||
return when(target.datatype) {
|
return when(target.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
||||||
arraySize = target.arraysize!!.size()!!
|
arraySize = target.arraysize?.constIndex()
|
||||||
if(arraySize>256)
|
if(arraySize==null)
|
||||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
throw CannotEvaluateException("len", "arraysize unknown")
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
arraySize = target.arraysize!!.size()!!
|
|
||||||
if(arraySize>256)
|
|
||||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
val refLv = target.value as StringLiteralValue
|
val refLv = target.value as StringLiteralValue
|
||||||
if(refLv.value.length>255)
|
|
||||||
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
|
||||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||||
}
|
}
|
||||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||||
|
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,9 +330,9 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
|
|
||||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
val constLsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
||||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||||
}
|
}
|
||||||
@ -293,7 +342,7 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
|
|||||||
throw SyntaxError("sin8 requires one argument", position)
|
throw SyntaxError("sin8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
@ -301,7 +350,7 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
|
|||||||
throw SyntaxError("sin8u requires one argument", position)
|
throw SyntaxError("sin8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
@ -309,7 +358,7 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
|
|||||||
throw SyntaxError("cos8 requires one argument", position)
|
throw SyntaxError("cos8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
@ -317,7 +366,7 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
|
|||||||
throw SyntaxError("cos8u requires one argument", position)
|
throw SyntaxError("cos8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
@ -356,7 +405,7 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
|
|||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sgn requires one argument", position)
|
throw SyntaxError("sgn requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toShort(), position)
|
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
||||||
@ -368,8 +417,8 @@ private fun numericLiteral(value: Number, position: Position): NumericLiteralVal
|
|||||||
floatNum
|
floatNum
|
||||||
|
|
||||||
return when(tweakedValue) {
|
return when(tweakedValue) {
|
||||||
is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
is Int -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||||
is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
is Short -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||||
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
|
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
|
||||||
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||||
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||||
|
89
compiler/src/prog8/optimizer/BinExprSplitter.kt
Normal file
89
compiler/src/prog8/optimizer/BinExprSplitter.kt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
|
||||||
|
|
||||||
|
internal class BinExprSplitter(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
||||||
|
// if(decl.type==VarDeclType.VAR ) {
|
||||||
|
// val binExpr = decl.value as? BinaryExpression
|
||||||
|
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
||||||
|
// // split into a vardecl with just the left expression, and an aug. assignment with the right expression.
|
||||||
|
// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position)
|
||||||
|
// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
// val assign = Assignment(target, augExpr, binExpr.position)
|
||||||
|
// println("SPLIT VARDECL $decl")
|
||||||
|
// return listOf(
|
||||||
|
// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl),
|
||||||
|
// IAstModification.InsertAfter(decl, assign, parent)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return noModifications
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if (binExpr != null) {
|
||||||
|
/*
|
||||||
|
|
||||||
|
reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||||
|
by attempting to splitting it up into individual simple steps:
|
||||||
|
|
||||||
|
|
||||||
|
X = BinExpr X = LeftExpr
|
||||||
|
<operator> followed by
|
||||||
|
/ \ IF 'X' not used X = BinExpr
|
||||||
|
/ \ IN LEFTEXPR ==> <operator>
|
||||||
|
/ \ / \
|
||||||
|
LeftExpr. RightExpr. / \
|
||||||
|
/ \ / \ X RightExpr.
|
||||||
|
.. .. .. ..
|
||||||
|
|
||||||
|
*/
|
||||||
|
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
|
||||||
|
if (!assignment.isAugmentable) {
|
||||||
|
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
|
||||||
|
val targetExpr = assignment.target.toExpression()
|
||||||
|
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.InsertBefore(assignment, firstAssign, parent),
|
||||||
|
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO further unraveling of binary expression trees into flat statements.
|
||||||
|
// however this should probably be done in a more generic way to also service
|
||||||
|
// the expressiontrees that are not used in an assignment statement...
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean {
|
||||||
|
return when {
|
||||||
|
target.identifier!=null -> target.isInRegularRAM(namespace)
|
||||||
|
target.memoryAddress!=null -> target.isInRegularRAM(namespace)
|
||||||
|
target.arrayindexed!=null -> {
|
||||||
|
val index = target.arrayindexed!!.arrayspec.index
|
||||||
|
if(index is NumericLiteralValue)
|
||||||
|
target.isInRegularRAM(namespace)
|
||||||
|
else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,8 +14,7 @@ import prog8.compiler.loadAsmIncludeFile
|
|||||||
|
|
||||||
private val alwaysKeepSubroutines = setOf(
|
private val alwaysKeepSubroutines = setOf(
|
||||||
Pair("main", "start"),
|
Pair("main", "start"),
|
||||||
Pair("irq", "irq"),
|
Pair("irq", "irq")
|
||||||
Pair("prog8_lib", "init_system")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
@ -24,10 +23,10 @@ private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexO
|
|||||||
|
|
||||||
class CallGraph(private val program: Program) : IAstVisitor {
|
class CallGraph(private val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val calls = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
|
||||||
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
||||||
val usedSymbols = mutableSetOf<Statement>()
|
val usedSymbols = mutableSetOf<Statement>()
|
||||||
@ -55,17 +54,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
it.importedBy.clear()
|
it.importedBy.clear()
|
||||||
it.imports.clear()
|
it.imports.clear()
|
||||||
|
|
||||||
it.importedBy.addAll(modulesImportedBy.getValue(it))
|
it.importedBy.addAll(importedBy.getValue(it))
|
||||||
it.imports.addAll(modulesImporting.getValue(it))
|
it.imports.addAll(imports.getValue(it))
|
||||||
|
|
||||||
forAllSubroutines(it) { sub ->
|
|
||||||
sub.calledBy.clear()
|
|
||||||
sub.calls.clear()
|
|
||||||
|
|
||||||
sub.calledBy.addAll(subroutinesCalledBy.getValue(sub))
|
|
||||||
sub.calls.addAll(subroutinesCalling.getValue(sub))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootmodule = program.modules.first()
|
val rootmodule = program.modules.first()
|
||||||
@ -85,8 +75,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val thisModule = directive.definingModule()
|
val thisModule = directive.definingModule()
|
||||||
if (directive.directive == "%import") {
|
if (directive.directive == "%import") {
|
||||||
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
val importedModule: Module = program.modules.single { it.name == directive.args[0].name }
|
||||||
modulesImporting[thisModule] = modulesImporting.getValue(thisModule).plus(importedModule)
|
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
||||||
modulesImportedBy[importedModule] = modulesImportedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive == "%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingScope()
|
val scope = directive.definingScope()
|
||||||
@ -127,7 +117,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
if (decl.autogeneratedDontRemove || decl.definingModule().isLibraryModule) {
|
if (decl.autogeneratedDontRemove || decl.definingModule().isLibraryModule) {
|
||||||
// make sure autogenerated vardecls are in the used symbols
|
// make sure autogenerated vardecls are in the used symbols and are never removed as 'unused'
|
||||||
addNodeAndParentScopes(decl)
|
addNodeAndParentScopes(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,8 +131,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCall.definingSubroutine()?.let { thisSub ->
|
functionCall.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
@ -152,8 +142,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCallStatement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
@ -163,8 +153,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
jump.definingSubroutine()?.let { thisSub ->
|
jump.definingSubroutine()?.let { thisSub ->
|
||||||
subroutinesCalling[thisSub] = subroutinesCalling.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
@ -190,14 +180,14 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
||||||
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
} else if (jumptarget.contains('.')) {
|
} else if (jumptarget.contains('.')) {
|
||||||
// maybe only the first part already refers to a subroutine
|
// maybe only the first part already refers to a subroutine
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
||||||
if (node2 is Subroutine) {
|
if (node2 is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node2)
|
calls[scope] = calls.getValue(scope).plus(node2)
|
||||||
subroutinesCalledBy[node2] = subroutinesCalledBy.getValue(node2).plus(context)
|
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,8 +199,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (target.contains('.')) {
|
if (target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
subroutinesCalling[scope] = subroutinesCalling.getValue(scope).plus(node)
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
subroutinesCalledBy[node] = subroutinesCalledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,11 +132,11 @@ class ConstExprEvaluator {
|
|||||||
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||||
if(left.type== DataType.UBYTE) {
|
if(left.type== DataType.UBYTE) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toShort(), left.position)
|
||||||
}
|
}
|
||||||
} else if(left.type== DataType.UWORD) {
|
} else if(left.type== DataType.UWORD) {
|
||||||
if(right.type in IntegerDatatypes) {
|
if(right.type in IntegerDatatypes) {
|
||||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
return NumericLiteralValue(DataType.UWORD, left.number.toInt() and right.number.toInt(), left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||||
@ -163,7 +163,7 @@ class ConstExprEvaluator {
|
|||||||
val error = "cannot add $left and $right"
|
val error = "cannot add $left and $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
|
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
|
||||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ class ConstExprEvaluator {
|
|||||||
val error = "cannot subtract $left and $right"
|
val error = "cannot subtract $left and $right"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
|
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
|
||||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
@ -197,7 +197,7 @@ class ConstExprEvaluator {
|
|||||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||||
return when (left.type) {
|
return when (left.type) {
|
||||||
in IntegerDatatypes -> when (right.type) {
|
in IntegerDatatypes -> when (right.type) {
|
||||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
|
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
|
||||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||||
else -> throw ExpressionError(error, left.position)
|
else -> throw ExpressionError(error, left.position)
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ class ConstExprEvaluator {
|
|||||||
in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||||
val result: Int = left.number.toInt() / right.number.toInt()
|
val result: Int = left.number.toInt() / right.number.toInt()
|
||||||
NumericLiteralValue.optimalNumeric(result, left.position)
|
NumericLiteralValue.optimalInteger(result, left.position)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||||
|
@ -1,277 +1,84 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
|
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
var optimizationsDone: Int = 0
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
|
||||||
// TODO: use call tree for this?
|
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
|
||||||
errors.err("recursive var declaration", decl.position)
|
|
||||||
return decl
|
|
||||||
}
|
|
||||||
|
|
||||||
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
|
||||||
if(decl.isArray){
|
|
||||||
if(decl.arraysize==null) {
|
|
||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
|
||||||
val arrayval = decl.value as? ArrayLiteralValue
|
|
||||||
if(arrayval!=null) {
|
|
||||||
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position), decl.position)
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(decl.arraysize?.size()==null) {
|
|
||||||
val size = decl.arraysize!!.index.accept(this)
|
|
||||||
if(size is NumericLiteralValue) {
|
|
||||||
decl.arraysize = ArrayIndex(size, decl.position)
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when(decl.datatype) {
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
|
||||||
val litval = decl.value as? NumericLiteralValue
|
|
||||||
if (litval!=null && litval.type in IntegerDatatypes) {
|
|
||||||
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
|
||||||
decl.value = newValue
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
val numericLv = decl.value as? NumericLiteralValue
|
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
|
||||||
if(rangeExpr!=null) {
|
|
||||||
// convert the initializer range expression to an actual array
|
|
||||||
val declArraySize = decl.arraysize?.size()
|
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
|
||||||
if(constRange!=null) {
|
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
|
||||||
if(eltType in ByteDatatypes) {
|
|
||||||
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
|
||||||
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
|
||||||
position = decl.value!!.position)
|
|
||||||
} else {
|
|
||||||
decl.value = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
|
||||||
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
|
||||||
position = decl.value!!.position)
|
|
||||||
}
|
|
||||||
decl.value!!.linkParents(decl)
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
|
||||||
errors.err("arraysize requires only integers here", numericLv.position)
|
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
|
||||||
if (rangeExpr==null && numericLv!=null) {
|
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
|
||||||
val fillvalue = numericLv.number.toInt()
|
|
||||||
when(decl.datatype){
|
|
||||||
DataType.ARRAY_UB -> {
|
|
||||||
if(fillvalue !in 0..255)
|
|
||||||
errors.err("ubyte value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_B -> {
|
|
||||||
if(fillvalue !in -128..127)
|
|
||||||
errors.err("byte value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW -> {
|
|
||||||
if(fillvalue !in 0..65535)
|
|
||||||
errors.err("uword value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
DataType.ARRAY_W -> {
|
|
||||||
if(fillvalue !in -32768..32767)
|
|
||||||
errors.err("word value overflow", numericLv.position)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
// create the array itself, filled with the fillvalue.
|
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) as Expression}.toTypedArray()
|
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
|
||||||
decl.value = refValue
|
|
||||||
refValue.parent=decl
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
|
||||||
val litval = decl.value as? NumericLiteralValue
|
|
||||||
if(litval==null) {
|
|
||||||
// there's no initialization value, but the size is known, so we're ok.
|
|
||||||
return super.visit(decl)
|
|
||||||
} else {
|
|
||||||
// arraysize initializer is a single int, and we know the size.
|
|
||||||
val fillvalue = litval.number.toDouble()
|
|
||||||
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
|
||||||
errors.err("float value overflow", litval.position)
|
|
||||||
else {
|
|
||||||
// create the array itself, filled with the fillvalue.
|
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
|
||||||
decl.value = refValue
|
|
||||||
refValue.parent=decl
|
|
||||||
optimizationsDone++
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// nothing to do for this type
|
|
||||||
// this includes strings and structs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val declValue = decl.value
|
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR
|
|
||||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
|
||||||
// cast the numeric literal to the appropriate datatype of the variable
|
|
||||||
decl.value = declValue.cast(decl.datatype)
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
|
||||||
*/
|
|
||||||
override fun visit(identifier: IdentifierReference): Expression {
|
|
||||||
// don't replace when it's an assignment target or loop variable
|
|
||||||
if(identifier.parent is AssignTarget)
|
|
||||||
return identifier
|
|
||||||
var forloop = identifier.parent as? ForLoop
|
|
||||||
if(forloop==null)
|
|
||||||
forloop = identifier.parent.parent as? ForLoop
|
|
||||||
if(forloop!=null && identifier===forloop.loopVar)
|
|
||||||
return identifier
|
|
||||||
|
|
||||||
val cval = identifier.constValue(program) ?: return identifier
|
|
||||||
return when (cval.type) {
|
|
||||||
in NumericDatatypes -> {
|
|
||||||
val copy = NumericLiteralValue(cval.type, cval.number, identifier.position)
|
|
||||||
copy.parent = identifier.parent
|
|
||||||
copy
|
|
||||||
}
|
|
||||||
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
|
||||||
else -> identifier
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
|
||||||
super.visit(functionCall)
|
|
||||||
typeCastConstArguments(functionCall)
|
|
||||||
return functionCall.constValue(program) ?: functionCall
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
super.visit(functionCallStatement)
|
|
||||||
typeCastConstArguments(functionCallStatement)
|
|
||||||
return functionCallStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun typeCastConstArguments(functionCall: IFunctionCall) {
|
|
||||||
if(functionCall.target.nameInSource.size==1) {
|
|
||||||
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
|
|
||||||
if(builtinFunction!=null) {
|
|
||||||
// match the arguments of a builtin function signature.
|
|
||||||
for(arg in functionCall.args.withIndex().zip(builtinFunction.parameters)) {
|
|
||||||
val possibleDts = arg.second.possibleDatatypes
|
|
||||||
val argConst = arg.first.value.constValue(program)
|
|
||||||
if(argConst!=null && argConst.type !in possibleDts) {
|
|
||||||
val convertedValue = argConst.cast(possibleDts.first())
|
|
||||||
functionCall.args[arg.first.index] = convertedValue
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// match the arguments of a subroutine.
|
|
||||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
|
|
||||||
for(arg in functionCall.args.withIndex().zip(subroutine.parameters)) {
|
|
||||||
val expectedDt = arg.second.type
|
|
||||||
val argConst = arg.first.value.constValue(program)
|
|
||||||
if(argConst!=null && argConst.type!=expectedDt) {
|
|
||||||
val convertedValue = argConst.cast(expectedDt)
|
|
||||||
functionCall.args[arg.first.index] = convertedValue
|
|
||||||
optimizationsDone++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
val addrOf = memread.addressExpression as? AddressOf
|
||||||
if(addrOf!=null)
|
return if(addrOf!=null)
|
||||||
return super.visit(addrOf.identifier)
|
listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent))
|
||||||
return super.visit(memread)
|
else
|
||||||
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
* Try to accept a unary prefix expression.
|
// Try to turn a unary prefix expression into a single constant value.
|
||||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
// Compile-time constant sub expressions will be evaluated on the spot.
|
||||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||||
*/
|
val subexpr = expr.expression
|
||||||
override fun visit(expr: PrefixExpression): Expression {
|
|
||||||
val prefixExpr=super.visit(expr)
|
|
||||||
if(prefixExpr !is PrefixExpression)
|
|
||||||
return prefixExpr
|
|
||||||
|
|
||||||
val subexpr = prefixExpr.expression
|
|
||||||
if (subexpr is NumericLiteralValue) {
|
if (subexpr is NumericLiteralValue) {
|
||||||
// accept prefixed literal values (such as -3, not true)
|
// accept prefixed literal values (such as -3, not true)
|
||||||
return when (prefixExpr.operator) {
|
return when (expr.operator) {
|
||||||
"+" -> subexpr
|
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
|
||||||
"-" -> when (subexpr.type) {
|
"-" -> when (subexpr.type) {
|
||||||
in IntegerDatatypes -> {
|
in IntegerDatatypes -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
|
NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position)
|
NumericLiteralValue(DataType.FLOAT, -subexpr.number.toDouble(), subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
|
||||||
}
|
}
|
||||||
"~" -> when (subexpr.type) {
|
"~" -> when (subexpr.type) {
|
||||||
in IntegerDatatypes -> {
|
DataType.BYTE -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
|
NumericLiteralValue(DataType.BYTE, subexpr.number.toInt().inv(), subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.UBYTE, subexpr.number.toInt().inv() and 255, subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.WORD, subexpr.number.toInt().inv(), subexpr.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
|
NumericLiteralValue(DataType.UWORD, subexpr.number.toInt().inv() and 65535, subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||||
}
|
}
|
||||||
"not" -> {
|
"not" -> {
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(expr,
|
||||||
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
|
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position),
|
||||||
|
parent))
|
||||||
}
|
}
|
||||||
else -> throw ExpressionError(prefixExpr.operator, subexpr.position)
|
else -> throw ExpressionError(expr.operator, subexpr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prefixExpr
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to accept a binary expression.
|
* Try to constfold a binary expression.
|
||||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||||
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
||||||
*
|
*
|
||||||
@ -287,13 +94,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
* (X / c1) * c2 -> X / (c2/c1)
|
* (X / c1) * c2 -> X / (c2/c1)
|
||||||
* (X + c1) - c2 -> X + (c1-c2)
|
* (X + c1) - c2 -> X + (c1-c2)
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(expr)
|
|
||||||
|
|
||||||
if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|
|
||||||
|| expr.right is StringLiteralValue || expr.right is ArrayLiteralValue)
|
|
||||||
throw FatalAstException("binexpr with reference litval instead of numeric")
|
|
||||||
|
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
val rightconst = expr.right.constValue(program)
|
val rightconst = expr.right.constValue(program)
|
||||||
|
|
||||||
@ -307,21 +108,153 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
val subrightconst = subExpr.right.constValue(program)
|
val subrightconst = subExpr.right.constValue(program)
|
||||||
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
|
if ((subleftconst != null && subrightconst == null) || (subleftconst==null && subrightconst!=null)) {
|
||||||
// try reordering.
|
// try reordering.
|
||||||
return groupTwoConstsTogether(expr, subExpr,
|
val change = groupTwoConstsTogether(expr, subExpr,
|
||||||
leftconst != null, rightconst != null,
|
leftconst != null, rightconst != null,
|
||||||
subleftconst != null, subrightconst != null)
|
subleftconst != null, subrightconst != null)
|
||||||
|
return change?.let { listOf(it) } ?: noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const fold when both operands are a const
|
// const fold when both operands are a const
|
||||||
return when {
|
if(leftconst != null && rightconst != null) {
|
||||||
leftconst != null && rightconst != null -> {
|
|
||||||
optimizationsDone++
|
|
||||||
val evaluator = ConstExprEvaluator()
|
val evaluator = ConstExprEvaluator()
|
||||||
evaluator.evaluate(leftconst, expr.operator, rightconst)
|
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> expr
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
// because constant folding can result in arrays that are now suddenly capable
|
||||||
|
// of telling the type of all their elements (for instance, when they contained -2 which
|
||||||
|
// was a prefix expression earlier), we recalculate the array's datatype.
|
||||||
|
if(array.type.isKnown)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
// if the array literalvalue is inside an array vardecl, take the type from that
|
||||||
|
// otherwise infer it from the elements of the array
|
||||||
|
val vardeclType = (array.parent as? VarDecl)?.datatype
|
||||||
|
if(vardeclType!=null) {
|
||||||
|
val newArray = array.cast(vardeclType)
|
||||||
|
if (newArray != null && newArray != array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
|
} else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if (arrayDt.isKnown) {
|
||||||
|
val newArray = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
if (newArray != null && newArray != array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
// the args of a fuction are constfolded via recursion already.
|
||||||
|
val constvalue = functionCall.constValue(program)
|
||||||
|
return if(constvalue!=null)
|
||||||
|
listOf(IAstModification.ReplaceNode(functionCall, constvalue, parent))
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr? {
|
||||||
|
val fromCast = rangeFrom.cast(targetDt)
|
||||||
|
val toCast = rangeTo.cast(targetDt)
|
||||||
|
if(!fromCast.isValid || !toCast.isValid)
|
||||||
|
return null
|
||||||
|
|
||||||
|
val newStep =
|
||||||
|
if(stepLiteral!=null) {
|
||||||
|
val stepCast = stepLiteral.cast(targetDt)
|
||||||
|
if(stepCast.isValid)
|
||||||
|
stepCast.valueOrZero()
|
||||||
|
else
|
||||||
|
range.step
|
||||||
|
} else {
|
||||||
|
range.step
|
||||||
|
}
|
||||||
|
|
||||||
|
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||||
|
val iterableRange = forLoop.iterable as? RangeExpr ?: return noModifications
|
||||||
|
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
||||||
|
val rangeTo = iterableRange.to as? NumericLiteralValue
|
||||||
|
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||||
|
|
||||||
|
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace)!!
|
||||||
|
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
||||||
|
when(loopvar.datatype) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if(rangeFrom.type!= DataType.UBYTE) {
|
||||||
|
// attempt to translate the iterable into ubyte values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(rangeFrom.type!= DataType.BYTE) {
|
||||||
|
// attempt to translate the iterable into byte values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(rangeFrom.type!= DataType.UWORD) {
|
||||||
|
// attempt to translate the iterable into uword values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(rangeFrom.type!= DataType.WORD) {
|
||||||
|
// attempt to translate the iterable into word values
|
||||||
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val numval = decl.value as? NumericLiteralValue
|
||||||
|
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||||
|
val valueDt = numval.inferType(program)
|
||||||
|
if(!valueDt.istype(decl.datatype)) {
|
||||||
|
val cast = numval.cast(decl.datatype)
|
||||||
|
if(cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ShuffleOperands(val expr: BinaryExpression,
|
||||||
|
val exprOperator: String?,
|
||||||
|
val subExpr: BinaryExpression,
|
||||||
|
val newExprLeft: Expression?,
|
||||||
|
val newExprRight: Expression?,
|
||||||
|
val newSubexprLeft: Expression?,
|
||||||
|
val newSubexprRight: Expression?
|
||||||
|
): IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
if(exprOperator!=null) expr.operator = exprOperator
|
||||||
|
if(newExprLeft!=null) expr.left = newExprLeft
|
||||||
|
if(newExprRight!=null) expr.right = newExprRight
|
||||||
|
if(newSubexprLeft!=null) subExpr.left = newSubexprLeft
|
||||||
|
if(newSubexprRight!=null) subExpr.right = newSubexprRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,64 +263,61 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
leftIsConst: Boolean,
|
leftIsConst: Boolean,
|
||||||
rightIsConst: Boolean,
|
rightIsConst: Boolean,
|
||||||
subleftIsConst: Boolean,
|
subleftIsConst: Boolean,
|
||||||
subrightIsConst: Boolean): Expression
|
subrightIsConst: Boolean): IAstModification?
|
||||||
{
|
{
|
||||||
// todo: this implements only a small set of possible reorderings at this time
|
// todo: this implements only a small set of possible reorderings at this time
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
// both operators are the isSameAs.
|
// both operators are the same.
|
||||||
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
|
||||||
if(expr.operator=="+" || expr.operator=="*") {
|
// If associative, we can simply shuffle the const operands around to optimize.
|
||||||
if(leftIsConst) {
|
if(expr.operator in associativeOperators) {
|
||||||
|
return if(leftIsConst) {
|
||||||
if(subleftIsConst)
|
if(subleftIsConst)
|
||||||
expr.left = subExpr.right.also { subExpr.right = expr.left }
|
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||||
else
|
else
|
||||||
expr.left = subExpr.left.also { subExpr.left = expr.left }
|
ShuffleOperands(expr, null, subExpr, subExpr.left, null, expr.left, null)
|
||||||
} else {
|
} else {
|
||||||
if(subleftIsConst)
|
if(subleftIsConst)
|
||||||
expr.right = subExpr.right.also {subExpr.right = expr.right }
|
ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||||
else
|
else
|
||||||
expr.right = subExpr.left.also { subExpr.left = expr.right }
|
ShuffleOperands(expr, null, subExpr, null, subExpr.left, expr.right, null)
|
||||||
}
|
}
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *)
|
// If - or /, we simetimes must reorder more, and flip operators (- -> +, / -> *)
|
||||||
if(expr.operator=="-" || expr.operator=="/") {
|
if(expr.operator=="-" || expr.operator=="/") {
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
if(leftIsConst) {
|
||||||
return if(subleftIsConst) {
|
return if (subleftIsConst) {
|
||||||
val tmp = subExpr.right
|
ShuffleOperands(expr, if (expr.operator == "-") "+" else "*", subExpr, subExpr.right, null, expr.left, subExpr.left)
|
||||||
subExpr.right = subExpr.left
|
} else {
|
||||||
subExpr.left = expr.left
|
IAstModification.ReplaceNode(expr,
|
||||||
expr.left = tmp
|
|
||||||
expr.operator = if(expr.operator=="-") "+" else "*"
|
|
||||||
expr
|
|
||||||
} else
|
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||||
expr.operator, subExpr.left, expr.position)
|
expr.operator, subExpr.left, expr.position),
|
||||||
|
expr.parent)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return if(subleftIsConst) {
|
return if(subleftIsConst) {
|
||||||
expr.right = subExpr.right.also { subExpr.right = expr.right }
|
return ShuffleOperands(expr, null, subExpr, null, subExpr.right, null, expr.right)
|
||||||
expr
|
} else {
|
||||||
} else
|
IAstModification.ReplaceNode(expr,
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
subExpr.left, expr.operator,
|
subExpr.left, expr.operator,
|
||||||
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||||
expr.position)
|
expr.position),
|
||||||
|
expr.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expr
|
}
|
||||||
|
return null
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
if(expr.operator=="/" && subExpr.operator=="*") {
|
if(expr.operator=="/" && subExpr.operator=="*") {
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
if(leftIsConst) {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// C1/(C2*V) -> (C1/C2)/V
|
// C1/(C2*V) -> (C1/C2)/V
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(expr.left, "/", subExpr.left, subExpr.position),
|
BinaryExpression(expr.left, "/", subExpr.left, subExpr.position),
|
||||||
@ -400,8 +330,9 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"/",
|
"/",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
} else {
|
} else {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// (C1*V)/C2 -> (C1/C2)*V
|
// (C1*V)/C2 -> (C1/C2)*V
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(subExpr.left, "/", expr.right, subExpr.position),
|
BinaryExpression(subExpr.left, "/", expr.right, subExpr.position),
|
||||||
@ -414,12 +345,12 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"*",
|
"*",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator=="*" && subExpr.operator=="/") {
|
else if(expr.operator=="*" && subExpr.operator=="/") {
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
if(leftIsConst) {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// C1*(C2/V) -> (C1*C2)/V
|
// C1*(C2/V) -> (C1*C2)/V
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(expr.left, "*", subExpr.left, subExpr.position),
|
BinaryExpression(expr.left, "*", subExpr.left, subExpr.position),
|
||||||
@ -432,8 +363,9 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"*",
|
"*",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
} else {
|
} else {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// (C1/V)*C2 -> (C1*C2)/V
|
// (C1/V)*C2 -> (C1*C2)/V
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(subExpr.left, "*", expr.right, subExpr.position),
|
BinaryExpression(subExpr.left, "*", expr.right, subExpr.position),
|
||||||
@ -446,12 +378,12 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"*",
|
"*",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator=="+" && subExpr.operator=="-") {
|
else if(expr.operator=="+" && subExpr.operator=="-") {
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst){
|
if(leftIsConst){
|
||||||
return if(subleftIsConst){
|
val change = if(subleftIsConst){
|
||||||
// c1+(c2-v) -> (c1+c2)-v
|
// c1+(c2-v) -> (c1+c2)-v
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(expr.left, "+", subExpr.left, subExpr.position),
|
BinaryExpression(expr.left, "+", subExpr.left, subExpr.position),
|
||||||
@ -464,8 +396,9 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"+",
|
"+",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
} else {
|
} else {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// (c1-v)+c2 -> (c1+c2)-v
|
// (c1-v)+c2 -> (c1+c2)-v
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(subExpr.left, "+", expr.right, subExpr.position),
|
BinaryExpression(subExpr.left, "+", expr.right, subExpr.position),
|
||||||
@ -478,12 +411,12 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"+",
|
"+",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(expr.operator=="-" && subExpr.operator=="+") {
|
else if(expr.operator=="-" && subExpr.operator=="+") {
|
||||||
optimizationsDone++
|
|
||||||
if(leftIsConst) {
|
if(leftIsConst) {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// c1-(c2+v) -> (c1-c2)-v
|
// c1-(c2+v) -> (c1-c2)-v
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(expr.left, "-", subExpr.left, subExpr.position),
|
BinaryExpression(expr.left, "-", subExpr.left, subExpr.position),
|
||||||
@ -496,8 +429,9 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"-",
|
"-",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
} else {
|
} else {
|
||||||
return if(subleftIsConst) {
|
val change = if(subleftIsConst) {
|
||||||
// (c1+v)-c2 -> v+(c1-c2)
|
// (c1+v)-c2 -> v+(c1-c2)
|
||||||
BinaryExpression(
|
BinaryExpression(
|
||||||
BinaryExpression(subExpr.left, "-", expr.right, subExpr.position),
|
BinaryExpression(subExpr.left, "-", expr.right, subExpr.position),
|
||||||
@ -510,113 +444,12 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
|||||||
"+",
|
"+",
|
||||||
subExpr.left, expr.position)
|
subExpr.left, expr.position)
|
||||||
}
|
}
|
||||||
|
return IAstModification.ReplaceNode(expr, change, expr.parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
|
||||||
|
|
||||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
|
||||||
val newFrom: NumericLiteralValue
|
|
||||||
val newTo: NumericLiteralValue
|
|
||||||
try {
|
|
||||||
newFrom = rangeFrom.cast(targetDt)
|
|
||||||
newTo = rangeTo.cast(targetDt)
|
|
||||||
} catch (x: ExpressionError) {
|
|
||||||
return range
|
|
||||||
}
|
|
||||||
val newStep: Expression = try {
|
|
||||||
stepLiteral?.cast(targetDt)?: range.step
|
|
||||||
} catch(ee: ExpressionError) {
|
|
||||||
range.step
|
|
||||||
}
|
|
||||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
val forLoop2 = super.visit(forLoop) as ForLoop
|
|
||||||
|
|
||||||
// check if we need to adjust an array literal to the loop variable's datatype
|
|
||||||
val array = forLoop2.iterable as? ArrayLiteralValue
|
|
||||||
if(array!=null) {
|
|
||||||
val loopvarDt: DataType = when {
|
|
||||||
forLoop.loopVar!=null -> forLoop.loopVar!!.inferType(program).typeOrElse(DataType.UBYTE)
|
|
||||||
forLoop.loopRegister!=null -> DataType.UBYTE
|
|
||||||
else -> throw FatalAstException("weird for loop")
|
|
||||||
}
|
|
||||||
|
|
||||||
val arrayType = when(loopvarDt) {
|
|
||||||
DataType.UBYTE -> DataType.ARRAY_UB
|
|
||||||
DataType.BYTE -> DataType.ARRAY_B
|
|
||||||
DataType.UWORD -> DataType.ARRAY_UW
|
|
||||||
DataType.WORD -> DataType.ARRAY_W
|
|
||||||
DataType.FLOAT -> DataType.ARRAY_F
|
|
||||||
else -> throw FatalAstException("invalid array elt type")
|
|
||||||
}
|
|
||||||
val array2 = array.cast(arrayType)
|
|
||||||
if(array2!=null && array2!==array) {
|
|
||||||
forLoop2.iterable = array2
|
|
||||||
array2.linkParents(forLoop2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
|
||||||
val iterableRange = forLoop2.iterable as? RangeExpr ?: return forLoop2
|
|
||||||
val rangeFrom = iterableRange.from as? NumericLiteralValue
|
|
||||||
val rangeTo = iterableRange.to as? NumericLiteralValue
|
|
||||||
if(rangeFrom==null || rangeTo==null) return forLoop2
|
|
||||||
|
|
||||||
val loopvar = forLoop2.loopVar?.targetVarDecl(program.namespace)
|
|
||||||
if(loopvar!=null) {
|
|
||||||
val stepLiteral = iterableRange.step as? NumericLiteralValue
|
|
||||||
when(loopvar.datatype) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
if(rangeFrom.type!= DataType.UBYTE) {
|
|
||||||
// attempt to translate the iterable into ubyte values
|
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
if(rangeFrom.type!= DataType.BYTE) {
|
|
||||||
// attempt to translate the iterable into byte values
|
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
if(rangeFrom.type!= DataType.UWORD) {
|
|
||||||
// attempt to translate the iterable into uword values
|
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
if(rangeFrom.type!= DataType.WORD) {
|
|
||||||
// attempt to translate the iterable into word values
|
|
||||||
forLoop2.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return forLoop2
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
|
||||||
// because constant folding can result in arrays that are now suddenly capable
|
|
||||||
// of telling the type of all their elements (for instance, when they contained -2 which
|
|
||||||
// was a prefix expression earlier), we recalculate the array's datatype.
|
|
||||||
val array = super.visit(arrayLiteral)
|
|
||||||
if(array is ArrayLiteralValue) {
|
|
||||||
if(array.type.isKnown)
|
|
||||||
return array
|
|
||||||
val arrayDt = array.guessDatatype(program)
|
|
||||||
if(arrayDt.isKnown) {
|
|
||||||
val newArray = arrayLiteral.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
|
||||||
if(newArray!=null)
|
|
||||||
return newArray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
192
compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
192
compiler/src/prog8/optimizer/ConstantIdentifierReplacer.kt
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.ArrayIndex
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
|
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val declConstValue = decl.value?.constValue(program)
|
||||||
|
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||||
|
&& !declConstValue.inferType(program).istype(decl.datatype)) {
|
||||||
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
|
val cast = declConstValue.cast(decl.datatype)
|
||||||
|
if(cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Replace all constant identifiers with their actual value,
|
||||||
|
// and the array var initializer values and sizes.
|
||||||
|
// This is needed because further constant optimizations depend on those.
|
||||||
|
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
|
// replace identifiers that refer to const value, with the value itself
|
||||||
|
// if it's a simple type and if it's not a left hand side variable
|
||||||
|
if(identifier.parent is AssignTarget)
|
||||||
|
return noModifications
|
||||||
|
var forloop = identifier.parent as? ForLoop
|
||||||
|
if(forloop==null)
|
||||||
|
forloop = identifier.parent.parent as? ForLoop
|
||||||
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
val cval = identifier.constValue(program) ?: return noModifications
|
||||||
|
return when (cval.type) {
|
||||||
|
in NumericDatatypes -> listOf(IAstModification.ReplaceNode(identifier, NumericLiteralValue(cval.type, cval.number, identifier.position), identifier.parent))
|
||||||
|
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
||||||
|
else -> noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
|
// TODO: use call graph for this?
|
||||||
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) {
|
||||||
|
errors.err("recursive var declaration", decl.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) {
|
||||||
|
if(decl.isArray){
|
||||||
|
if(decl.arraysize==null) {
|
||||||
|
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
||||||
|
val arrayval = decl.value as? ArrayLiteralValue
|
||||||
|
if(arrayval!=null) {
|
||||||
|
return listOf(IAstModification.SetExpression(
|
||||||
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
|
||||||
|
decl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(decl.arraysize?.constIndex()==null) {
|
||||||
|
val size = decl.arraysize!!.index.constValue(program)
|
||||||
|
if(size!=null) {
|
||||||
|
return listOf(IAstModification.SetExpression(
|
||||||
|
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
||||||
|
size, decl
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when(decl.datatype) {
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
// vardecl: for scalar float vars, promote constant integer initialization values to floats
|
||||||
|
val litval = decl.value as? NumericLiteralValue
|
||||||
|
if (litval!=null && litval.type in IntegerDatatypes) {
|
||||||
|
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number.toDouble(), litval.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
val numericLv = decl.value as? NumericLiteralValue
|
||||||
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
|
if(rangeExpr!=null) {
|
||||||
|
// convert the initializer range expression to an actual array
|
||||||
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
|
if(constRange!=null) {
|
||||||
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
|
val newValue = if(eltType in ByteDatatypes) {
|
||||||
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
|
constRange.map { NumericLiteralValue(eltType, it.toShort(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
} else {
|
||||||
|
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
||||||
|
constRange.map { NumericLiteralValue(eltType, it, decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||||
|
errors.err("arraysize requires only integers here", numericLv.position)
|
||||||
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
|
val fillvalue = numericLv.number.toInt()
|
||||||
|
when(decl.datatype){
|
||||||
|
DataType.ARRAY_UB -> {
|
||||||
|
if(fillvalue !in 0..255)
|
||||||
|
errors.err("ubyte value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B -> {
|
||||||
|
if(fillvalue !in -128..127)
|
||||||
|
errors.err("byte value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW -> {
|
||||||
|
if(fillvalue !in 0..65535)
|
||||||
|
errors.err("uword value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_W -> {
|
||||||
|
if(fillvalue !in -32768..32767)
|
||||||
|
errors.err("word value overflow", numericLv.position)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// create the array itself, filled with the fillvalue.
|
||||||
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||||
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
|
val litval = decl.value as? NumericLiteralValue
|
||||||
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
|
if(rangeExpr!=null) {
|
||||||
|
// convert the initializer range expression to an actual array of floats
|
||||||
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
|
if(constRange!=null) {
|
||||||
|
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||||
|
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||||
|
position = decl.value!!.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rangeExpr==null && litval!=null) {
|
||||||
|
// arraysize initializer is a single int, and we know the size.
|
||||||
|
val fillvalue = litval.number.toDouble()
|
||||||
|
if (fillvalue < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
||||||
|
errors.err("float value overflow", litval.position)
|
||||||
|
else {
|
||||||
|
// create the array itself, filled with the fillvalue.
|
||||||
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) }.toTypedArray<Expression>()
|
||||||
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// nothing to do for this type
|
||||||
|
// this includes strings and structs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,6 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@ -14,12 +13,6 @@ import kotlin.math.pow
|
|||||||
/*
|
/*
|
||||||
todo add more expression optimizations
|
todo add more expression optimizations
|
||||||
|
|
||||||
x + x -> x << 1 (for words... for bytes too?)
|
|
||||||
x + x + x + x -> x << 2 (for words... for bytes too?)
|
|
||||||
x + x + x -> ???? x*3 ??? words/bytes?
|
|
||||||
x - x -> 0
|
|
||||||
|
|
||||||
|
|
||||||
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@ -28,12 +21,7 @@ import kotlin.math.pow
|
|||||||
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
|
||||||
if (assignment.aug_op != null)
|
|
||||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
@ -42,20 +30,23 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val literal = typecast.expression as? NumericLiteralValue
|
val literal = typecast.expression as? NumericLiteralValue
|
||||||
if (literal != null) {
|
if (literal != null) {
|
||||||
val newLiteral = literal.cast(typecast.type)
|
val newLiteral = literal.cast(typecast.type)
|
||||||
if (newLiteral !== literal)
|
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
||||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast)
|
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove redundant nested typecasts:
|
// remove redundant nested typecasts
|
||||||
// if the typecast casts a value to the same type, remove the cast.
|
|
||||||
// if the typecast contains another typecast, remove the inner typecast.
|
|
||||||
val subTypecast = typecast.expression as? TypecastExpression
|
val subTypecast = typecast.expression as? TypecastExpression
|
||||||
if (subTypecast != null) {
|
if (subTypecast != null) {
|
||||||
|
// remove the sub-typecast if its datatype is larger than the outer typecast
|
||||||
|
if(subTypecast.type largerThan typecast.type) {
|
||||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (typecast.expression.inferType(program).istype(typecast.type))
|
if (typecast.expression.inferType(program).istype(typecast.type)) {
|
||||||
|
// remove duplicate cast
|
||||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
@ -88,10 +79,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (newExpr != null)
|
if (newExpr != null)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
}
|
}
|
||||||
else -> return emptyList()
|
else -> return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
@ -103,7 +94,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||||
|
|
||||||
|
|
||||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||||
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
||||||
return listOf(IAstModification.SwapOperands(expr))
|
return listOf(IAstModification.SwapOperands(expr))
|
||||||
@ -180,6 +170,42 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(expr.operator == ">=" && rightVal?.number == 0) {
|
||||||
|
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||||
|
// unsigned >= 0 --> true
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(true, expr.position), parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(expr.operator == "<" && rightVal?.number == 0) {
|
||||||
|
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
|
||||||
|
// unsigned < 0 --> false
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(false, expr.position), parent))
|
||||||
|
}
|
||||||
|
when(leftDt) {
|
||||||
|
DataType.BYTE -> {
|
||||||
|
// signed < 0 --> signed & $80
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
BinaryExpression(expr.left, "&", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
// signedw < 0 --> msb(signedw) & $80
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
BinaryExpression(FunctionCall(IdentifierReference(listOf("msb"), expr.position),
|
||||||
|
mutableListOf(expr.left),
|
||||||
|
expr.position
|
||||||
|
), "&", NumericLiteralValue.optimalInteger(0x80, expr.position), expr.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// simplify when a term is constant and directly determines the outcome
|
// simplify when a term is constant and directly determines the outcome
|
||||||
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
||||||
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
||||||
@ -246,7 +272,50 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if(newExpr != null)
|
if(newExpr != null)
|
||||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
|
||||||
return emptyList()
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCall.target.nameInSource == listOf("lsb")) {
|
||||||
|
val arg = functionCall.args[0]
|
||||||
|
if(arg is TypecastExpression) {
|
||||||
|
val valueDt = arg.expression.inferType(program)
|
||||||
|
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
||||||
|
// useless lsb() of byte value that was casted to word
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val argDt = arg.inferType(program)
|
||||||
|
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
||||||
|
// useless lsb() of byte value
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(functionCall.target.nameInSource == listOf("msb")) {
|
||||||
|
val arg = functionCall.args[0]
|
||||||
|
if(arg is TypecastExpression) {
|
||||||
|
val valueDt = arg.expression.inferType(program)
|
||||||
|
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
||||||
|
// useless msb() of byte value that was casted to word, replace with 0
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
functionCall,
|
||||||
|
NumericLiteralValue(valueDt.typeOrElse(DataType.UBYTE), 0, arg.expression.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val argDt = arg.inferType(program)
|
||||||
|
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
||||||
|
// useless msb() of byte value, replace with 0
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
functionCall,
|
||||||
|
NumericLiteralValue(argDt.typeOrElse(DataType.UBYTE), 0, arg.position),
|
||||||
|
parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
@ -538,10 +607,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
} else if (amount >= 8) {
|
} else if (amount >= 8) {
|
||||||
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||||
if (amount == 8) {
|
if (amount == 8) {
|
||||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position)
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||||
}
|
}
|
||||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position)
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -558,8 +627,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
return expr.left
|
return expr.left
|
||||||
}
|
}
|
||||||
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
when (targetDt) {
|
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
@ -5,12 +5,32 @@ import prog8.ast.base.ErrorReporter
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||||
optimizer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
while(errors.isEmpty() && optimizer.optimizationsDone>0) {
|
val replacer = ConstantIdentifierReplacer(this, errors)
|
||||||
optimizer.optimizationsDone = 0
|
replacer.visit(this)
|
||||||
|
if (errors.isEmpty()) {
|
||||||
|
replacer.applyModifications()
|
||||||
|
|
||||||
|
valuetypefixer.visit(this)
|
||||||
|
if(errors.isEmpty()) {
|
||||||
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
|
val optimizer = ConstantFoldingOptimizer(this)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
|
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
||||||
|
optimizer.visit(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.isEmpty()) {
|
||||||
|
replacer.visit(this)
|
||||||
|
replacer.applyModifications()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.isEmpty())
|
if(errors.isEmpty())
|
||||||
@ -21,9 +41,11 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
||||||
val optimizer = StatementOptimizer(this, errors)
|
val optimizer = StatementOptimizer(this, errors)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
||||||
|
|
||||||
return optimizer.optimizationsDone
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyExpressions() : Int {
|
internal fun Program.simplifyExpressions() : Int {
|
||||||
@ -31,3 +53,9 @@ internal fun Program.simplifyExpressions() : Int {
|
|||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.splitBinaryExpressions() : Int {
|
||||||
|
val opti = BinExprSplitter(this)
|
||||||
|
opti.visit(this)
|
||||||
|
return opti.applyModifications()
|
||||||
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
package prog8.optimizer
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.NopStatement
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
|
|
||||||
internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor {
|
|
||||||
private var scopesToFlatten = mutableListOf<INameScope>()
|
|
||||||
private val nopStatements = mutableListOf<NopStatement>()
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
super.visit(program)
|
|
||||||
for(scope in scopesToFlatten.reversed()) {
|
|
||||||
val namescope = scope.parent as INameScope
|
|
||||||
val idx = namescope.statements.indexOf(scope as Statement)
|
|
||||||
if(idx>=0) {
|
|
||||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
|
||||||
nop.parent = namescope as Node
|
|
||||||
namescope.statements[idx] = nop
|
|
||||||
namescope.statements.addAll(idx, scope.statements)
|
|
||||||
scope.statements.forEach { it.parent = namescope }
|
|
||||||
visit(nop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nopStatements.forEach {
|
|
||||||
it.definingScope().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope) {
|
|
||||||
if(scope.parent is INameScope) {
|
|
||||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
nopStatements.add(nopStatement)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +1,86 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: remove unreachable code after return and exit()
|
|
||||||
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program,
|
internal class StatementOptimizer(private val program: Program,
|
||||||
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
private val errors: ErrorReporter) : AstWalker() {
|
||||||
var optimizationsDone: Int = 0
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
private val noModifications = emptyList<IAstModification>()
|
||||||
private val callgraph = CallGraph(program)
|
private val callgraph = CallGraph(program)
|
||||||
private val vardeclsToRemove = mutableListOf<VarDecl>()
|
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(program)
|
|
||||||
|
|
||||||
for(decl in vardeclsToRemove) {
|
|
||||||
decl.definingScope().remove(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block): Statement {
|
|
||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
optimizationsDone++
|
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
errors.warn("removing empty block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block)
|
return listOf(IAstModification.Remove(block, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
if (block !in callgraph.usedSymbols) {
|
||||||
optimizationsDone++
|
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block) // remove unused block
|
return listOf(IAstModification.Remove(block, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
return super.visit(block)
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
|
||||||
super.visit(subroutine)
|
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
if(subroutine.asmAddress==null && !forceOutput) {
|
||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
val removals = callgraph.calledBy.getValue(subroutine).map {
|
||||||
return NopStatement.insteadOf(subroutine)
|
IAstModification.Remove(it, it.parent)
|
||||||
|
}.toMutableList()
|
||||||
|
removals += IAstModification.Remove(subroutine, parent)
|
||||||
|
return removals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val linesToRemove = deduplicateAssignments(subroutine.statements)
|
|
||||||
if(linesToRemove.isNotEmpty()) {
|
|
||||||
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(subroutine, parent))
|
||||||
return NopStatement.insteadOf(subroutine)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return subroutine
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||||
if(decl.type == VarDeclType.VAR)
|
if(decl.type == VarDeclType.VAR)
|
||||||
errors.warn("removing unused variable ${decl.type} '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(decl)
|
return listOf(IAstModification.Remove(decl, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(decl)
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
// removes 'duplicate' assignments that assign the isSameAs target
|
|
||||||
val linesToRemove = mutableListOf<Int>()
|
|
||||||
var previousAssignmentLine: Int? = null
|
|
||||||
for (i in statements.indices) {
|
|
||||||
val stmt = statements[i] as? Assignment
|
|
||||||
if (stmt != null && stmt.value is NumericLiteralValue) {
|
|
||||||
if (previousAssignmentLine == null) {
|
|
||||||
previousAssignmentLine = i
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
val prev = statements[previousAssignmentLine] as Assignment
|
|
||||||
if (prev.target.isSameAs(stmt.target, program)) {
|
|
||||||
// get rid of the previous assignment, if the target is not MEMORY
|
|
||||||
if (prev.target.isNotMemory(program.namespace))
|
|
||||||
linesToRemove.add(previousAssignmentLine)
|
|
||||||
}
|
|
||||||
previousAssignmentLine = i
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
previousAssignmentLine = null
|
|
||||||
}
|
|
||||||
return linesToRemove
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in pureBuiltinFunctions) {
|
if (functionName in pureBuiltinFunctions) {
|
||||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||||
return NopStatement.insteadOf(functionCallStatement)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
|
||||||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||||
|
// this is a C-64 specific optimization
|
||||||
|
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
||||||
val arg = functionCallStatement.args.single()
|
val arg = functionCallStatement.args.single()
|
||||||
val stringVar: IdentifierReference?
|
val stringVar: IdentifierReference?
|
||||||
stringVar = if(arg is AddressOf) {
|
stringVar = if(arg is AddressOf) {
|
||||||
@ -136,85 +90,78 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
if(stringVar!=null) {
|
if(stringVar!=null) {
|
||||||
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
||||||
val string = vardecl.value!! as StringLiteralValue
|
val string = vardecl.value as? StringLiteralValue
|
||||||
if(string.value.length==1) {
|
if(string!=null) {
|
||||||
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
|
val pos = functionCallStatement.position
|
||||||
functionCallStatement.args.clear()
|
if (string.value.length == 1) {
|
||||||
functionCallStatement.args.add(NumericLiteralValue.optimalInteger(firstCharEncoded.toInt(), functionCallStatement.position))
|
val firstCharEncoded = CompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
||||||
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
val chrout = FunctionCallStatement(
|
||||||
vardeclsToRemove.add(vardecl)
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
optimizationsDone++
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||||
return functionCallStatement
|
functionCallStatement.void, pos
|
||||||
} else if(string.value.length==2) {
|
)
|
||||||
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||||
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
} else if (string.value.length == 2) {
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
val firstTwoCharsEncoded = CompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
||||||
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[0].toInt(), functionCallStatement.position)),
|
val chrout1 = FunctionCallStatement(
|
||||||
functionCallStatement.void, functionCallStatement.position))
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||||
mutableListOf(NumericLiteralValue.optimalInteger(firstTwoCharsEncoded[1].toInt(), functionCallStatement.position)),
|
functionCallStatement.void, pos
|
||||||
functionCallStatement.void, functionCallStatement.position))
|
)
|
||||||
vardeclsToRemove.add(vardecl)
|
val chrout2 = FunctionCallStatement(
|
||||||
optimizationsDone++
|
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||||
return scope
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||||
|
functionCallStatement.void, pos
|
||||||
|
)
|
||||||
|
val anonscope = AnonymousScope(mutableListOf(), pos)
|
||||||
|
anonscope.statements.add(chrout1)
|
||||||
|
anonscope.statements.add(chrout2)
|
||||||
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it calls a subroutine,
|
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
|
||||||
// if the first instruction in the subroutine is a return statement, replace with a nop instruction
|
|
||||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Jump && first.identifier!=null) {
|
if(first is Return)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||||
return FunctionCallStatement(first.identifier, functionCallStatement.args, functionCallStatement.void, functionCallStatement.position)
|
|
||||||
}
|
|
||||||
if(first is ReturnFromIrq || first is Return) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(functionCallStatement)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(functionCallStatement)
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
// if it calls a subroutine,
|
// if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value
|
||||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
|
||||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
|
||||||
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
val subroutine = functionCall.target.targetSubroutine(program.namespace)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Jump && first.identifier!=null) {
|
|
||||||
optimizationsDone++
|
|
||||||
return FunctionCall(first.identifier, functionCall.args, functionCall.position)
|
|
||||||
}
|
|
||||||
if(first is Return && first.value!=null) {
|
if(first is Return && first.value!=null) {
|
||||||
val constval = first.value?.constValue(program)
|
val constval = first.value?.constValue(program)
|
||||||
if(constval!=null)
|
if(constval!=null)
|
||||||
return constval
|
return listOf(IAstModification.ReplaceNode(functionCall, constval, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(functionCall)
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(ifStatement: IfStatement): Statement {
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(ifStatement)
|
// remove empty if statements
|
||||||
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars())
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
return listOf(IAstModification.Remove(ifStatement, parent))
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(ifStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// empty true part? switch with the else part
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
||||||
// invert the condition and move else part to true part
|
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
||||||
ifStatement.truepart = ifStatement.elsepart
|
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
||||||
ifStatement.elsepart = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
||||||
ifStatement.condition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
return listOf(
|
||||||
optimizationsDone++
|
IAstModification.ReplaceNode(ifStatement.condition, invertedCondition, ifStatement),
|
||||||
return ifStatement
|
IAstModification.ReplaceNode(ifStatement.truepart, truepart, ifStatement),
|
||||||
|
IAstModification.ReplaceNode(ifStatement.elsepart, emptyscope, ifStatement)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val constvalue = ifStatement.condition.constValue(program)
|
val constvalue = ifStatement.condition.constValue(program)
|
||||||
@ -222,115 +169,289 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only if-part
|
// always true -> keep only if-part
|
||||||
errors.warn("condition is always true", ifStatement.position)
|
errors.warn("condition is always true", ifStatement.position)
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.truepart, parent))
|
||||||
ifStatement.truepart
|
|
||||||
} else {
|
} else {
|
||||||
// always false -> keep only else-part
|
// always false -> keep only else-part
|
||||||
errors.warn("condition is always false", ifStatement.position)
|
errors.warn("condition is always false", ifStatement.position)
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(ifStatement, ifStatement.elsepart, parent))
|
||||||
ifStatement.elsepart
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ifStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop): Statement {
|
return noModifications
|
||||||
super.visit(forLoop)
|
}
|
||||||
|
|
||||||
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
if(forLoop.body.containsNoCodeNorVars()) {
|
if(forLoop.body.containsNoCodeNorVars()) {
|
||||||
// remove empty for loop
|
errors.warn("removing empty for loop", forLoop.position)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(forLoop, parent))
|
||||||
return NopStatement.insteadOf(forLoop)
|
|
||||||
} else if(forLoop.body.statements.size==1) {
|
} else if(forLoop.body.statements.size==1) {
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar?.nameInSource?.singleOrNull()) {
|
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||||
// remove empty for loop
|
// remove empty for loop (only loopvar decl in it)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(forLoop, parent))
|
||||||
return NopStatement.insteadOf(forLoop)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val range = forLoop.iterable as? RangeExpr
|
val range = forLoop.iterable as? RangeExpr
|
||||||
if(range!=null) {
|
if(range!=null) {
|
||||||
if(range.size()==1) {
|
if(range.size()==1) {
|
||||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||||
// loopvar/reg = range value , follow by block
|
// loopvar/reg = range value , follow by block
|
||||||
val assignment = Assignment(AssignTarget(forLoop.loopRegister, forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
forLoop.body.statements.add(0, assignment)
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), range.from, forLoop.position))
|
||||||
optimizationsDone++
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
return forLoop.body
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||||
|
if(iterable!=null) {
|
||||||
|
if(iterable.datatype==DataType.STR) {
|
||||||
|
val sv = iterable.value as StringLiteralValue
|
||||||
|
val size = sv.value.length
|
||||||
|
if(size==1) {
|
||||||
|
// loop over string of length 1 -> just assign the single character
|
||||||
|
val character = CompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0]
|
||||||
|
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||||
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||||
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(iterable.datatype in ArrayDatatypes) {
|
||||||
|
val size = iterable.arraysize!!.constIndex()
|
||||||
|
if(size==1) {
|
||||||
|
// loop over array of length 1 -> just assign the single value
|
||||||
|
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
||||||
|
if(av!=null) {
|
||||||
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
|
scope.statements.add(Assignment(
|
||||||
|
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
|
||||||
|
forLoop.position))
|
||||||
|
scope.statements.addAll(forLoop.body.statements)
|
||||||
|
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return forLoop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop): Statement {
|
return noModifications
|
||||||
super.visit(whileLoop)
|
}
|
||||||
|
|
||||||
|
override fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val constvalue = untilLoop.condition.constValue(program)
|
||||||
|
if(constvalue!=null) {
|
||||||
|
if(constvalue.asBooleanValue) {
|
||||||
|
// always true -> keep only the statement block (if there are no break statements)
|
||||||
|
errors.warn("condition is always true", untilLoop.condition.position)
|
||||||
|
if(!hasBreak(untilLoop.body))
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
|
||||||
|
} else {
|
||||||
|
// always false
|
||||||
|
val forever = RepeatLoop(null, untilLoop.body, untilLoop.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
val constvalue = whileLoop.condition.constValue(program)
|
val constvalue = whileLoop.condition.constValue(program)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue) {
|
||||||
// always true -> print a warning, and optimize into a forever-loop
|
// always true
|
||||||
errors.warn("condition is always true", whileLoop.condition.position)
|
val forever = RepeatLoop(null, whileLoop.body, whileLoop.position)
|
||||||
optimizationsDone++
|
listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
||||||
ForeverLoop(whileLoop.body, whileLoop.position)
|
|
||||||
} else {
|
} else {
|
||||||
// always false -> remove the while statement altogether
|
// always false -> remove the while statement altogether
|
||||||
errors.warn("condition is always false", whileLoop.condition.position)
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
optimizationsDone++
|
listOf(IAstModification.Remove(whileLoop, parent))
|
||||||
NopStatement.insteadOf(whileLoop)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return whileLoop
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop): Statement {
|
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
super.visit(repeatLoop)
|
val iter = repeatLoop.iterations
|
||||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
if(iter!=null) {
|
||||||
if(constvalue!=null) {
|
if(repeatLoop.body.containsNoCodeNorVars()) {
|
||||||
return if(constvalue.asBooleanValue){
|
errors.warn("empty loop removed", repeatLoop.position)
|
||||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||||
errors.warn("condition is always true", repeatLoop.untilCondition.position)
|
|
||||||
if(hasContinueOrBreak(repeatLoop.body))
|
|
||||||
repeatLoop
|
|
||||||
else {
|
|
||||||
optimizationsDone++
|
|
||||||
repeatLoop.body
|
|
||||||
}
|
}
|
||||||
} else {
|
val iterations = iter.constValue(program)?.number?.toInt()
|
||||||
// always false -> print a warning, and optimize into a forever loop
|
if (iterations == 0) {
|
||||||
errors.warn("condition is always false", repeatLoop.untilCondition.position)
|
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||||
optimizationsDone++
|
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||||
ForeverLoop(repeatLoop.body, repeatLoop.position)
|
}
|
||||||
|
if (iterations == 1) {
|
||||||
|
errors.warn("iterations is always 1", iter.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return repeatLoop
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement): Statement {
|
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val choices = whenStatement.choices.toList()
|
// remove empty choices
|
||||||
for(choice in choices) {
|
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
|
||||||
if(choice.statements.containsNoCodeNorVars())
|
override fun perform() {
|
||||||
whenStatement.choices.remove(choice)
|
whenStatement.choices.remove(choice)
|
||||||
}
|
}
|
||||||
return super.visit(whenStatement)
|
}
|
||||||
|
return whenStatement.choices
|
||||||
|
.filter { !it.statements.containsCodeOrVars() }
|
||||||
|
.map { ChoiceRemover(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
|
// if the jump is to the next statement, remove the jump
|
||||||
|
val scope = jump.definingScope()
|
||||||
|
val label = jump.identifier?.targetStatement(scope)
|
||||||
|
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||||
|
return listOf(IAstModification.Remove(jump, parent))
|
||||||
|
|
||||||
class Searcher: IAstModifyingVisitor
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if(binExpr!=null) {
|
||||||
|
if(binExpr.left isSameAs assignment.target) {
|
||||||
|
val rExpr = binExpr.right as? BinaryExpression
|
||||||
|
if(rExpr!=null) {
|
||||||
|
val op1 = binExpr.operator
|
||||||
|
val op2 = rExpr.operator
|
||||||
|
|
||||||
|
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
|
||||||
|
// associative operator, make sure the constant numeric value is second (right)
|
||||||
|
return listOf(IAstModification.SwapOperands(rExpr))
|
||||||
|
}
|
||||||
|
|
||||||
|
val rNum = (rExpr.right as? NumericLiteralValue)?.number
|
||||||
|
if(rNum!=null) {
|
||||||
|
if (op1 == "+" || op1 == "-") {
|
||||||
|
if (op2 == "+") {
|
||||||
|
// A = A +/- B + N
|
||||||
|
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||||
|
val addConstant = Assignment(
|
||||||
|
assignment.target,
|
||||||
|
BinaryExpression(binExpr.left, "+", rExpr.right, rExpr.position),
|
||||||
|
assignment.position
|
||||||
|
)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
|
IAstModification.InsertAfter(assignment, addConstant, parent))
|
||||||
|
} else if (op2 == "-") {
|
||||||
|
// A = A +/- B - N
|
||||||
|
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||||
|
val subConstant = Assignment(
|
||||||
|
assignment.target,
|
||||||
|
BinaryExpression(binExpr.left, "-", rExpr.right, rExpr.position),
|
||||||
|
assignment.position
|
||||||
|
)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
|
IAstModification.InsertAfter(assignment, subConstant, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(binExpr.operator in associativeOperators && binExpr.right isSameAs assignment.target) {
|
||||||
|
// associative operator, swap the operands so that the assignment target is first (left)
|
||||||
|
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||||
|
if (!(binExpr.left isSameAs binExpr.right))
|
||||||
|
return listOf(IAstModification.SwapOperands(binExpr))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignment.target isSameAs assignment.value) {
|
||||||
|
// remove assignment to self
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
val targetIDt = assignment.target.inferType(program, assignment)
|
||||||
|
if(!targetIDt.isKnown)
|
||||||
|
throw FatalAstException("can't infer type of assignment target")
|
||||||
|
|
||||||
|
// optimize binary expressions a bit
|
||||||
|
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
|
if(bexpr!=null) {
|
||||||
|
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
|
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
||||||
|
// assignments of the form: X = X <operator> <expr>
|
||||||
|
// remove assignments that have no effect (such as X=X+0)
|
||||||
|
// optimize/rewrite some other expressions
|
||||||
|
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
||||||
|
when (bexpr.operator) {
|
||||||
|
"+" -> {
|
||||||
|
if (rightCv == 0.0) {
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
|
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||||
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
repeat(rightCv.toInt()) {
|
||||||
|
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"-" -> {
|
||||||
|
if (rightCv == 0.0) {
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
|
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||||
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
|
repeat(rightCv.toInt()) {
|
||||||
|
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
||||||
|
}
|
||||||
|
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
"<<" -> {
|
||||||
|
if (rightCv == 0.0)
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
}
|
||||||
|
">>" -> {
|
||||||
|
if (rightCv == 0.0)
|
||||||
|
return listOf(IAstModification.Remove(assignment, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasBreak(scope: INameScope): Boolean {
|
||||||
|
|
||||||
|
class Searcher: IAstVisitor
|
||||||
{
|
{
|
||||||
var count=0
|
var count=0
|
||||||
|
|
||||||
override fun visit(breakStmt: Break): Statement {
|
override fun visit(breakStmt: Break) {
|
||||||
count++
|
count++
|
||||||
return super.visit(breakStmt)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(contStmt: Continue): Statement {
|
|
||||||
count++
|
|
||||||
return super.visit(contStmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val s=Searcher()
|
val s=Searcher()
|
||||||
for(stmt in scope.statements) {
|
for(stmt in scope.statements) {
|
||||||
stmt.accept(s)
|
stmt.accept(s)
|
||||||
@ -340,185 +461,4 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return s.count > 0
|
return s.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(jump: Jump): Statement {
|
|
||||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
|
||||||
if(subroutine!=null) {
|
|
||||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
|
||||||
if(first is Jump) {
|
|
||||||
optimizationsDone++
|
|
||||||
return first
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the jump is to the next statement, remove the jump
|
|
||||||
val scope = jump.definingScope()
|
|
||||||
val label = jump.identifier?.targetStatement(scope)
|
|
||||||
if(label!=null) {
|
|
||||||
if(scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(jump)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jump
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
if(assignment.aug_op!=null)
|
|
||||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
|
||||||
|
|
||||||
if(assignment.target isSameAs assignment.value) {
|
|
||||||
if(assignment.target.isNotMemory(program.namespace)) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val targetIDt = assignment.target.inferType(program, assignment)
|
|
||||||
if(!targetIDt.isKnown)
|
|
||||||
throw FatalAstException("can't infer type of assignment target")
|
|
||||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
|
||||||
if(bexpr!=null) {
|
|
||||||
val cv = bexpr.right.constValue(program)?.number?.toDouble()
|
|
||||||
if (cv == null) {
|
|
||||||
if (bexpr.operator == "+" && targetDt != DataType.FLOAT) {
|
|
||||||
if (bexpr.left isSameAs bexpr.right && assignment.target isSameAs bexpr.left) {
|
|
||||||
bexpr.operator = "*"
|
|
||||||
bexpr.right = NumericLiteralValue.optimalInteger(2, assignment.value.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (assignment.target isSameAs bexpr.left) {
|
|
||||||
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
|
||||||
// A = A <operator> B
|
|
||||||
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type
|
|
||||||
|
|
||||||
when (bexpr.operator) {
|
|
||||||
"+" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
|
||||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
|
||||||
// replace by several INCs (a bit less when dealing with memory targets)
|
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
repeat(cv.toInt()) {
|
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
|
||||||
}
|
|
||||||
return decs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"-" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
|
||||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
|
||||||
// replace by several DECs (a bit less when dealing with memory targets)
|
|
||||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
repeat(cv.toInt()) {
|
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
|
||||||
}
|
|
||||||
return decs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"*" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"/" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"**" -> if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"|" -> if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"^" -> if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
"<<" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
if (((targetDt == DataType.UWORD || targetDt == DataType.WORD) && cv > 15.0) ||
|
|
||||||
((targetDt == DataType.UBYTE || targetDt == DataType.BYTE) && cv > 7.0)) {
|
|
||||||
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
|
||||||
assignment.value.linkParents(assignment)
|
|
||||||
optimizationsDone++
|
|
||||||
} else {
|
|
||||||
// replace by in-place lsl(...) call
|
|
||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
var numshifts = cv.toInt()
|
|
||||||
while (numshifts > 0) {
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position),
|
|
||||||
mutableListOf(bexpr.left), true, assignment.position))
|
|
||||||
numshifts--
|
|
||||||
}
|
|
||||||
optimizationsDone++
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
">>" -> {
|
|
||||||
if (cv == 0.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NopStatement.insteadOf(assignment)
|
|
||||||
}
|
|
||||||
if ((targetDt == DataType.UWORD && cv > 15.0) || (targetDt == DataType.UBYTE && cv > 7.0)) {
|
|
||||||
assignment.value = NumericLiteralValue.optimalInteger(0, assignment.value.position)
|
|
||||||
assignment.value.linkParents(assignment)
|
|
||||||
optimizationsDone++
|
|
||||||
} else {
|
|
||||||
// replace by in-place lsr(...) call
|
|
||||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
|
||||||
var numshifts = cv.toInt()
|
|
||||||
while (numshifts > 0) {
|
|
||||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position),
|
|
||||||
mutableListOf(bexpr.left), true, assignment.position))
|
|
||||||
numshifts--
|
|
||||||
}
|
|
||||||
optimizationsDone++
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return super.visit(assignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope): Statement {
|
|
||||||
val linesToRemove = deduplicateAssignments(scope.statements)
|
|
||||||
if(linesToRemove.isNotEmpty()) {
|
|
||||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
|
||||||
}
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(label: Label): Statement {
|
|
||||||
// remove duplicate labels
|
|
||||||
val stmts = label.definingScope().statements
|
|
||||||
val startIdx = stmts.indexOf(label)
|
|
||||||
if(startIdx< stmts.lastIndex && stmts[startIdx+1] == label)
|
|
||||||
return NopStatement.insteadOf(label)
|
|
||||||
|
|
||||||
return super.visit(label)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,55 +1,101 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.ast.statements.Block
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
internal class UnusedCodeRemover: IAstModifyingVisitor {
|
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() {
|
||||||
|
|
||||||
|
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
||||||
override fun visit(program: Program) {
|
|
||||||
val callgraph = CallGraph(program)
|
val callgraph = CallGraph(program)
|
||||||
|
val removals = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
// remove all subroutines that aren't called, or are empty
|
// remove all subroutines that aren't called, or are empty
|
||||||
val removeSubroutines = mutableSetOf<Subroutine>()
|
|
||||||
val entrypoint = program.entrypoint()
|
val entrypoint = program.entrypoint()
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
callgraph.forAllSubroutines(it) { sub ->
|
||||||
if (sub !== entrypoint && !sub.keepAlways && (sub.calledBy.isEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||||
removeSubroutines.add(sub)
|
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeSubroutines.isNotEmpty()) {
|
|
||||||
removeSubroutines.forEach {
|
|
||||||
it.definingScope().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val removeBlocks = mutableSetOf<Block>()
|
|
||||||
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
||||||
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
||||||
removeBlocks.add(block)
|
removals.add(IAstModification.Remove(block, block.definingScope() as Node))
|
||||||
}
|
|
||||||
|
|
||||||
if (removeBlocks.isNotEmpty()) {
|
|
||||||
removeBlocks.forEach { it.definingScope().remove(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove modules that are not imported, or are empty (unless it's a library modules)
|
// remove modules that are not imported, or are empty (unless it's a library modules)
|
||||||
val removeModules = mutableSetOf<Module>()
|
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
||||||
removeModules.add(it)
|
removals.add(IAstModification.Remove(it, it.parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeModules.isNotEmpty()) {
|
return removals
|
||||||
program.modules.removeAll(removeModules)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visit(program)
|
|
||||||
|
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||||
|
reportUnreachable(breakStmt, parent as INameScope)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
|
reportUnreachable(jump, parent as INameScope)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
|
reportUnreachable(returnStmt, parent as INameScope)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||||
|
reportUnreachable(functionCallStatement, parent as INameScope)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
||||||
|
when(val next = parent.nextSibling(stmt)) {
|
||||||
|
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is StructDecl -> {}
|
||||||
|
else -> errors.warn("unreachable code", next.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
val removeDoubleAssignments = deduplicateAssignments(scope.statements)
|
||||||
|
return removeDoubleAssignments.map { IAstModification.Remove(it, scope) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
|
val removeDoubleAssignments = deduplicateAssignments(block.statements)
|
||||||
|
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
||||||
|
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
|
||||||
|
// removes 'duplicate' assignments that assign the same target directly after another
|
||||||
|
val linesToRemove = mutableListOf<Assignment>()
|
||||||
|
|
||||||
|
for (stmtPairs in statements.windowed(2, step = 1)) {
|
||||||
|
val assign1 = stmtPairs[0] as? Assignment
|
||||||
|
val assign2 = stmtPairs[1] as? Assignment
|
||||||
|
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
||||||
|
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace))
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return linesToRemove
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import org.antlr.v4.runtime.*
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.toAst
|
import prog8.ast.antlr.toAst
|
||||||
import prog8.ast.base.ErrorReporter
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.base.checkImportedValid
|
import prog8.ast.base.checkImportedValid
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.statements.DirectiveArg
|
import prog8.ast.statements.DirectiveArg
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.pathFrom
|
import prog8.pathFrom
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -20,21 +20,13 @@ import java.nio.file.Paths
|
|||||||
internal class ParsingFailedError(override var message: String) : Exception(message)
|
internal class ParsingFailedError(override var message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
private class LexerErrorListener: BaseErrorListener() {
|
|
||||||
var numberOfErrors: Int = 0
|
|
||||||
override fun syntaxError(p0: Recognizer<*, *>?, p1: Any?, p2: Int, p3: Int, p4: String?, p5: RecognitionException?) {
|
|
||||||
numberOfErrors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
|
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
|
||||||
|
|
||||||
|
|
||||||
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||||
|
|
||||||
|
|
||||||
internal class ModuleImporter(private val errors: ErrorReporter) {
|
internal class ModuleImporter {
|
||||||
|
|
||||||
internal fun importModule(program: Program, filePath: Path): Module {
|
internal fun importModule(program: Program, filePath: Path): Module {
|
||||||
print("importing '${moduleName(filePath.fileName)}'")
|
print("importing '${moduleName(filePath.fileName)}'")
|
||||||
@ -61,13 +53,28 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
|||||||
return executeImportDirective(program, import, Paths.get(""))
|
return executeImportDirective(program, import, Paths.get(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MyErrorListener: ConsoleErrorListener() {
|
||||||
|
var numberOfErrors: Int = 0
|
||||||
|
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
|
||||||
|
numberOfErrors++
|
||||||
|
when (recognizer) {
|
||||||
|
is CustomLexer -> System.err.println("${recognizer.modulePath}:$line:$charPositionInLine: $msg")
|
||||||
|
is prog8Parser -> System.err.println("${recognizer.inputStream.sourceName}:$line:$charPositionInLine: $msg")
|
||||||
|
else -> System.err.println("$line:$charPositionInLine $msg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||||
val moduleName = moduleName(modulePath.fileName)
|
val moduleName = moduleName(modulePath.fileName)
|
||||||
val lexer = CustomLexer(modulePath, stream)
|
val lexer = CustomLexer(modulePath, stream)
|
||||||
val lexerErrors = LexerErrorListener()
|
lexer.removeErrorListeners()
|
||||||
|
val lexerErrors = MyErrorListener()
|
||||||
lexer.addErrorListener(lexerErrors)
|
lexer.addErrorListener(lexerErrors)
|
||||||
val tokens = CommentHandlingTokenStream(lexer)
|
val tokens = CommentHandlingTokenStream(lexer)
|
||||||
val parser = prog8Parser(tokens)
|
val parser = prog8Parser(tokens)
|
||||||
|
parser.removeErrorListeners()
|
||||||
|
parser.addErrorListener(MyErrorListener())
|
||||||
val parseTree = parser.module()
|
val parseTree = parser.module()
|
||||||
val numberOfErrors = parser.numberOfSyntaxErrors + lexerErrors.numberOfErrors
|
val numberOfErrors = parser.numberOfSyntaxErrors + lexerErrors.numberOfErrors
|
||||||
if(numberOfErrors > 0)
|
if(numberOfErrors > 0)
|
||||||
@ -95,7 +102,7 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
|||||||
|
|
||||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||||
val fileName = "$name.p8"
|
val fileName = "$name.p8"
|
||||||
val locations = mutableListOf(source.parent)
|
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
|
||||||
|
|
||||||
val propPath = System.getProperty("prog8.libdir")
|
val propPath = System.getProperty("prog8.libdir")
|
||||||
if(propPath!=null)
|
if(propPath!=null)
|
||||||
@ -110,7 +117,7 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
|||||||
if (Files.isReadable(file)) return file
|
if (Files.isReadable(file)) return file
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: $locations)")
|
throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: embedded libs and $locations)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? {
|
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? {
|
||||||
@ -124,16 +131,14 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
|||||||
if(existing!=null)
|
if(existing!=null)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val resource = tryGetEmbeddedResource("$moduleName.p8")
|
val rsc = tryGetEmbeddedResource("$moduleName.p8")
|
||||||
val importedModule =
|
val importedModule =
|
||||||
if(resource!=null) {
|
if(rsc!=null) {
|
||||||
// load the module from the embedded resource
|
// load the module from the embedded resource
|
||||||
|
val (resource, resourcePath) = rsc
|
||||||
resource.use {
|
resource.use {
|
||||||
if(import.args[0].int==42)
|
|
||||||
println("importing '$moduleName' (library, auto)")
|
|
||||||
else
|
|
||||||
println("importing '$moduleName' (library)")
|
println("importing '$moduleName' (library)")
|
||||||
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
|
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$resourcePath"), true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val modulePath = discoverImportedModuleFile(moduleName, source, import.position)
|
val modulePath = discoverImportedModuleFile(moduleName, source, import.position)
|
||||||
@ -144,7 +149,18 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
|||||||
return importedModule
|
return importedModule
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryGetEmbeddedResource(name: String): InputStream? {
|
private fun tryGetEmbeddedResource(name: String): Pair<InputStream, String>? {
|
||||||
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
val target = CompilationTarget.instance.name
|
||||||
|
val targetSpecificPath = "/prog8lib/$target/$name"
|
||||||
|
val targetSpecificResource = object{}.javaClass.getResourceAsStream(targetSpecificPath)
|
||||||
|
if(targetSpecificResource!=null)
|
||||||
|
return Pair(targetSpecificResource, targetSpecificPath)
|
||||||
|
|
||||||
|
val generalPath = "/prog8lib/$name"
|
||||||
|
val generalResource = object{}.javaClass.getResourceAsStream(generalPath)
|
||||||
|
if(generalResource!=null)
|
||||||
|
return Pair(generalResource, generalPath)
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,21 @@ import org.hamcrest.Matchers.closeTo
|
|||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.Module
|
||||||
import prog8.ast.base.ErrorReporter
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
|
import java.nio.file.Path
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@ -123,13 +126,13 @@ class TestCompiler {
|
|||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestZeropage {
|
class TestC64Zeropage {
|
||||||
|
|
||||||
private val errors = ErrorReporter()
|
private val errors = ErrorReporter()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||||
|
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
@ -142,37 +145,37 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate("", DataType.FLOAT, null, errors)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
||||||
zp3.allocate("", DataType.FLOAT, null, errors)
|
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpModesWithFloats() {
|
fun testZpModesWithFloats() {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false))
|
||||||
}
|
}
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpDontuse() {
|
fun testZpDontuse() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
@ -182,19 +185,19 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFreeSpaces() {
|
fun testFreeSpaces() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp1.available())
|
assertEquals(16, zp1.available())
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
||||||
assertEquals(91, zp2.available())
|
assertEquals(89, zp2.available())
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
||||||
assertEquals(125, zp3.available())
|
assertEquals(125, zp3.available())
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||||
assertEquals(238, zp4.available())
|
assertEquals(238, zp4.available())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReservedSpace() {
|
fun testReservedSpace() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||||
assertEquals(238, zp1.available())
|
assertEquals(238, zp1.available())
|
||||||
assertTrue(50 in zp1.free)
|
assertTrue(50 in zp1.free)
|
||||||
assertTrue(100 in zp1.free)
|
assertTrue(100 in zp1.free)
|
||||||
@ -203,7 +206,7 @@ class TestZeropage {
|
|||||||
assertTrue(200 in zp1.free)
|
assertTrue(200 in zp1.free)
|
||||||
assertTrue(255 in zp1.free)
|
assertTrue(255 in zp1.free)
|
||||||
assertTrue(199 in zp1.free)
|
assertTrue(199 in zp1.free)
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false))
|
||||||
assertEquals(139, zp2.available())
|
assertEquals(139, zp2.available())
|
||||||
assertFalse(50 in zp2.free)
|
assertFalse(50 in zp2.free)
|
||||||
assertFalse(100 in zp2.free)
|
assertFalse(100 in zp2.free)
|
||||||
@ -216,7 +219,7 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBasicsafeAllocation() {
|
fun testBasicsafeAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(16, zp.available())
|
||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
@ -239,7 +242,7 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFullAllocation() {
|
fun testFullAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||||
assertEquals(238, zp.available())
|
assertEquals(238, zp.available())
|
||||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||||
assertTrue(loc > 3)
|
assertTrue(loc > 3)
|
||||||
@ -269,7 +272,7 @@ class TestZeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(16, zp.available())
|
||||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
@ -379,3 +382,169 @@ class TestPetscii {
|
|||||||
assertFalse(abc!=abc)
|
assertFalse(abc!=abc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestMemory {
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_memory_addresses() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
|
||||||
|
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
|
||||||
|
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
var scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertTrue(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertTrue(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertTrue(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertTrue(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertTrue(target.isInRegularRAM(scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNotInValidRamC64_memory_addresses() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
|
||||||
|
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
|
||||||
|
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
var scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertFalse(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertFalse(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertFalse(target.isInRegularRAM(scope))
|
||||||
|
|
||||||
|
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
|
||||||
|
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertFalse(target.isInRegularRAM(scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_memory_identifiers() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
|
||||||
|
assertFalse(target.isInRegularRAM(target.definingScope()))
|
||||||
|
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.CONST)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.CONST)
|
||||||
|
assertFalse(target.isInRegularRAM(target.definingScope()))
|
||||||
|
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.MEMORY)
|
||||||
|
assertFalse(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
|
||||||
|
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
|
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||||
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_memory_expression() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
|
||||||
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
|
val scope = AnonymousScope(mutableListOf(), Position.DUMMY)
|
||||||
|
assertFalse(target.isInRegularRAM(scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_variable() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_memmap_variable() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val address = 0x1000
|
||||||
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNotInValidRamC64_memmap_variable() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val address = 0xd020
|
||||||
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertFalse(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_array() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
||||||
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInValidRamC64_array_memmapped() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val address = 0x1000
|
||||||
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertTrue(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNotValidRamC64_array_memmapped() {
|
||||||
|
CompilationTarget.instance = C64Target
|
||||||
|
val address = 0xe000
|
||||||
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
|
subroutine.linkParents(ParentSentinel)
|
||||||
|
assertFalse(target.isInRegularRAM(target.definingScope()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIN
docs/source/_static/cobra3d.png
Normal file
BIN
docs/source/_static/cobra3d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
docs/source/_static/primes_cx16.png
Normal file
BIN
docs/source/_static/primes_cx16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -83,7 +83,7 @@ For normal use the compiler is invoked with the command:
|
|||||||
By default, assembly code is generated and written to ``sourcefile.asm``.
|
By default, assembly code is generated and written to ``sourcefile.asm``.
|
||||||
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
||||||
that assembles it into the final program.
|
that assembles it into the final program.
|
||||||
If you use the option to let the compiler auto-start a C-64 emulator, it will do so after
|
If you use the option to let the compiler auto-start an emulator, it will do so after
|
||||||
a successful compilation. This will load your program and the symbol and breakpoint lists
|
a successful compilation. This will load your program and the symbol and breakpoint lists
|
||||||
(for the machine code monitor) into the emulator.
|
(for the machine code monitor) into the emulator.
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ A module source file is a text file with the ``.p8`` suffix, containing the prog
|
|||||||
It consists of compilation options and other directives, imports of other modules,
|
It consists of compilation options and other directives, imports of other modules,
|
||||||
and source code for one or more code blocks.
|
and source code for one or more code blocks.
|
||||||
|
|
||||||
Prog8 has a couple of *LIBRARY* modules that are defined in special internal files provided by the compiler:
|
Prog8 has various *LIBRARY* modules that are defined in special internal files provided by the compiler.
|
||||||
``c64lib``, ``c64utils``, ``c64flt`` and ``prog8lib``. You should not overwrite these or reuse their names.
|
You should not overwrite these or reuse their names.
|
||||||
They are embedded into the packaged release version of the compiler so you don't have to worry about
|
They are embedded into the packaged release version of the compiler so you don't have to worry about
|
||||||
where they are, but their names are still reserved.
|
where they are, but their names are still reserved.
|
||||||
|
|
||||||
@ -149,10 +149,10 @@ If your running program hits one of the breakpoints, Vice will halt execution an
|
|||||||
Troubleshooting
|
Troubleshooting
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Getting an assembler error about undefined symbols such as ``not defined 'c64flt'``?
|
Getting an assembler error about undefined symbols such as ``not defined 'floats'``?
|
||||||
This happens when your program uses floating point values, and you forgot to import ``c64flt`` library.
|
This happens when your program uses floating point values, and you forgot to import ``floats`` library.
|
||||||
If you use floating points, the compiler needs routines from that library.
|
If you use floating points, the compiler needs routines from that library.
|
||||||
Fix it by adding an ``%import c64flt``.
|
Fix it by adding an ``%import floats``.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -12,6 +12,7 @@ What is Prog8?
|
|||||||
|
|
||||||
This is an experimental compiled programming language targeting the 8-bit
|
This is an experimental compiled programming language targeting the 8-bit
|
||||||
`6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ /
|
`6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ /
|
||||||
|
`65c02 <https://en.wikipedia.org/wiki/MOS_Technology_65C02>`_ /
|
||||||
`6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ microprocessor.
|
`6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ microprocessor.
|
||||||
This CPU is from the late 1970's and early 1980's and was used in many home computers from that era,
|
This CPU is from the late 1970's and early 1980's and was used in many home computers from that era,
|
||||||
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
|
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
|
||||||
@ -37,49 +38,49 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
|
|||||||
:alt: Fully playable tetris clone
|
:alt: Fully playable tetris clone
|
||||||
|
|
||||||
|
|
||||||
Code examples
|
Code example
|
||||||
-------------
|
------------
|
||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64utils
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
ubyte[256] sieve
|
ubyte[256] sieve
|
||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2 ; is increased in the loop
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
; clear the sieve, to reset starting situation on subsequent runs
|
||||||
memset(sieve, 256, false)
|
memset(sieve, 256, false)
|
||||||
|
; calculate primes
|
||||||
c64scr.print("prime numbers up to 255:\n\n")
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
while true {
|
repeat {
|
||||||
ubyte prime = find_next_prime()
|
ubyte prime = find_next_prime()
|
||||||
if prime==0
|
if prime==0
|
||||||
break
|
break
|
||||||
c64scr.print_ub(prime)
|
txt.print_ub(prime)
|
||||||
c64scr.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
c64scr.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
c64scr.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
txt.chrout('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
|
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
if candidate_prime==0
|
||||||
return 0
|
return 0 ; we wrapped; no more primes available in the sieve
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; found next one, mark the multiples and return it.
|
||||||
sieve[candidate_prime] = true
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
multiple += candidate_prime
|
||||||
@ -95,39 +96,12 @@ when compiled an ran on a C-64 you get this:
|
|||||||
:align: center
|
:align: center
|
||||||
:alt: result when run on C-64
|
:alt: result when run on C-64
|
||||||
|
|
||||||
|
when the exact same program is compiled for the Commander X16 target, and run on the emulator, you get this:
|
||||||
|
|
||||||
The following programs shows a use of the high level ``struct`` type::
|
.. image:: _static/primes_cx16.png
|
||||||
|
:align: center
|
||||||
|
:alt: result when run on CX16 emulator
|
||||||
|
|
||||||
%import c64utils
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
ubyte red
|
|
||||||
ubyte green
|
|
||||||
ubyte blue
|
|
||||||
}
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
Color purple = {255, 0, 255}
|
|
||||||
Color other
|
|
||||||
other = purple
|
|
||||||
other.red /= 2
|
|
||||||
other.green = 10 + other.green / 2
|
|
||||||
other.blue = 99
|
|
||||||
|
|
||||||
c64scr.print_ub(other.red)
|
|
||||||
c64.CHROUT(',')
|
|
||||||
c64scr.print_ub(other.green)
|
|
||||||
c64.CHROUT(',')
|
|
||||||
c64scr.print_ub(other.blue)
|
|
||||||
c64.CHROUT('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when compiled and ran, it prints ``127,10,99`` on the screen.
|
|
||||||
|
|
||||||
|
|
||||||
Design principles and features
|
Design principles and features
|
||||||
@ -139,8 +113,8 @@ Design principles and features
|
|||||||
- 'One statement per line' code, resulting in clear readable programs.
|
- 'One statement per line' code, resulting in clear readable programs.
|
||||||
- Modular programming and scoping via modules, code blocks, and subroutines.
|
- Modular programming and scoping via modules, code blocks, and subroutines.
|
||||||
- Provide high level programming constructs but at the same time stay close to the metal;
|
- Provide high level programming constructs but at the same time stay close to the metal;
|
||||||
still able to directly use memory addresses, CPU registers and ROM subroutines,
|
still able to directly use memory addresses and ROM subroutines,
|
||||||
and inline assembly to have full control when every cycle or byte matters
|
and inline assembly to have full control when every register, cycle or byte matters
|
||||||
- Arbitrary number of subroutine parameters
|
- Arbitrary number of subroutine parameters
|
||||||
- Complex nested expressions are possible
|
- Complex nested expressions are possible
|
||||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||||
@ -156,8 +130,9 @@ Design principles and features
|
|||||||
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
|
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
|
||||||
performance or space-critical parts will likely still be required. This is supported by
|
performance or space-critical parts will likely still be required. This is supported by
|
||||||
the ability to easily write embedded assembly code directly in the program source code.
|
the ability to easily write embedded assembly code directly in the program source code.
|
||||||
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
|
||||||
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
||||||
|
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
.. _requirements:
|
.. _requirements:
|
||||||
@ -175,6 +150,7 @@ Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <http
|
|||||||
|
|
||||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
|
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
|
||||||
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||||
|
If you're targeting the CommanderX16, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
||||||
|
@ -50,7 +50,7 @@ Code
|
|||||||
There are different kinds of instructions ('statements' is a better name) such as:
|
There are different kinds of instructions ('statements' is a better name) such as:
|
||||||
|
|
||||||
- value assignment
|
- value assignment
|
||||||
- looping (for, while, repeat, unconditional jumps)
|
- looping (for, while, do-until, repeat, unconditional jumps)
|
||||||
- conditional execution (if - then - else, when, and conditional jumps)
|
- conditional execution (if - then - else, when, and conditional jumps)
|
||||||
- subroutine calls
|
- subroutine calls
|
||||||
- label definition
|
- label definition
|
||||||
@ -137,7 +137,7 @@ Scopes are created using either of these two statements:
|
|||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
Unlike most other programming languages, a new scope is *not* created inside
|
Unlike most other programming languages, a new scope is *not* created inside
|
||||||
for, while and repeat statements, the if statement, and the branching conditionals.
|
for, while, repeat, and do-until statements, the if statement, and the branching conditionals.
|
||||||
These all share the same scope from the subroutine they're defined in.
|
These all share the same scope from the subroutine they're defined in.
|
||||||
You can define variables in these blocks, but these will be treated as if they
|
You can define variables in these blocks, but these will be treated as if they
|
||||||
were defined in the subroutine instead.
|
were defined in the subroutine instead.
|
||||||
@ -204,13 +204,6 @@ Example::
|
|||||||
byte @zp zeropageCounter = 42
|
byte @zp zeropageCounter = 42
|
||||||
|
|
||||||
|
|
||||||
Variables that represent CPU hardware registers
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The following variables are reserved
|
|
||||||
and map directly (read/write) to a CPU hardware register: ``A``, ``X``, ``Y``.
|
|
||||||
|
|
||||||
|
|
||||||
Integers
|
Integers
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
@ -233,7 +226,7 @@ This is because routines in the C-64 BASIC and KERNAL ROMs are used for that.
|
|||||||
So floating point operations will only work if the C-64 BASIC ROM (and KERNAL ROM)
|
So floating point operations will only work if the C-64 BASIC ROM (and KERNAL ROM)
|
||||||
are banked in.
|
are banked in.
|
||||||
|
|
||||||
Also your code needs to import the ``c64flt`` library to enable floating point support
|
Also your code needs to import the ``floats`` library to enable floating point support
|
||||||
in the compiler, and to gain access to the floating point routines.
|
in the compiler, and to gain access to the floating point routines.
|
||||||
(this library contains the directive to enable floating points, you don't have
|
(this library contains the directive to enable floating points, you don't have
|
||||||
to worry about this yourself)
|
to worry about this yourself)
|
||||||
@ -243,12 +236,15 @@ The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (ne
|
|||||||
|
|
||||||
Arrays
|
Arrays
|
||||||
^^^^^^
|
^^^^^^
|
||||||
Array types are also supported. They can be made of bytes, words or floats::
|
Array types are also supported. They can be made of bytes, words or floats, strings, and other arrays
|
||||||
|
(although the usefulness of the latter is very limited for now)::
|
||||||
|
|
||||||
byte[10] array ; array of 10 bytes, initially set to 0
|
byte[10] array ; array of 10 bytes, initially set to 0
|
||||||
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
|
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
|
||||||
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
|
byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
|
||||||
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
|
||||||
|
str[] names = ["ally", "pete"] ; array of string pointers/addresses (equivalent to uword)
|
||||||
|
uword[] others = [names, array] ; array of pointers/addresses to other arrays
|
||||||
|
|
||||||
value = array[3] ; the fourth value in the array (index is 0-based)
|
value = array[3] ; the fourth value in the array (index is 0-based)
|
||||||
char = string[4] ; the fifth character (=byte) in the string
|
char = string[4] ; the fifth character (=byte) in the string
|
||||||
@ -264,6 +260,21 @@ Note that the various keywords for the data type and variable type (``byte``, ``
|
|||||||
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||||
for instance.
|
for instance.
|
||||||
|
|
||||||
|
It's possible to assign a new array to another array, this will overwrite all elements in the original
|
||||||
|
array with those in the value array. The number and types of elements have to match.
|
||||||
|
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
|
||||||
|
|
||||||
|
|
||||||
|
**Arrays at a specific memory location:**
|
||||||
|
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
||||||
|
For instance to reference the first 5 rows of the Commodore 64's screen matrix as an array, you can define::
|
||||||
|
|
||||||
|
&ubyte[5*40] top5screenrows = $0400
|
||||||
|
|
||||||
|
This way you can set the second character on the second row from the top like this::
|
||||||
|
|
||||||
|
top5screenrows[41] = '!'
|
||||||
|
|
||||||
|
|
||||||
Strings
|
Strings
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
@ -279,16 +290,35 @@ This @-prefix can also be used for character byte values.
|
|||||||
|
|
||||||
|
|
||||||
You can concatenate two string literals using '+' (not very useful though) or repeat
|
You can concatenate two string literals using '+' (not very useful though) or repeat
|
||||||
a string literal a given number of times using '*'::
|
a string literal a given number of times using '*'. You can also assign a new string
|
||||||
|
value to another string. No bounds check is done so be sure the destination string is
|
||||||
|
large enough to contain the new value (it is overwritten in memory)::
|
||||||
|
|
||||||
str string1 = "first part" + "second part"
|
str string1 = "first part" + "second part"
|
||||||
str string2 = "hello!" * 10
|
str string2 = "hello!" * 10
|
||||||
|
|
||||||
|
string1 = string2
|
||||||
|
string1 = "new value"
|
||||||
|
|
||||||
|
|
||||||
|
There are several 'escape sequences' to help you put special characters into strings, such
|
||||||
|
as newlines, quote characters themselves, and so on. The ones used most often are
|
||||||
|
``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean,
|
||||||
|
read the syntax reference on strings.
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Strings and uwords (=memory address) can often be interchanged.
|
||||||
|
An array of strings is actually an array of uwords where every element is the memory
|
||||||
|
address of the string. You can pass a memory address to assembly functions
|
||||||
|
that require a string as an argument.
|
||||||
|
|
||||||
.. caution::
|
.. caution::
|
||||||
Avoid changing strings after they've been created.
|
It's probably best to avoid changing strings after they've been created. This
|
||||||
|
includes changing certain letters by index, or by assigning a new value, or by
|
||||||
|
modifying the string via other means for example ``substr`` function and its cousins.
|
||||||
This is because if your program exits and is restarted (without loading it again),
|
This is because if your program exits and is restarted (without loading it again),
|
||||||
it will then start working with the changed strings instead of the original ones.
|
it will then start working with the changed strings instead of the original ones!
|
||||||
The same is true for arrays.
|
The same is true for arrays.
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +347,7 @@ and then create a variable with it::
|
|||||||
ubyte blue
|
ubyte blue
|
||||||
}
|
}
|
||||||
|
|
||||||
Color rgb = {255,122,0} ; note the curly braces here instead of brackets
|
Color rgb = [255,122,0] ; note that struct initializer value is same as an array
|
||||||
Color another ; the init value is optional, like arrays
|
Color another ; the init value is optional, like arrays
|
||||||
|
|
||||||
another = rgb ; assign all of the values of rgb to another
|
another = rgb ; assign all of the values of rgb to another
|
||||||
@ -386,21 +416,21 @@ expected when the program is restarted.
|
|||||||
Loops
|
Loops
|
||||||
-----
|
-----
|
||||||
|
|
||||||
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
||||||
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
|
The loop variable must be declared as byte or word earlier so you can reuse it for multiple occasions.
|
||||||
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
||||||
|
|
||||||
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
||||||
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
|
The *do--until* loop is used to repeat a piece of code until a certain condition is true.
|
||||||
|
The *repeat* loop is used as a short notation of a for loop where the loop variable doesn't matter and you're only interested in the number of iterations.
|
||||||
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still
|
(without iteration count specified it simply loops forever).
|
||||||
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
|
|
||||||
a forever-loop.
|
|
||||||
|
|
||||||
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
||||||
|
|
||||||
|
Breaking out of a loop prematurely is possible with the ``break`` statement.
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
|
The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
|
||||||
after the loop without first assigning a new value to it!
|
after the loop without first assigning a new value to it!
|
||||||
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
|
||||||
|
|
||||||
@ -414,15 +444,15 @@ if statements
|
|||||||
Conditional execution means that the flow of execution changes based on certiain conditions,
|
Conditional execution means that the flow of execution changes based on certiain conditions,
|
||||||
rather than having fixed gotos or subroutine calls::
|
rather than having fixed gotos or subroutine calls::
|
||||||
|
|
||||||
if A>4 goto overflow
|
if aa>4 goto overflow
|
||||||
|
|
||||||
if X==3 Y = 4
|
if xx==3 yy = 4
|
||||||
if X==3 Y = 4 else A = 2
|
if xx==3 yy = 4 else aa = 2
|
||||||
|
|
||||||
if X==5 {
|
if xx==5 {
|
||||||
Y = 99
|
yy = 99
|
||||||
} else {
|
} else {
|
||||||
A = 3
|
aa = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -486,16 +516,16 @@ Assignments
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
Assignment statements assign a single value to a target variable or memory location.
|
Assignment statements assign a single value to a target variable or memory location.
|
||||||
Augmented assignments (such as ``A += X``) are also available, but these are just shorthands
|
Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
|
||||||
for normal assignments (``A = A + X``).
|
for normal assignments (``aa = aa + xx``).
|
||||||
|
|
||||||
Only register variables and variables of type byte, word and float can be assigned a new value.
|
Only variables of type byte, word and float can be assigned a new value.
|
||||||
It's not possible to set a new value to string or array variables etc, because they get allocated
|
It's not possible to set a new value to string or array variables etc, because they get allocated
|
||||||
a fixed amount of memory which will not change.
|
a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though).
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
**Data type conversion (in assignments):**
|
**Data type conversion (in assignments):**
|
||||||
When assigning a value with a 'smaller' datatype to a register or variable with a 'larger' datatype,
|
When assigning a value with a 'smaller' datatype to variable with a 'larger' datatype,
|
||||||
the value will be automatically converted to the target datatype: byte --> word --> float.
|
the value will be automatically converted to the target datatype: byte --> word --> float.
|
||||||
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
|
So assigning a byte to a word variable, or a word to a floating point variable, is fine.
|
||||||
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
|
The reverse is *not* true: it is *not* possible to assign a value of a 'larger' datatype to
|
||||||
@ -511,7 +541,7 @@ as the memory mapped address $d021.
|
|||||||
If you want to access a memory location directly (by using the address itself), without defining
|
If you want to access a memory location directly (by using the address itself), without defining
|
||||||
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
|
a memory mapped location, you can do so by enclosing the address in ``@(...)``::
|
||||||
|
|
||||||
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
|
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
|
||||||
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
||||||
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
|
@(vic+$20) = 6 ; you can also use expressions to 'calculate' the address
|
||||||
|
|
||||||
@ -568,25 +598,24 @@ within parentheses will be evaluated first. So ``(4 + 8) * 2`` is 24 and not 20,
|
|||||||
and ``(true or false) and false`` is false instead of true.
|
and ``(true or false) and false`` is false instead of true.
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
**calculations keep their datatype:**
|
**calculations keep their datatype even if the target variable is larger:**
|
||||||
When you do calculations on a BYTE type, the result will remain a BYTE.
|
When you do calculations on a BYTE type, the result will remain a BYTE.
|
||||||
When you do calculations on a WORD type, the result will remain a WORD.
|
When you do calculations on a WORD type, the result will remain a WORD.
|
||||||
For instance::
|
For instance::
|
||||||
|
|
||||||
byte b = 44
|
byte b = 44
|
||||||
word w = b*55 ; the result will be 116! (even though the target variable is a word)
|
word w = b*55 ; the result will be 116! (even though the target variable is a word)
|
||||||
w *= 999 ; the result will be -15188 (the multiplication stays within a word)
|
w *= 999 ; the result will be -15188 (the multiplication stays within a word, but overflows)
|
||||||
|
|
||||||
The compiler will NOT give a warning about this! It's doing this for
|
*The compiler does NOT warn about this!* It's doing this for
|
||||||
performance reasons - so you won't get sudden 16 bit (or even float)
|
performance reasons - so you won't get sudden 16 bit (or even float)
|
||||||
calculations where you needed only simple fast byte arithmetic.
|
calculations where you needed only simple fast byte arithmetic.
|
||||||
If you do need the extended resulting value, cast at least one of the
|
If you do need the extended resulting value, cast at least one of the
|
||||||
operands of an operator to the larger datatype. For example::
|
operands explicitly to the larger datatype. For example::
|
||||||
|
|
||||||
byte b = 44
|
byte b = 44
|
||||||
word w = b*55.w ; the result will be 2420
|
w = (b as word)*55
|
||||||
w = (b as word)*55 ; same result
|
w = b*(55 as word)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -709,16 +738,22 @@ sort(array)
|
|||||||
floating point values.
|
floating point values.
|
||||||
|
|
||||||
reverse(array)
|
reverse(array)
|
||||||
Reverse the values in the array (in-place). Supports all data types including floats.
|
Reverse the values in the array (in-place).
|
||||||
Can be used after sort() to sort an array in descending order.
|
Can be used after sort() to sort an array in descending order.
|
||||||
|
|
||||||
len(x)
|
len(x)
|
||||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
|
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
|
||||||
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
|
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
|
||||||
length of the string during execution, the value of len(string) may no longer be correct!
|
length of the string during execution, the value of len(string) may no longer be correct!
|
||||||
(use strlen function if you want to dynamically determine the length)
|
(use strlen function if you want to dynamically determine the length)
|
||||||
|
|
||||||
|
sizeof(name)
|
||||||
|
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
|
||||||
|
the object. For instance, for a variable of type uword, the sizeof is 2.
|
||||||
|
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||||
|
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||||
|
|
||||||
strlen(str)
|
strlen(str)
|
||||||
Number of bytes in the string. This value is determined during runtime and counts upto
|
Number of bytes in the string. This value is determined during runtime and counts upto
|
||||||
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
||||||
@ -732,8 +767,9 @@ msb(x)
|
|||||||
sgn(x)
|
sgn(x)
|
||||||
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
||||||
|
|
||||||
mkword(lsb, msb)
|
mkword(msb, lsb)
|
||||||
Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting.
|
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
||||||
|
So mkword($80, $22) results in $8022.
|
||||||
|
|
||||||
any(x)
|
any(x)
|
||||||
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
||||||
@ -750,16 +786,6 @@ rndw()
|
|||||||
rndf()
|
rndf()
|
||||||
returns a pseudo-random float between 0.0 and 1.0
|
returns a pseudo-random float between 0.0 and 1.0
|
||||||
|
|
||||||
lsl(x)
|
|
||||||
Shift the bits in x (byte or word) one position to the left.
|
|
||||||
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
|
||||||
|
|
||||||
lsr(x)
|
|
||||||
Shift the bits in x (byte or word) one position to the right.
|
|
||||||
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
|
||||||
|
|
||||||
rol(x)
|
rol(x)
|
||||||
Rotate the bits in x (byte or word) one position to the left.
|
Rotate the bits in x (byte or word) one position to the left.
|
||||||
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
||||||
@ -796,12 +822,28 @@ memset(address, numbytes, bytevalue)
|
|||||||
Efficiently set a part of memory to the given (u)byte value.
|
Efficiently set a part of memory to the given (u)byte value.
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
Note that for clearing the character screen, very fast specialized subroutines are
|
Note that for clearing the character screen, very fast specialized subroutines are
|
||||||
available in the ``c64scr`` block (part of the ``c64utils`` module)
|
available in the ``txt`` block (part of the ``textio`` module)
|
||||||
|
|
||||||
memsetw(address, numwords, wordvalue)
|
memsetw(address, numwords, wordvalue)
|
||||||
Efficiently set a part of memory to the given (u)word value.
|
Efficiently set a part of memory to the given (u)word value.
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
|
|
||||||
|
leftstr(source, target, length)
|
||||||
|
Copies the left side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
rightstr(source, target, length)
|
||||||
|
Copies the right side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
substr(source, target, start, length)
|
||||||
|
Copies a segment from the source string, starting at the given index,
|
||||||
|
and of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
swap(x, y)
|
swap(x, y)
|
||||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ Everything after a semicolon ``;`` is a comment and is ignored.
|
|||||||
If the whole line is just a comment, it will be copied into the resulting assembly source code.
|
If the whole line is just a comment, it will be copied into the resulting assembly source code.
|
||||||
This makes it easier to understand and relate the generated code. Examples::
|
This makes it easier to understand and relate the generated code. Examples::
|
||||||
|
|
||||||
A = 42 ; set the initial value to 42
|
counter = 42 ; set the initial value to 42
|
||||||
; next is the code that...
|
; next is the code that...
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +33,13 @@ This makes it easier to understand and relate the generated code. Examples::
|
|||||||
Directives
|
Directives
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
.. data:: %target <target>
|
||||||
|
|
||||||
|
Level: module.
|
||||||
|
Global setting, specifies that this module can only work for the given compiler target.
|
||||||
|
If compiled with a different target, compilation is aborted with an error message.
|
||||||
|
|
||||||
|
|
||||||
.. data:: %output <type>
|
.. data:: %output <type>
|
||||||
|
|
||||||
Level: module.
|
Level: module.
|
||||||
@ -60,7 +67,8 @@ Directives
|
|||||||
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
|
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
|
||||||
and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines),
|
and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines),
|
||||||
including default IRQs during normal system operation.
|
including default IRQs during normal system operation.
|
||||||
When the program exits, a system reset is performed (because BASIC will be in a corrupt state).
|
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
||||||
|
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
||||||
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
||||||
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
|
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
|
||||||
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
||||||
@ -70,10 +78,11 @@ Directives
|
|||||||
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
||||||
except the few addresses mentioned above that are used by the system's IRQ routine.
|
except the few addresses mentioned above that are used by the system's IRQ routine.
|
||||||
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
||||||
This includes many floating point operations and several utility routines that do I/O, such as ``print_string``.
|
This includes many floating point operations and several utility routines that do I/O, such as ``print``.
|
||||||
As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine.
|
|
||||||
This option makes programs smaller and faster because even more variables can
|
This option makes programs smaller and faster because even more variables can
|
||||||
be stored in the ZP (which allows for more efficient assembly code).
|
be stored in the ZP (which allows for more efficient assembly code).
|
||||||
|
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
||||||
|
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
||||||
- style ``dontuse`` -- don't use *any* location in the zeropage.
|
- style ``dontuse`` -- don't use *any* location in the zeropage.
|
||||||
|
|
||||||
Also read :ref:`zeropage`.
|
Also read :ref:`zeropage`.
|
||||||
@ -110,10 +119,15 @@ Directives
|
|||||||
|
|
||||||
Level: module, block.
|
Level: module, block.
|
||||||
Sets special compiler options.
|
Sets special compiler options.
|
||||||
For a module option, only the ``enable_floats`` option is recognised, which will tell the compiler
|
|
||||||
|
- For a module option, there is ``enable_floats``, which will tell the compiler
|
||||||
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
|
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
|
||||||
Otherwise, floating point support is not enabled.
|
Otherwise, floating point support is not enabled.
|
||||||
When used in a block with the ``force_output`` option, it will force the block to be outputted
|
- There's also ``no_sysinit`` which cause the resulting program to *not* include
|
||||||
|
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
|
||||||
|
take care of that yourself. The program will just start running from whatever state the machine is in when the
|
||||||
|
program was launched.
|
||||||
|
- When used in a block with the ``force_output`` option, it will force the block to be outputted
|
||||||
in the final program. Can be useful to make sure some
|
in the final program. Can be useful to make sure some
|
||||||
data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
|
data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
|
||||||
|
|
||||||
@ -157,7 +171,7 @@ Directives
|
|||||||
Identifiers
|
Identifiers
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Naming things in Prog8 is done via valid *identifiers*. They start with a letter or underscore,
|
Naming things in Prog8 is done via valid *identifiers*. They start with a letter,
|
||||||
and after that, a combination of letters, numbers, or underscores. Examples of valid identifiers::
|
and after that, a combination of letters, numbers, or underscores. Examples of valid identifiers::
|
||||||
|
|
||||||
a
|
a
|
||||||
@ -165,7 +179,7 @@ and after that, a combination of letters, numbers, or underscores. Examples of v
|
|||||||
monkey
|
monkey
|
||||||
COUNTER
|
COUNTER
|
||||||
Better_Name_2
|
Better_Name_2
|
||||||
_something_strange_
|
something_strange__
|
||||||
|
|
||||||
|
|
||||||
Code blocks
|
Code blocks
|
||||||
@ -267,6 +281,7 @@ type identifier type storage size example var declara
|
|||||||
``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]``
|
``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]``
|
||||||
``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]``
|
``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]``
|
||||||
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
|
``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]``
|
||||||
|
``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]``
|
||||||
``str`` string (petscii) varies ``str myvar = "hello."``
|
``str`` string (petscii) varies ``str myvar = "hello."``
|
||||||
implicitly terminated by a 0-byte
|
implicitly terminated by a 0-byte
|
||||||
=============== ======================= ================= =========================================
|
=============== ======================= ================= =========================================
|
||||||
@ -289,7 +304,8 @@ of something with an operand starting with 1 or 0, you'll have to add a space in
|
|||||||
- When an integer value ranges from 256..65535 the compiler sees it as a ``uword``. For -32768..32767 it's a ``word``.
|
- When an integer value ranges from 256..65535 the compiler sees it as a ``uword``. For -32768..32767 it's a ``word``.
|
||||||
- When a hex number has 3 or 4 digits, for example ``$0004``, it is seen as a ``word`` otherwise as a ``byte``.
|
- When a hex number has 3 or 4 digits, for example ``$0004``, it is seen as a ``word`` otherwise as a ``byte``.
|
||||||
- When a binary number has 9 to 16 digits, for example ``%1100110011``, it is seen as a ``word`` otherwise as a ``byte``.
|
- When a binary number has 9 to 16 digits, for example ``%1100110011``, it is seen as a ``word`` otherwise as a ``byte``.
|
||||||
- You can force a byte value into a word value by adding the ``.w`` datatype suffix to the number: ``$2a.w`` is equivalent to ``$002a``.
|
- If the number fits in a byte but you really require it as a word value, you'll have to explicitly cast it: ``60 as uword``
|
||||||
|
or you can use the full word hexadecimal notation ``$003c``.
|
||||||
|
|
||||||
|
|
||||||
Data type conversion
|
Data type conversion
|
||||||
@ -306,6 +322,7 @@ should be allocated by the compiler. Instead, the (mandatory) value assigned to
|
|||||||
should be the *memory address* where the value is located::
|
should be the *memory address* where the value is located::
|
||||||
|
|
||||||
&byte BORDERCOLOR = $d020
|
&byte BORDERCOLOR = $d020
|
||||||
|
&ubyte[5*40] top5screenrows = $0400 ; works for array as well
|
||||||
|
|
||||||
|
|
||||||
Direct access to memory locations
|
Direct access to memory locations
|
||||||
@ -313,7 +330,7 @@ Direct access to memory locations
|
|||||||
Instead of defining a memory mapped name for a specific memory location, you can also
|
Instead of defining a memory mapped name for a specific memory location, you can also
|
||||||
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
|
directly access the memory. Enclose a numeric expression or literal with ``@(...)`` to do that::
|
||||||
|
|
||||||
A = @($d020) ; set the A register to the current c64 screen border color ("peek(53280)")
|
color = @($d020) ; set the variable 'color' to the current c64 screen border color ("peek(53280)")
|
||||||
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
|
||||||
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
|
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
|
||||||
|
|
||||||
@ -333,8 +350,6 @@ Reserved names
|
|||||||
|
|
||||||
The following names are reserved, they have a special meaning::
|
The following names are reserved, they have a special meaning::
|
||||||
|
|
||||||
A X Y ; 6502 hardware registers
|
|
||||||
Pc Pz Pn Pv ; 6502 status register flags
|
|
||||||
true false ; boolean values 1 and 0
|
true false ; boolean values 1 and 0
|
||||||
|
|
||||||
|
|
||||||
@ -384,7 +399,25 @@ After defining a struct you can use the name of the struct as a data type to dec
|
|||||||
|
|
||||||
Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
|
Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
|
||||||
|
|
||||||
Color rgb = {255, 100, 0} ; curly braces instead of brackets
|
Color rgb = [255, 100, 0] ; note that the value is an array
|
||||||
|
|
||||||
|
|
||||||
|
String
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
``"hello"`` is a string translated into the default character encoding (PETSCII)
|
||||||
|
|
||||||
|
``@"hello"`` is a string translated into the alternate character encoding (Screencodes/pokes)
|
||||||
|
|
||||||
|
There are several escape sequences available to put special characters into your string value:
|
||||||
|
|
||||||
|
- ``\\`` - the backslash itself, has to be escaped because it is the escape symbol by itself
|
||||||
|
- ``\n`` - newline character (move cursor down and to beginning of next line)
|
||||||
|
- ``\r`` - carriage return character (more or less the same as newline if printing to the screen)
|
||||||
|
- ``\"`` - quote character (otherwise it would terminate the string)
|
||||||
|
- ``\'`` - apostrophe character (has to be escaped in character literals, is okay inside a string)
|
||||||
|
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
|
||||||
|
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
@ -406,10 +439,10 @@ assignment: ``=``
|
|||||||
Note that an assignment sometimes is not possible or supported.
|
Note that an assignment sometimes is not possible or supported.
|
||||||
|
|
||||||
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
||||||
Syntactic sugar; ``A += X`` is equivalent to ``A = A + X``
|
This is syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||||
|
|
||||||
postfix increment and decrement: ``++`` ``--``
|
postfix increment and decrement: ``++`` ``--``
|
||||||
Syntactic sugar; ``A++`` is equivalent to ``A = A + 1``, and ``A--`` is equivalent to ``A = A - 1``.
|
Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
|
||||||
Because these operations are so common, we have these short forms.
|
Because these operations are so common, we have these short forms.
|
||||||
|
|
||||||
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
|
comparison: ``!=`` ``<`` ``>`` ``<=`` ``>=``
|
||||||
@ -427,9 +460,9 @@ range creation: ``to``
|
|||||||
|
|
||||||
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
|
0 to 7 ; range of values 0, 1, 2, 3, 4, 5, 6, 7 (constant)
|
||||||
|
|
||||||
A = 5
|
aa = 5
|
||||||
X = 10
|
aa = 10
|
||||||
A to X ; range of 5, 6, 7, 8, 9, 10
|
aa to xx ; range of 5, 6, 7, 8, 9, 10
|
||||||
|
|
||||||
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
|
byte[] array = 10 to 13 ; sets the array to [1, 2, 3, 4]
|
||||||
|
|
||||||
@ -471,6 +504,8 @@ takes no parameters. If the subroutine returns a value, usually you assign it t
|
|||||||
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
|
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
|
||||||
Otherwise the compiler will warn you about discarding the result of the call.
|
Otherwise the compiler will warn you about discarding the result of the call.
|
||||||
|
|
||||||
|
Multiple return values
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Normal subroutines can only return zero or one return values.
|
Normal subroutines can only return zero or one return values.
|
||||||
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
||||||
(referencing a routine in kernel ROM) can return more than one return value.
|
(referencing a routine in kernel ROM) can return more than one return value.
|
||||||
@ -478,9 +513,16 @@ For example a status in the carry bit and a number in A, or a 16-bit value in A/
|
|||||||
It is not possible to process the results of a call to these kind of routines
|
It is not possible to process the results of a call to these kind of routines
|
||||||
directly from the language, because only single value assignments are possible.
|
directly from the language, because only single value assignments are possible.
|
||||||
You can still call the subroutine and not store the results.
|
You can still call the subroutine and not store the results.
|
||||||
But if you want to do something with the values it returns, you'll have to write
|
|
||||||
a small block of custom inline assembly that does the call and stores the values
|
**There is an exception:** if there's just one return value in a register, and one or more others that are returned
|
||||||
appropriately. Don't forget to save/restore the registers if required.
|
as bits in the status register (such as the Carry bit), the compiler allows you to call the subroutine.
|
||||||
|
It will then store the result value in a variable if required, and *keep the status register untouched
|
||||||
|
after the call* so you can use a conditional branch statement for that.
|
||||||
|
Note that this makes no sense inside an expression, so the compiler will still give an error for that.
|
||||||
|
|
||||||
|
If there really are multiple return values (other than a combined 16 bit return value in 2 registers),
|
||||||
|
you'll have to write a small block of custom inline assembly that does the call and stores the values
|
||||||
|
appropriately. Don't forget to save/restore any registers that are modified.
|
||||||
|
|
||||||
|
|
||||||
Subroutine definitions
|
Subroutine definitions
|
||||||
@ -515,17 +557,19 @@ and returning stuff in several registers as well. The ``clobbers`` clause is use
|
|||||||
what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value.
|
what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value.
|
||||||
|
|
||||||
|
|
||||||
Subroutines that are implemented purely in assembly code and which have an assembly calling convention (i.e.
|
User subroutines in the program source code that are implemented purely in assembly and which have an assembly calling convention (i.e.
|
||||||
the parameters are strictly passed via cpu registers), are defined like this::
|
the parameters are strictly passed via cpu registers), are defined with ``asmsub`` like this::
|
||||||
|
|
||||||
asmsub FREADS32() clobbers(A,X,Y) {
|
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda $62
|
ldy #0
|
||||||
eor #$ff
|
_loop sta c64.Screen,y
|
||||||
asl a
|
sta c64.Screen+$0100,y
|
||||||
lda #0
|
sta c64.Screen+$0200,y
|
||||||
ldx #$a0
|
sta c64.Screen+$02e8,y
|
||||||
jmp $bc4f
|
iny
|
||||||
|
bne _loop
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +595,7 @@ Loops
|
|||||||
for loop
|
for loop
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
The loop variable must be a register or a byte/word variable,
|
The loop variable must be a byte or word variable,
|
||||||
and must be defined first in the local scope of the for loop.
|
and must be defined first in the local scope of the for loop.
|
||||||
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
|
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
|
||||||
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
|
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
|
||||||
@ -561,7 +605,6 @@ You can use a single statement, or a statement block like in the example below::
|
|||||||
for <loopvar> in <expression> [ step <amount> ] {
|
for <loopvar> in <expression> [ step <amount> ] {
|
||||||
; do something...
|
; do something...
|
||||||
break ; break out of the loop
|
break ; break out of the loop
|
||||||
continue ; immediately enter next iteration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
|
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
|
||||||
@ -594,35 +637,35 @@ You can use a single statement, or a statement block like in the example below::
|
|||||||
while <condition> {
|
while <condition> {
|
||||||
; do something...
|
; do something...
|
||||||
break ; break out of the loop
|
break ; break out of the loop
|
||||||
continue ; immediately enter next iteration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
repeat-until loop
|
do-until loop
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
Until the given condition is true (1), repeat the given statement(s).
|
Until the given condition is true (1), repeat the given statement(s).
|
||||||
You can use a single statement, or a statement block like in the example below::
|
You can use a single statement, or a statement block like in the example below::
|
||||||
|
|
||||||
repeat {
|
do {
|
||||||
; do something...
|
; do something...
|
||||||
break ; break out of the loop
|
break ; break out of the loop
|
||||||
continue ; immediately enter next iteration
|
|
||||||
} until <condition>
|
} until <condition>
|
||||||
|
|
||||||
|
|
||||||
forever loop
|
repeat loop
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
Simply run the code in a loop, forever. It's the same as a while true or until false loop,
|
When you're only interested in repeating something a given number of times.
|
||||||
or just a jump back to a previous label. You can still break out of this loop as well, if you want::
|
It's a short hand for a for loop without an explicit loop variable::
|
||||||
|
|
||||||
forever {
|
repeat 15 {
|
||||||
; .. do stuff
|
; do something...
|
||||||
if something
|
break ; you can break out of the loop
|
||||||
break ; you can exit the loop if you want
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
If you omit the iteration count, it simply loops forever.
|
||||||
|
You can still ``break`` out of such a loop if you want though.
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution and Jumps
|
Conditional Execution and Jumps
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -702,3 +745,4 @@ case you have to use { } to enclose them::
|
|||||||
}
|
}
|
||||||
else -> c64scr.print("don't know")
|
else -> c64scr.print("don't know")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,20 @@ Target system specification
|
|||||||
|
|
||||||
Prog8 targets the following hardware:
|
Prog8 targets the following hardware:
|
||||||
|
|
||||||
- 8 bit MOS 6502/6510 CPU
|
- 8 bit MOS 6502/65c02/6510 CPU
|
||||||
- 64 Kb addressable memory (RAM or ROM)
|
- 64 Kb addressable memory (RAM or ROM)
|
||||||
- memory mapped I/O registers
|
- optional use of memory mapped I/O registers
|
||||||
|
- optional use of system ROM routines
|
||||||
|
|
||||||
The main target machine is the Commodore-64, which is an example of this.
|
Currently there are two machines that are supported as compiler target (selectable via the ``-target`` compiler argument):
|
||||||
This chapter explains the relevant system details of such a machine.
|
|
||||||
|
- 'c64': the well-known Commodore-64, premium support
|
||||||
|
- 'cx16': the `CommanderX16 <https://www.commanderx16.com/>`_ a project from the 8-Bit Guy. Support for this is still experimental.
|
||||||
|
|
||||||
|
This chapter explains the relevant system details of these machines.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
Memory Model
|
Memory Model
|
||||||
@ -113,22 +121,14 @@ CPU
|
|||||||
Directly Usable Registers
|
Directly Usable Registers
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The following 6502 CPU hardware registers are directly usable in program code (and are reserved symbols):
|
The hardware CPU registers are not directly accessible from regular Prog8 code.
|
||||||
|
If you need to mess with them, you'll have to use inline assembly.
|
||||||
|
Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and
|
||||||
|
changing its value you will destroy the evaluation stack and likely crash the program.
|
||||||
|
|
||||||
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
The status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
||||||
- the status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
||||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
and read via the ``read_flags()`` function.
|
||||||
and read via the ``read_flags()`` function.
|
|
||||||
|
|
||||||
However, you must assume that the 3 hardware registers ``A``, ``X`` and ``Y``
|
|
||||||
are volatile. Their values cannot be depended upon, the compiler will use them as required.
|
|
||||||
Even simple assignments may require modification of one or more of the registers (for instance, when using arrays).
|
|
||||||
|
|
||||||
Even more important, the ``X`` register is used as an evaluation stack pointer.
|
|
||||||
If you mess with it, you will destroy the evaluation stack and likely crash your program.
|
|
||||||
In some cases the compiler will warn you about this, but you should really avoid to use
|
|
||||||
this register. It's possible to store/restore the register's value (using special built in functions)
|
|
||||||
for the cases you really really need to use it directly.
|
|
||||||
|
|
||||||
|
|
||||||
Subroutine Calling Conventions
|
Subroutine Calling Conventions
|
||||||
@ -155,15 +155,15 @@ You can however install your own IRQ handler.
|
|||||||
This is possible ofcourse by doing it all using customized inline assembly,
|
This is possible ofcourse by doing it all using customized inline assembly,
|
||||||
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
||||||
|
|
||||||
These routines are::
|
For the C64 these routines are::
|
||||||
|
|
||||||
c64utils.set_irqvec()
|
c64.set_irqvec()
|
||||||
c64utils.set_irqvec_excl()
|
c64.set_irqvec_excl()
|
||||||
|
|
||||||
c64utils.set_rasterirq( <raster line> )
|
c64.set_rasterirq( <raster line> )
|
||||||
c64utils.set_rasterirq_excl( <raster line> )
|
c64.set_rasterirq_excl( <raster line> )
|
||||||
|
|
||||||
c64utils.restore_irqvec() ; set it back to the systems default irq handler
|
c64.restore_irqvec() ; set it back to the systems default irq handler
|
||||||
|
|
||||||
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
||||||
as a subroutine ``irq`` in the module ``irq`` so like this::
|
as a subroutine ``irq`` in the module ``irq`` so like this::
|
||||||
@ -173,3 +173,4 @@ as a subroutine ``irq`` in the module ``irq`` so like this::
|
|||||||
; ... irq handling here ...
|
; ... irq handling here ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,28 +2,27 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
- finalize (most) of the still missing "new" assignment asm code generation
|
- get rid of all other TODO's in the code ;-)
|
||||||
|
- implement @stack for asmsub parameters
|
||||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||||
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||||
- investigate support for 8bitguy's Commander X16 platform https://murray2.com/forums/commander-x16.9/
|
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
||||||
|
- use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation
|
||||||
|
- add some primitives/subroutines/examples for using custom char sets, copying the default charset.
|
||||||
|
- recursive subroutines? via %option recursive, allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
|
||||||
|
|
||||||
More optimizations
|
More optimizations
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Add more compiler optimizations to the existing ones.
|
Add more compiler optimizations to the existing ones.
|
||||||
|
|
||||||
- more targeted optimizations for assigment asm code, such as the following:
|
- further optimize assignment codegeneration, such as the following:
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
||||||
- remove unreachable code after an exit(), return or goto
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise. Especially for built-in functions!
|
||||||
- working subroutine inlining (start with trivial routines, grow to taking care of vars and identifier refs to them)
|
- can such parameter passing to subroutines be optimized to avoid copying?
|
||||||
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
|
||||||
the program will then rely solely on the values as they are in memory at the time of program startup.
|
|
||||||
- Also some library routines and code patterns could perhaps be optimized further
|
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
- note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around.
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64utils
|
%import textio
|
||||||
%import c64flt
|
%zeropage basicsafe
|
||||||
%zeropage dontuse
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
@ -20,101 +19,90 @@ main {
|
|||||||
|
|
||||||
; LEN/STRLEN
|
; LEN/STRLEN
|
||||||
ubyte length = len(name)
|
ubyte length = len(name)
|
||||||
if length!=5 c64scr.print("error len1\n")
|
if length!=5 txt.print("error len1\n")
|
||||||
length = len(uwarr)
|
length = len(uwarr)
|
||||||
if length!=5 c64scr.print("error len2\n")
|
if length!=5 txt.print("error len2\n")
|
||||||
length=strlen(name)
|
length=strlen(name)
|
||||||
if length!=5 c64scr.print("error strlen1\n")
|
if length!=5 txt.print("error strlen1\n")
|
||||||
name[3] = 0
|
name[3] = 0
|
||||||
length=strlen(name)
|
length=strlen(name)
|
||||||
if length!=3 c64scr.print("error strlen2\n")
|
if length!=3 txt.print("error strlen2\n")
|
||||||
|
|
||||||
; MAX
|
; MAX
|
||||||
ub = max(ubarr)
|
ub = max(ubarr)
|
||||||
if ub!=199 c64scr.print("error max1\n")
|
if ub!=199 txt.print("error max1\n")
|
||||||
bb = max(barr)
|
bb = max(barr)
|
||||||
if bb!=99 c64scr.print("error max2\n")
|
if bb!=99 txt.print("error max2\n")
|
||||||
uw = max(uwarr)
|
uw = max(uwarr)
|
||||||
if uw!=4444 c64scr.print("error max3\n")
|
if uw!=4444 txt.print("error max3\n")
|
||||||
ww = max(warr)
|
ww = max(warr)
|
||||||
if ww!=999 c64scr.print("error max4\n")
|
if ww!=999 txt.print("error max4\n")
|
||||||
ff = max(farr)
|
ff = max(farr)
|
||||||
if ff!=999.9 c64scr.print("error max5\n")
|
if ff!=999.9 txt.print("error max5\n")
|
||||||
|
|
||||||
; MIN
|
; MIN
|
||||||
ub = min(ubarr)
|
ub = min(ubarr)
|
||||||
if ub!=0 c64scr.print("error min1\n")
|
if ub!=0 txt.print("error min1\n")
|
||||||
bb = min(barr)
|
bb = min(barr)
|
||||||
if bb!=-122 c64scr.print("error min2\n")
|
if bb!=-122 txt.print("error min2\n")
|
||||||
uw = min(uwarr)
|
uw = min(uwarr)
|
||||||
if uw!=0 c64scr.print("error min3\n")
|
if uw!=0 txt.print("error min3\n")
|
||||||
ww = min(warr)
|
ww = min(warr)
|
||||||
if ww!=-4444 c64scr.print("error min4\n")
|
if ww!=-4444 txt.print("error min4\n")
|
||||||
ff = min(farr)
|
ff = min(farr)
|
||||||
if ff!=-4444.4 c64scr.print("error min5\n")
|
if ff!=-4444.4 txt.print("error min5\n")
|
||||||
|
|
||||||
; SUM
|
; SUM
|
||||||
uw = sum(ubarr)
|
uw = sum(ubarr)
|
||||||
if uw!=420 c64scr.print("error sum1\n")
|
if uw!=420 txt.print("error sum1\n")
|
||||||
ww = sum(barr)
|
ww = sum(barr)
|
||||||
if ww!=-101 c64scr.print("error sum2\n")
|
if ww!=-101 txt.print("error sum2\n")
|
||||||
uw = sum(uwarr)
|
uw = sum(uwarr)
|
||||||
if uw!=6665 c64scr.print("error sum3\n")
|
if uw!=6665 txt.print("error sum3\n")
|
||||||
ww = sum(warr)
|
ww = sum(warr)
|
||||||
if ww!=-4223 c64scr.print("error sum4\n")
|
if ww!=-4223 txt.print("error sum4\n")
|
||||||
ff = sum(farr)
|
ff = sum(farr)
|
||||||
if ff!=-4222.4 c64scr.print("error sum5\n")
|
if ff!=-4222.4 txt.print("error sum5\n")
|
||||||
|
|
||||||
; ANY
|
; ANY
|
||||||
ub = any(ubarr)
|
ub = any(ubarr)
|
||||||
if ub==0 c64scr.print("error any1\n")
|
if ub==0 txt.print("error any1\n")
|
||||||
ub = any(barr)
|
ub = any(barr)
|
||||||
if ub==0 c64scr.print("error any2\n")
|
if ub==0 txt.print("error any2\n")
|
||||||
ub = any(uwarr)
|
ub = any(uwarr)
|
||||||
if ub==0 c64scr.print("error any3\n")
|
if ub==0 txt.print("error any3\n")
|
||||||
ub = any(warr)
|
ub = any(warr)
|
||||||
if ub==0 c64scr.print("error any4\n")
|
if ub==0 txt.print("error any4\n")
|
||||||
ub = any(farr)
|
ub = any(farr)
|
||||||
if ub==0 c64scr.print("error any5\n")
|
if ub==0 txt.print("error any5\n")
|
||||||
|
|
||||||
; ALL
|
; ALL
|
||||||
ub = all(ubarr)
|
ub = all(ubarr)
|
||||||
if ub==1 c64scr.print("error all1\n")
|
if ub==1 txt.print("error all1\n")
|
||||||
ub = all(barr)
|
ub = all(barr)
|
||||||
if ub==1 c64scr.print("error all2\n")
|
if ub==1 txt.print("error all2\n")
|
||||||
ub = all(uwarr)
|
ub = all(uwarr)
|
||||||
if ub==1 c64scr.print("error all3\n")
|
if ub==1 txt.print("error all3\n")
|
||||||
ub = all(warr)
|
ub = all(warr)
|
||||||
if ub==1 c64scr.print("error all4\n")
|
if ub==1 txt.print("error all4\n")
|
||||||
ub = all(farr)
|
ub = all(farr)
|
||||||
if ub==1 c64scr.print("error all5\n")
|
if ub==1 txt.print("error all5\n")
|
||||||
ubarr[1]=$40
|
ubarr[1]=$40
|
||||||
barr[1]=$40
|
barr[1]=$40
|
||||||
uwarr[1]=$4000
|
uwarr[1]=$4000
|
||||||
warr[1]=$4000
|
warr[1]=$4000
|
||||||
farr[1]=1.1
|
farr[1]=1.1
|
||||||
ub = all(ubarr)
|
ub = all(ubarr)
|
||||||
if ub==0 c64scr.print("error all6\n")
|
if ub==0 txt.print("error all6\n")
|
||||||
ub = all(barr)
|
ub = all(barr)
|
||||||
if ub==0 c64scr.print("error all7\n")
|
if ub==0 txt.print("error all7\n")
|
||||||
ub = all(uwarr)
|
ub = all(uwarr)
|
||||||
if ub==0 c64scr.print("error all8\n")
|
if ub==0 txt.print("error all8\n")
|
||||||
ub = all(warr)
|
ub = all(warr)
|
||||||
if ub==0 c64scr.print("error all9\n")
|
if ub==0 txt.print("error all9\n")
|
||||||
ub = all(farr)
|
ub = all(farr)
|
||||||
if ub==0 c64scr.print("error all10\n")
|
if ub==0 txt.print("error all10\n")
|
||||||
|
|
||||||
check_eval_stack()
|
txt.print("\nyou should see no errors printed above (only at first run).")
|
||||||
|
|
||||||
c64scr.print("\nyou should see no errors printed above (only at first run).")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,384 +1,385 @@
|
|||||||
%import c64utils
|
%import textio
|
||||||
;%import c64flt
|
%zeropage basicsafe
|
||||||
;%option enable_floats
|
|
||||||
%zeropage dontuse
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
c64scr.print("ubyte shift left\n")
|
ubyte A
|
||||||
|
|
||||||
|
txt.print("ubyte shift left\n")
|
||||||
A = shiftlb0()
|
A = shiftlb0()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb1()
|
A = shiftlb1()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb2()
|
A = shiftlb2()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb3()
|
A = shiftlb3()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb4()
|
A = shiftlb4()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb5()
|
A = shiftlb5()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb6()
|
A = shiftlb6()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb7()
|
A = shiftlb7()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb8()
|
A = shiftlb8()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb9()
|
A = shiftlb9()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("ubyte shift right\n")
|
txt.print("ubyte shift right\n")
|
||||||
A = shiftrb0()
|
A = shiftrb0()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb1()
|
A = shiftrb1()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb2()
|
A = shiftrb2()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb3()
|
A = shiftrb3()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb4()
|
A = shiftrb4()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb5()
|
A = shiftrb5()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb6()
|
A = shiftrb6()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb7()
|
A = shiftrb7()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb8()
|
A = shiftrb8()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb9()
|
A = shiftrb9()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c64scr.print("signed byte shift left\n")
|
txt.print("signed byte shift left\n")
|
||||||
byte signedb
|
byte signedb
|
||||||
signedb = shiftlsb0()
|
signedb = shiftlsb0()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb1()
|
signedb = shiftlsb1()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb2()
|
signedb = shiftlsb2()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb3()
|
signedb = shiftlsb3()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb4()
|
signedb = shiftlsb4()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb5()
|
signedb = shiftlsb5()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb6()
|
signedb = shiftlsb6()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb7()
|
signedb = shiftlsb7()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb8()
|
signedb = shiftlsb8()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb9()
|
signedb = shiftlsb9()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed byte shift right\n")
|
txt.print("signed byte shift right\n")
|
||||||
signedb = shiftrsb0()
|
signedb = shiftrsb0()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb1()
|
signedb = shiftrsb1()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb2()
|
signedb = shiftrsb2()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb3()
|
signedb = shiftrsb3()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb4()
|
signedb = shiftrsb4()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb5()
|
signedb = shiftrsb5()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb6()
|
signedb = shiftrsb6()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb7()
|
signedb = shiftrsb7()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb8()
|
signedb = shiftrsb8()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb9()
|
signedb = shiftrsb9()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c64scr.print("uword shift left\n")
|
txt.print("uword shift left\n")
|
||||||
uword uw
|
uword uw
|
||||||
uw = shiftluw0()
|
uw = shiftluw0()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw1()
|
uw = shiftluw1()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw2()
|
uw = shiftluw2()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw3()
|
uw = shiftluw3()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw4()
|
uw = shiftluw4()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw5()
|
uw = shiftluw5()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw6()
|
uw = shiftluw6()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw7()
|
uw = shiftluw7()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw8()
|
uw = shiftluw8()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw9()
|
uw = shiftluw9()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw10()
|
uw = shiftluw10()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw11()
|
uw = shiftluw11()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw12()
|
uw = shiftluw12()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw13()
|
uw = shiftluw13()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw14()
|
uw = shiftluw14()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw15()
|
uw = shiftluw15()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw16()
|
uw = shiftluw16()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw17()
|
uw = shiftluw17()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("uword shift right\n")
|
txt.print("uword shift right\n")
|
||||||
uw = shiftruw0()
|
uw = shiftruw0()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw1()
|
uw = shiftruw1()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw2()
|
uw = shiftruw2()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw3()
|
uw = shiftruw3()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw4()
|
uw = shiftruw4()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw5()
|
uw = shiftruw5()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw6()
|
uw = shiftruw6()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw7()
|
uw = shiftruw7()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw8()
|
uw = shiftruw8()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw9()
|
uw = shiftruw9()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw10()
|
uw = shiftruw10()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw11()
|
uw = shiftruw11()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw12()
|
uw = shiftruw12()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw13()
|
uw = shiftruw13()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw14()
|
uw = shiftruw14()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw15()
|
uw = shiftruw15()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw16()
|
uw = shiftruw16()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw17()
|
uw = shiftruw17()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed word shift left\n")
|
txt.print("signed word shift left\n")
|
||||||
word sw
|
word sw
|
||||||
sw = shiftlsw0()
|
sw = shiftlsw0()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw1()
|
sw = shiftlsw1()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw2()
|
sw = shiftlsw2()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw3()
|
sw = shiftlsw3()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw4()
|
sw = shiftlsw4()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw5()
|
sw = shiftlsw5()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw6()
|
sw = shiftlsw6()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw7()
|
sw = shiftlsw7()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw8()
|
sw = shiftlsw8()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw9()
|
sw = shiftlsw9()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw10()
|
sw = shiftlsw10()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw11()
|
sw = shiftlsw11()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw12()
|
sw = shiftlsw12()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw13()
|
sw = shiftlsw13()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw14()
|
sw = shiftlsw14()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw15()
|
sw = shiftlsw15()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw16()
|
sw = shiftlsw16()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw17()
|
sw = shiftlsw17()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed word shift right\n")
|
txt.print("signed word shift right\n")
|
||||||
sw = shiftrsw0()
|
sw = shiftrsw0()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw1()
|
sw = shiftrsw1()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw2()
|
sw = shiftrsw2()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw3()
|
sw = shiftrsw3()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw4()
|
sw = shiftrsw4()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw5()
|
sw = shiftrsw5()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw6()
|
sw = shiftrsw6()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw7()
|
sw = shiftrsw7()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw8()
|
sw = shiftrsw8()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw9()
|
sw = shiftrsw9()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw10()
|
sw = shiftrsw10()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw11()
|
sw = shiftrsw11()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw12()
|
sw = shiftrsw12()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw13()
|
sw = shiftrsw13()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw14()
|
sw = shiftrsw14()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw15()
|
sw = shiftrsw15()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw16()
|
sw = shiftrsw16()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw17()
|
sw = shiftrsw17()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64utils
|
%import textio
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -24,91 +23,81 @@ main {
|
|||||||
|
|
||||||
div_float(0,1,0)
|
div_float(0,1,0)
|
||||||
div_float(999.9,111.0,9.008108108108107)
|
div_float(999.9,111.0,9.008108108108107)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1/a2
|
ubyte r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_byte(byte a1, byte a2, byte c) {
|
sub div_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1/a2
|
byte r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_uword(uword a1, uword a2, uword c) {
|
sub div_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1/a2
|
uword r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_word(word a1, word a2, word c) {
|
sub div_word(word a1, word a2, word c) {
|
||||||
word r = a1/a2
|
word r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_float(float a1, float a2, float c) {
|
sub div_float(float a1, float a2, float c) {
|
||||||
float r = a1/a2
|
float r = a1/a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64utils
|
%import textio
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -32,92 +31,81 @@ main {
|
|||||||
minus_float(0,0,0)
|
minus_float(0,0,0)
|
||||||
minus_float(2.5,1.5,1.0)
|
minus_float(2.5,1.5,1.0)
|
||||||
minus_float(-1.5,3.5,-5.0)
|
minus_float(-1.5,3.5,-5.0)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1-a2
|
ubyte r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_byte(byte a1, byte a2, byte c) {
|
sub minus_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1-a2
|
byte r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_uword(uword a1, uword a2, uword c) {
|
sub minus_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1-a2
|
uword r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_word(word a1, word a2, word c) {
|
sub minus_word(word a1, word a2, word c) {
|
||||||
word r = a1-a2
|
word r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_float(float a1, float a2, float c) {
|
sub minus_float(float a1, float a2, float c) {
|
||||||
float r = a1-a2
|
float r = a1-a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64utils
|
%import textio
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -26,91 +25,81 @@ main {
|
|||||||
mul_float(0,0,0)
|
mul_float(0,0,0)
|
||||||
mul_float(2.5,10,25)
|
mul_float(2.5,10,25)
|
||||||
mul_float(-1.5,10,-15)
|
mul_float(-1.5,10,-15)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1*a2
|
ubyte r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_byte(byte a1, byte a2, byte c) {
|
sub mul_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1*a2
|
byte r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_uword(uword a1, uword a2, uword c) {
|
sub mul_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1*a2
|
uword r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_word(word a1, word a2, word c) {
|
sub mul_word(word a1, word a2, word c) {
|
||||||
word r = a1*a2
|
word r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_float(float a1, float a2, float c) {
|
sub mul_float(float a1, float a2, float c) {
|
||||||
float r = a1*a2
|
float r = a1*a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
%import floats
|
||||||
%import c64utils
|
%import textio
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -30,92 +29,81 @@ main {
|
|||||||
plus_float(1.5,2.5,4.0)
|
plus_float(1.5,2.5,4.0)
|
||||||
plus_float(-1.5,3.5,2.0)
|
plus_float(-1.5,3.5,2.0)
|
||||||
plus_float(-1.1,3.3,2.2)
|
plus_float(-1.1,3.3,2.2)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1+a2
|
ubyte r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_byte(byte a1, byte a2, byte c) {
|
sub plus_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1+a2
|
byte r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_uword(uword a1, uword a2, uword c) {
|
sub plus_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1+a2
|
uword r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_word(word a1, word a2, word c) {
|
sub plus_word(word a1, word a2, word c) {
|
||||||
word r = a1+a2
|
word r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_float(float a1, float a2, float c) {
|
sub plus_float(float a1, float a2, float c) {
|
||||||
float r = a1+a2
|
float r = a1+a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
floats.print_f(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64flt.print_f(a2)
|
floats.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
floats.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
%import c64utils
|
%import floats
|
||||||
%import c64flt
|
%import textio
|
||||||
%option enable_floats
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
c64scr.plot(0,24)
|
txt.plot(0,24)
|
||||||
|
|
||||||
|
ubyte Y
|
||||||
ubyte ub=200
|
ubyte ub=200
|
||||||
byte bb=-100
|
byte bb=-100
|
||||||
uword uw = 2000
|
uword uw = 2000
|
||||||
@ -20,7 +20,7 @@ main {
|
|||||||
word[3] warr = -1000
|
word[3] warr = -1000
|
||||||
float[3] flarr = 999.99
|
float[3] flarr = 999.99
|
||||||
|
|
||||||
c64scr.print("++\n")
|
txt.print("++\n")
|
||||||
ub++
|
ub++
|
||||||
bb++
|
bb++
|
||||||
uw++
|
uw++
|
||||||
@ -51,7 +51,7 @@ main {
|
|||||||
check_uw(uwarr[1], 2001)
|
check_uw(uwarr[1], 2001)
|
||||||
check_w(warr[1], -999)
|
check_w(warr[1], -999)
|
||||||
|
|
||||||
c64scr.print("--\n")
|
txt.print("--\n")
|
||||||
ub--
|
ub--
|
||||||
bb--
|
bb--
|
||||||
uw--
|
uw--
|
||||||
@ -76,76 +76,65 @@ main {
|
|||||||
check_b(barr[1], -100)
|
check_b(barr[1], -100)
|
||||||
check_uw(uwarr[1], 2000)
|
check_uw(uwarr[1], 2000)
|
||||||
check_w(warr[1], -1000)
|
check_w(warr[1], -1000)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_ub(ubyte value, ubyte expected) {
|
sub check_ub(ubyte value, ubyte expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" ubyte ")
|
txt.print(" ubyte ")
|
||||||
c64scr.print_ub(value)
|
txt.print_ub(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_ub(expected)
|
txt.print_ub(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_b(byte value, byte expected) {
|
sub check_b(byte value, byte expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" byte ")
|
txt.print(" byte ")
|
||||||
c64scr.print_b(value)
|
txt.print_b(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_b(expected)
|
txt.print_b(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_uw(uword value, uword expected) {
|
sub check_uw(uword value, uword expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" uword ")
|
txt.print(" uword ")
|
||||||
c64scr.print_uw(value)
|
txt.print_uw(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_uw(expected)
|
txt.print_uw(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_w(word value, word expected) {
|
sub check_w(word value, word expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" word ")
|
txt.print(" word ")
|
||||||
c64scr.print_w(value)
|
txt.print_w(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_w(expected)
|
txt.print_w(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_fl(float value, float expected) {
|
sub check_fl(float value, float expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" float ")
|
txt.print(" float ")
|
||||||
c64flt.print_f(value)
|
floats.print_f(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64flt.print_f(expected)
|
floats.print_f(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
%import c64lib
|
%import textio
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -15,45 +13,35 @@ main {
|
|||||||
remainder_uword(40000,511,142)
|
remainder_uword(40000,511,142)
|
||||||
remainder_uword(40000,500,0)
|
remainder_uword(40000,500,0)
|
||||||
remainder_uword(43211,12,11)
|
remainder_uword(43211,12,11)
|
||||||
|
|
||||||
check_eval_stack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1%a2
|
ubyte r = a1%a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" % ")
|
txt.print(" % ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remainder_uword(uword a1, uword a2, uword c) {
|
sub remainder_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1%a2
|
uword r = a1%a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" % ")
|
txt.print(" % ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_eval_stack() {
|
|
||||||
if X!=255 {
|
|
||||||
c64scr.print("x=")
|
|
||||||
c64scr.print_ub(X)
|
|
||||||
c64scr.print(" error!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
%import c64flt
|
%import floats
|
||||||
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -18,113 +19,113 @@ main {
|
|||||||
b1 = 10
|
b1 = 10
|
||||||
b2 = 10
|
b2 = 10
|
||||||
if sgn(b2-b1) != 0
|
if sgn(b2-b1) != 0
|
||||||
c64scr.print("sgn1 error1\n")
|
txt.print("sgn1 error1\n")
|
||||||
|
|
||||||
b1 = -100
|
b1 = -100
|
||||||
b2 = -100
|
b2 = -100
|
||||||
if sgn(b2-b1) != 0
|
if sgn(b2-b1) != 0
|
||||||
c64scr.print("sgn1 error2\n")
|
txt.print("sgn1 error2\n")
|
||||||
|
|
||||||
ub1 = 200
|
ub1 = 200
|
||||||
ub2 = 200
|
ub2 = 200
|
||||||
if sgn(ub2-ub1) != 0
|
if sgn(ub2-ub1) != 0
|
||||||
c64scr.print("sgn1 error3\n")
|
txt.print("sgn1 error3\n")
|
||||||
|
|
||||||
w1 = 100
|
w1 = 100
|
||||||
w2 = 100
|
w2 = 100
|
||||||
if sgn(w2-w1) != 0
|
if sgn(w2-w1) != 0
|
||||||
c64scr.print("sgn1 error4\n")
|
txt.print("sgn1 error4\n")
|
||||||
|
|
||||||
w1 = -2000
|
w1 = -2000
|
||||||
w2 = -2000
|
w2 = -2000
|
||||||
if sgn(w2-w1) != 0
|
if sgn(w2-w1) != 0
|
||||||
c64scr.print("sgn1 error5\n")
|
txt.print("sgn1 error5\n")
|
||||||
|
|
||||||
uw1 = 999
|
uw1 = 999
|
||||||
uw2 = 999
|
uw2 = 999
|
||||||
if sgn(uw2-uw1) != 0
|
if sgn(uw2-uw1) != 0
|
||||||
c64scr.print("sgn1 error6\n")
|
txt.print("sgn1 error6\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 3.45
|
f2 = 3.45
|
||||||
if sgn(f2-f1) != 0
|
if sgn(f2-f1) != 0
|
||||||
c64scr.print("sgn1 error7\n")
|
txt.print("sgn1 error7\n")
|
||||||
|
|
||||||
|
|
||||||
; -1
|
; -1
|
||||||
b1 = 11
|
b1 = 11
|
||||||
b2 = 10
|
b2 = 10
|
||||||
if sgn(b2-b1) != -1
|
if sgn(b2-b1) != -1
|
||||||
c64scr.print("sgn2 error1\n")
|
txt.print("sgn2 error1\n")
|
||||||
|
|
||||||
b1 = -10
|
b1 = -10
|
||||||
b2 = -100
|
b2 = -100
|
||||||
if sgn(b2-b1) != -1
|
if sgn(b2-b1) != -1
|
||||||
c64scr.print("sgn2 error2\n")
|
txt.print("sgn2 error2\n")
|
||||||
|
|
||||||
ub1 = 202
|
ub1 = 202
|
||||||
ub2 = 200
|
ub2 = 200
|
||||||
if sgn(ub2 as byte - ub1 as byte) != -1
|
if sgn(ub2 as byte - ub1 as byte) != -1
|
||||||
c64scr.print("sgn2 error3\n")
|
txt.print("sgn2 error3\n")
|
||||||
|
|
||||||
w1 = 101
|
w1 = 101
|
||||||
w2 = 100
|
w2 = 100
|
||||||
if sgn(w2-w1) != -1
|
if sgn(w2-w1) != -1
|
||||||
c64scr.print("sgn2 error4\n")
|
txt.print("sgn2 error4\n")
|
||||||
|
|
||||||
w1 = -200
|
w1 = -200
|
||||||
w2 = -2000
|
w2 = -2000
|
||||||
if sgn(w2-w1) != -1
|
if sgn(w2-w1) != -1
|
||||||
c64scr.print("sgn2 error5\n")
|
txt.print("sgn2 error5\n")
|
||||||
|
|
||||||
uw1 = 2222
|
uw1 = 2222
|
||||||
uw2 = 999
|
uw2 = 999
|
||||||
if sgn((uw2 as word) - (uw1 as word)) != -1
|
if sgn((uw2 as word) - (uw1 as word)) != -1
|
||||||
c64scr.print("sgn2 error6a\n")
|
txt.print("sgn2 error6a\n")
|
||||||
if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
|
if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
|
||||||
c64scr.print("sgn2 error6b\n")
|
txt.print("sgn2 error6b\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 1.11
|
f2 = 1.11
|
||||||
if sgn(f2-f1) != -1
|
if sgn(f2-f1) != -1
|
||||||
c64scr.print("sgn2 error7\n")
|
txt.print("sgn2 error7\n")
|
||||||
|
|
||||||
; +1
|
; +1
|
||||||
b1 = 11
|
b1 = 11
|
||||||
b2 = 20
|
b2 = 20
|
||||||
if sgn(b2-b1) != 1
|
if sgn(b2-b1) != 1
|
||||||
c64scr.print("sgn3 error1\n")
|
txt.print("sgn3 error1\n")
|
||||||
|
|
||||||
b1 = -10
|
b1 = -10
|
||||||
b2 = -1
|
b2 = -1
|
||||||
if sgn(b2-b1) != 1
|
if sgn(b2-b1) != 1
|
||||||
c64scr.print("sgn3 error2\n")
|
txt.print("sgn3 error2\n")
|
||||||
|
|
||||||
ub1 = 202
|
ub1 = 202
|
||||||
ub2 = 205
|
ub2 = 205
|
||||||
if sgn(ub2-ub1) != 1
|
if sgn(ub2-ub1) != 1
|
||||||
c64scr.print("sgn3 error3\n")
|
txt.print("sgn3 error3\n")
|
||||||
|
|
||||||
w1 = 101
|
w1 = 101
|
||||||
w2 = 200
|
w2 = 200
|
||||||
if sgn(w2-w1) != 1
|
if sgn(w2-w1) != 1
|
||||||
c64scr.print("sgn3 error4\n")
|
txt.print("sgn3 error4\n")
|
||||||
|
|
||||||
w1 = -200
|
w1 = -200
|
||||||
w2 = -20
|
w2 = -20
|
||||||
if sgn(w2-w1) != 1
|
if sgn(w2-w1) != 1
|
||||||
c64scr.print("sgn3 error5\n")
|
txt.print("sgn3 error5\n")
|
||||||
|
|
||||||
uw1 = 2222
|
uw1 = 2222
|
||||||
uw2 = 9999
|
uw2 = 9999
|
||||||
if sgn(uw2-uw1) != 1
|
if sgn(uw2-uw1) != 1
|
||||||
c64scr.print("sgn3 error6\n")
|
txt.print("sgn3 error6\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 5.11
|
f2 = 5.11
|
||||||
if sgn(f2-f1) != 1
|
if sgn(f2-f1) != 1
|
||||||
c64scr.print("sgn3 error7\n")
|
txt.print("sgn3 error7\n")
|
||||||
|
|
||||||
c64scr.print("should see no sgn errors\n")
|
txt.print("should see no sgn errors\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
315
examples/arithmetic/wordbyte.p8
Normal file
315
examples/arithmetic/wordbyte.p8
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
%import textio
|
||||||
|
%import syslib
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
repeat 25 {
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte ub
|
||||||
|
byte bb
|
||||||
|
uword uwsum
|
||||||
|
word wsum
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
ub=50
|
||||||
|
uwsum += ub
|
||||||
|
ub=250
|
||||||
|
uwsum += ub
|
||||||
|
|
||||||
|
if uwsum==50300
|
||||||
|
txt.print("1 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("1 fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
bb = 100
|
||||||
|
wsum += bb
|
||||||
|
bb = -50
|
||||||
|
wsum += bb
|
||||||
|
|
||||||
|
if wsum==-29950
|
||||||
|
txt.print("2 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("2 fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
ub=50
|
||||||
|
uwsum -= ub
|
||||||
|
ub=250
|
||||||
|
uwsum -= ub
|
||||||
|
|
||||||
|
if uwsum==49700
|
||||||
|
txt.print("3 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("3 fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
wsum = -30000
|
||||||
|
bb = 100
|
||||||
|
wsum -= bb
|
||||||
|
bb = -50
|
||||||
|
wsum -= bb
|
||||||
|
|
||||||
|
if wsum==-30050
|
||||||
|
txt.print("4 ok\n")
|
||||||
|
else
|
||||||
|
txt.print("4 fail\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
bb=50
|
||||||
|
uwsum += bb as uword
|
||||||
|
bb=-100
|
||||||
|
uwsum += bb as uword
|
||||||
|
|
||||||
|
if uwsum==49950
|
||||||
|
txt.print("5 ok\n")
|
||||||
|
else
|
||||||
|
txt.print("5 fail\n")
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
bb=50
|
||||||
|
uwsum -= bb as uword
|
||||||
|
bb=100
|
||||||
|
uwsum -= bb as uword
|
||||||
|
|
||||||
|
if uwsum==49850
|
||||||
|
txt.print("6 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("6 fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
ub = 50
|
||||||
|
wsum += ub
|
||||||
|
ub = 250
|
||||||
|
wsum += ub
|
||||||
|
|
||||||
|
if wsum==-29700
|
||||||
|
txt.print("7 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("7 fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
ub = 50
|
||||||
|
wsum -= ub
|
||||||
|
ub = 250
|
||||||
|
wsum -= ub
|
||||||
|
|
||||||
|
if wsum==-30300
|
||||||
|
txt.print("8 ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("8 fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
txt.chrout('\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
ub=0
|
||||||
|
uwsum += (50+ub)
|
||||||
|
uwsum += (250+ub)
|
||||||
|
|
||||||
|
if uwsum==50300
|
||||||
|
txt.print("1b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("1b fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
bb = 0
|
||||||
|
wsum = -30000
|
||||||
|
wsum += (100+bb)
|
||||||
|
wsum += (-50+bb)
|
||||||
|
|
||||||
|
if wsum==-29950
|
||||||
|
txt.print("2b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("2b fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum -= (50+ub)
|
||||||
|
uwsum -= (250+ub)
|
||||||
|
|
||||||
|
if uwsum==49700
|
||||||
|
txt.print("3b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("3b fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
wsum = -30000
|
||||||
|
wsum -= (100+bb)
|
||||||
|
wsum -= (-50+bb)
|
||||||
|
|
||||||
|
if wsum==-30050
|
||||||
|
txt.print("4b ok\n")
|
||||||
|
else
|
||||||
|
txt.print("4b fail\n")
|
||||||
|
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum += (50+bb) as uword
|
||||||
|
uwsum += (-100+bb) as uword
|
||||||
|
|
||||||
|
if uwsum==49950
|
||||||
|
txt.print("5b ok\n")
|
||||||
|
else
|
||||||
|
txt.print("5b fail\n")
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum -= (50+bb) as uword
|
||||||
|
uwsum -= (100+bb) as uword
|
||||||
|
|
||||||
|
if uwsum==49850
|
||||||
|
txt.print("6b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("6b fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
wsum += (50+ub)
|
||||||
|
wsum += (250+ub)
|
||||||
|
|
||||||
|
if wsum==-29700
|
||||||
|
txt.print("7b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("7b fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
wsum -= (50+ub)
|
||||||
|
wsum -= (250+ub)
|
||||||
|
|
||||||
|
if wsum==-30300
|
||||||
|
txt.print("8b ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("8b fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
txt.chrout('\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum += 50
|
||||||
|
uwsum += 250
|
||||||
|
|
||||||
|
if uwsum==50300
|
||||||
|
txt.print("1c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("1c fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
wsum += 100
|
||||||
|
wsum += -50
|
||||||
|
|
||||||
|
if wsum==-29950
|
||||||
|
txt.print("2c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("2c fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum -= 50
|
||||||
|
uwsum -= 250
|
||||||
|
|
||||||
|
if uwsum==49700
|
||||||
|
txt.print("3c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("3c fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
wsum = -30000
|
||||||
|
wsum -= 100
|
||||||
|
wsum -= -50
|
||||||
|
|
||||||
|
if wsum==-30050
|
||||||
|
txt.print("4c ok\n")
|
||||||
|
else
|
||||||
|
txt.print("4c fail\n")
|
||||||
|
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum += 50 as uword
|
||||||
|
uwsum += -100 as uword
|
||||||
|
|
||||||
|
if uwsum==49950
|
||||||
|
txt.print("5c ok\n")
|
||||||
|
else
|
||||||
|
txt.print("5c fail\n")
|
||||||
|
|
||||||
|
uwsum = 50000
|
||||||
|
uwsum -= 50 as uword
|
||||||
|
uwsum -= 100 as uword
|
||||||
|
|
||||||
|
if uwsum==49850
|
||||||
|
txt.print("6c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("6c fail:")
|
||||||
|
txt.print_uw(uwsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
wsum += 50
|
||||||
|
wsum += 250
|
||||||
|
|
||||||
|
if wsum==-29700
|
||||||
|
txt.print("7c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("7c fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
wsum = -30000
|
||||||
|
wsum -= 50
|
||||||
|
wsum -= 250
|
||||||
|
|
||||||
|
if wsum==-30300
|
||||||
|
txt.print("8c ok\n")
|
||||||
|
else {
|
||||||
|
txt.print("8c fail:")
|
||||||
|
txt.print_w(wsum)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
%import c64lib
|
%target c64
|
||||||
%import c64utils
|
%import syslib
|
||||||
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -15,13 +16,13 @@ main {
|
|||||||
|
|
||||||
c64.SCROLX &= %11110111 ; 38 column mode
|
c64.SCROLX &= %11110111 ; 38 column mode
|
||||||
|
|
||||||
c64utils.set_rasterirq(1) ; enable animation
|
c64.set_rasterirq(1) ; enable animation
|
||||||
|
|
||||||
ubyte target_height = 10
|
ubyte target_height = 10
|
||||||
ubyte active_height = 24
|
ubyte active_height = 24
|
||||||
ubyte upwards = true
|
ubyte upwards = true
|
||||||
|
|
||||||
forever {
|
repeat {
|
||||||
ubyte mountain = 223 ; slope upwards
|
ubyte mountain = 223 ; slope upwards
|
||||||
if active_height < target_height {
|
if active_height < target_height {
|
||||||
active_height++
|
active_height++
|
||||||
@ -43,25 +44,27 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
perform_scroll = false
|
perform_scroll = false
|
||||||
c64scr.scroll_left_full(true)
|
txt.scroll_left(true)
|
||||||
if c64.RASTER & 1
|
|
||||||
|
; float the balloon
|
||||||
|
if rnd() & %10000
|
||||||
c64.SPXY[1] ++
|
c64.SPXY[1] ++
|
||||||
else
|
else
|
||||||
c64.SPXY[1] --
|
c64.SPXY[1] --
|
||||||
|
|
||||||
ubyte yy
|
ubyte yy
|
||||||
for yy in 0 to active_height-1 {
|
for yy in 0 to active_height-1 {
|
||||||
c64scr.setcc(39, yy, 32, 2) ; clear top of screen
|
txt.setcc(39, yy, 32, 2) ; clear top of screen
|
||||||
}
|
}
|
||||||
c64scr.setcc(39, active_height, mountain, 8) ; mountain edge
|
txt.setcc(39, active_height, mountain, 8) ; mountain edge
|
||||||
for yy in active_height+1 to 24 {
|
for yy in active_height+1 to 24 {
|
||||||
c64scr.setcc(39, yy, 160, 8) ; draw mountain
|
txt.setcc(39, yy, 160, 8) ; draw mountain
|
||||||
}
|
}
|
||||||
|
|
||||||
yy = rnd()
|
yy = rnd()
|
||||||
if yy > 100 {
|
if yy > 100 {
|
||||||
; draw a star
|
; draw a star
|
||||||
c64scr.setcc(39, yy % (active_height-1), '.', rnd())
|
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 200 {
|
if yy > 200 {
|
||||||
@ -74,12 +77,12 @@ main {
|
|||||||
tree = 65
|
tree = 65
|
||||||
if rnd() > 130
|
if rnd() > 130
|
||||||
treecolor = 13
|
treecolor = 13
|
||||||
c64scr.setcc(39, active_height, tree, treecolor)
|
txt.setcc(39, active_height, tree, treecolor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 235 {
|
if yy > 235 {
|
||||||
; draw a camel
|
; draw a camel
|
||||||
c64scr.setcc(39, active_height, 94, 9)
|
txt.setcc(39, active_height, 94, 9)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
|
%target c64
|
||||||
|
%import syslib
|
||||||
|
%import textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%import c64lib
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
c64scr.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
txt.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||||
c64utils.set_rasterirq(60) ; enable raster irq
|
c64.set_rasterirq(60) ; enable raster irq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user