mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 01:23:34 +00:00
Compare commits
140 Commits
Author | SHA1 | Date | |
---|---|---|---|
e680de05ea | |||
56fec674c5 | |||
54d92a027a | |||
319ac3a641 | |||
0a03c46351 | |||
ae1b62e147 | |||
8d567f6b06 | |||
b1ef09675b | |||
2b7b925090 | |||
e0454e95db | |||
91e421d961 | |||
c853afe769 | |||
1a64cb38d5 | |||
ccebd22856 | |||
a1f3b82333 | |||
3dda29781e | |||
a9d297ee31 | |||
e5ff61f201 | |||
d116eb7655 | |||
bc726c6334 | |||
123473dfc8 | |||
d9eccd4fba | |||
5b890847e5 | |||
64c85b9617 | |||
3e3b0bcd8b | |||
4c1eb1b12a | |||
530d03d284 | |||
619fa9b65e | |||
0032235933 | |||
61d1f1ea87 | |||
238d27acdc | |||
2f62271453 | |||
75d5117a2d | |||
b4700af2f5 | |||
374e2b311d | |||
49036abbaf | |||
38ccbac97c | |||
6b4896b8f5 | |||
d582d1cc42 | |||
9e2b8a2aa9 | |||
6fdc733941 | |||
422b390c48 | |||
67a9d1285c | |||
8e26e38ecc | |||
02e12d8575 | |||
fe2954ce08 | |||
1fe4439395 | |||
2ff04d2abd | |||
3f30d3aa89 | |||
129e17b33a | |||
bf2d8c3f4b | |||
b29f04ce01 | |||
d185ebad48 | |||
605df7c91c | |||
ec60cad8bb | |||
6aa0f5a392 | |||
4cae2c56ec | |||
d840975054 | |||
1b14da6c03 | |||
292640b17a | |||
112a7b09f2 | |||
863ec9ce8a | |||
2eb346a205 | |||
8092355acb | |||
e7ef2ed31b | |||
af4de6d2fc | |||
69f73dd779 | |||
9706b46012 | |||
6d75dd3bb8 | |||
bd295ffc99 | |||
07ce3e3c9d | |||
cbc3e37a89 | |||
3626828ceb | |||
24b77fb5a5 | |||
1505fe686a | |||
0991131fa8 | |||
2e928bd3c2 | |||
ca868ae19e | |||
3e286dd14c | |||
11247d52b1 | |||
1dbc902513 | |||
330e691b78 | |||
6780d4f562 | |||
b30b8b7368 | |||
3df182b8c3 | |||
7f21d89fea | |||
2b267b4ba1 | |||
ef64881528 | |||
9a6bd760bd | |||
00b9766aea | |||
6381d2b6ac | |||
d2ab5f230d | |||
824b41d457 | |||
b5523c7077 | |||
eb3594b18c | |||
852d85d010 | |||
5e0aef04fe | |||
a00c693f93 | |||
c943da1448 | |||
b630fae580 | |||
38e40084f1 | |||
bf23ad78e6 | |||
ded1d19737 | |||
496a3b0d2c | |||
6922333755 | |||
a00c39e9cf | |||
1c1da8e38e | |||
50a306f492 | |||
6995ee2d17 | |||
6c60ea9cac | |||
2431ed811a | |||
6bd205c02a | |||
62ec77e148 | |||
9120e1de88 | |||
60e169bd87 | |||
e4bca5fe47 | |||
a1729b65ab | |||
2950d26c8e | |||
4f8d4a9585 | |||
d787795759 | |||
cf74e73e27 | |||
2770254fd9 | |||
de04bd8cfa | |||
076a547f91 | |||
dffd0a2706 | |||
6c66f86103 | |||
26502c949a | |||
8dfe510883 | |||
96ba9f5902 | |||
3a6ba0ab71 | |||
32d894d6b6 | |||
543efa4299 | |||
eba0708099 | |||
51e6bf0d45 | |||
07b5c44a54 | |||
9fe32c1c34 | |||
0e0278c84a | |||
dea775a9cd | |||
7e3e18a5c7 | |||
8e3ebc84f0 |
6
.idea/compiler.xml
generated
Normal file
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<option name="BUILD_PROCESS_HEAP_SIZE" value="1200" />
|
||||||
|
</component>
|
||||||
|
</project>
|
14
README.md
14
README.md
@ -23,13 +23,15 @@ https://prog8.readthedocs.io/
|
|||||||
What does Prog8 provide?
|
What does Prog8 provide?
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- big reduction of source code length over raw assembly
|
- reduction of source code length over raw assembly
|
||||||
|
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||||
- 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 static variable allocations, automatic string and array variables and string sharing
|
||||||
- subroutines with an input- and output parameter signature
|
- subroutines with input parameters and result values
|
||||||
- no stack frame allocations because parameters and local variables are automatically allocated statically
|
- high-level program optimizations
|
||||||
- constant folding in expressions and other high-level program optimizations
|
- small program boilerplate/compilersupport overhead
|
||||||
|
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- floating point operations (requires the C64 Basic ROM routines for this)
|
- floating point operations (requires the C64 Basic ROM routines for this)
|
||||||
- '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
|
||||||
@ -37,9 +39,9 @@ What does Prog8 provide?
|
|||||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||||
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
- various powerful built-in libraries to do I/O, number conversions, graphics and more
|
||||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||||
- fast execution speed due to compilation to native assembly code
|
|
||||||
- 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
|
||||||
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
|
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
|
||||||
|
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
|
||||||
|
|
||||||
*Rapid edit-compile-run-debug cycle:*
|
*Rapid edit-compile-run-debug cycle:*
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.4.30"
|
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||||
id 'org.jetbrains.dokka' version "0.9.18"
|
|
||||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ dependencies {
|
|||||||
implementation project(':compilerAst')
|
implementation project(':compilerAst')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.2'
|
||||||
// implementation 'net.razorvine:ksim65:1.8'
|
// implementation 'net.razorvine:ksim65:1.8'
|
||||||
// implementation "com.github.hypfvieh:dbus-java:3.2.4"
|
// implementation "com.github.hypfvieh:dbus-java:3.2.4"
|
||||||
|
|
||||||
@ -97,11 +96,6 @@ test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dokka {
|
|
||||||
outputFormat = 'html'
|
|
||||||
outputDirectory = "$buildDir/kdoc"
|
|
||||||
}
|
|
||||||
|
|
||||||
task wrapper(type: Wrapper) {
|
task wrapper(type: Wrapper) {
|
||||||
gradleVersion = '6.7'
|
gradleVersion = '6.7'
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="JAVA_MODULE" version="4">
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="Python" name="Python">
|
||||||
|
<configuration sdkName="Python 3.9" />
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
@ -14,5 +19,6 @@
|
|||||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
<orderEntry type="library" name="unittest-libs" level="project" />
|
||||||
<orderEntry type="library" name="kotlinx-cli-jvm" level="project" />
|
<orderEntry type="library" name="kotlinx-cli-jvm" level="project" />
|
||||||
<orderEntry type="module" module-name="compilerAst" />
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
|
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -428,7 +428,9 @@ var_fac1_greater_f .proc
|
|||||||
cmp #1
|
cmp #1
|
||||||
beq +
|
beq +
|
||||||
lda #0
|
lda #0
|
||||||
+ rts
|
rts
|
||||||
|
+ lda #1
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
var_fac1_greatereq_f .proc
|
var_fac1_greatereq_f .proc
|
||||||
|
@ -83,7 +83,7 @@ romsub $bc58 = ABS() ; fac1 = ABS(fac1)
|
|||||||
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $bf71 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
romsub $bf74 = SQRA() clobbers(A,X,Y) ; fac1 = SQRT(fac2)
|
||||||
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
romsub $bfed = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
romsub $bfb4 = NEGOP() clobbers(A) ; switch the sign of fac1
|
romsub $bfb4 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||||
romsub $e097 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
romsub $e097 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
romsub $e264 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
romsub $e26b = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
%import textio
|
%import textio
|
||||||
|
|
||||||
; bitmap pixel graphics module for the C64
|
; bitmap pixel graphics module for the C64
|
||||||
; only black/white monchrome 320x200 for now
|
; only black/white monochrome 320x200 for now
|
||||||
; assumes bitmap screen memory is $2000-$3fff
|
; assumes bitmap screen memory is $2000-$3fff
|
||||||
|
|
||||||
graphics {
|
graphics {
|
||||||
@ -34,36 +34,33 @@ graphics {
|
|||||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||||
; Bresenham algorithm.
|
; Bresenham algorithm.
|
||||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||||
; TODO there are some slight errors at the first/last pixels in certain slopes...??
|
|
||||||
if y1>y2 {
|
if y1>y2 {
|
||||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
}
|
}
|
||||||
word @zp dx = x2-x1 as word
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = y2-y1
|
word @zp dy = (y2 as word)-y1
|
||||||
|
|
||||||
if dx==0 {
|
if dx==0 {
|
||||||
vertical_line(x1, y1, abs(dy)+1 as ubyte)
|
vertical_line(x1, y1, abs(dy) as ubyte +1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dy==0 {
|
if dy==0 {
|
||||||
if x1>x2
|
if x1>x2
|
||||||
x1=x2
|
x1=x2
|
||||||
horizontal_line(x1, y1, abs(dx)+1 as uword)
|
horizontal_line(x1, y1, abs(dx) as uword +1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
; TODO rewrite the rest in optimized assembly
|
|
||||||
|
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
ubyte positive_ix = true
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
positive_ix = false
|
positive_ix = false
|
||||||
}
|
}
|
||||||
dx *= 2
|
word @zp dx2 = dx*2
|
||||||
dy *= 2
|
word @zp dy2 = dy*2
|
||||||
internal_plotx = x1
|
internal_plotx = x1
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
@ -73,10 +70,10 @@ graphics {
|
|||||||
if internal_plotx==x2
|
if internal_plotx==x2
|
||||||
return
|
return
|
||||||
internal_plotx++
|
internal_plotx++
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -85,10 +82,10 @@ graphics {
|
|||||||
if internal_plotx==x2
|
if internal_plotx==x2
|
||||||
return
|
return
|
||||||
internal_plotx--
|
internal_plotx--
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,10 +97,10 @@ graphics {
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
internal_plotx++
|
internal_plotx++
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -112,10 +109,10 @@ graphics {
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
internal_plotx--
|
internal_plotx--
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
|
|||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
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 $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
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 secondary @ 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(X,Y) -> ubyte @Pc, ubyte @A ; (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
|
||||||
@ -211,10 +211,10 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
|
|||||||
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(X, 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, uword @ XY ; (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 (A=lo,X=mid,Y=high)
|
||||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
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 $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
|
||||||
@ -246,7 +246,7 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
asmsub RDTIM16() -> uword @AY {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits for convenience
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
jsr c64.RDTIM
|
jsr c64.RDTIM
|
||||||
@ -478,7 +478,7 @@ sys {
|
|||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #14
|
lda #14
|
||||||
@ -489,6 +489,7 @@ sys {
|
|||||||
|
|
||||||
sub wait(uword jiffies) {
|
sub wait(uword jiffies) {
|
||||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||||
|
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||||
repeat jiffies {
|
repeat jiffies {
|
||||||
ubyte jiff = lsb(c64.RDTIM16())
|
ubyte jiff = lsb(c64.RDTIM16())
|
||||||
while jiff==lsb(c64.RDTIM16()) {
|
while jiff==lsb(c64.RDTIM16()) {
|
||||||
@ -497,6 +498,29 @@ sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub waitvsync() clobbers(A) {
|
||||||
|
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||||
|
; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||||
|
%asm {{
|
||||||
|
- lda c64.RASTER
|
||||||
|
beq -
|
||||||
|
- lda c64.RASTER
|
||||||
|
bne -
|
||||||
|
bit c64.SCROLY
|
||||||
|
bmi -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub waitrastborder() {
|
||||||
|
; --- busy wait till the raster position has reached the bottom screen border (approximately)
|
||||||
|
; note: a more accurate way to do this is by using a raster irq handler instead.
|
||||||
|
%asm {{
|
||||||
|
- bit c64.SCROLY
|
||||||
|
bpl -
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
ldx cx16.r0
|
ldx cx16.r0
|
||||||
|
@ -7,239 +7,213 @@ conv {
|
|||||||
|
|
||||||
; ----- number conversions to decimal strings ----
|
; ----- number conversions to decimal strings ----
|
||||||
|
|
||||||
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
str string_out = "????????????????" ; result buffer for the string conversion routines
|
||||||
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
|
||||||
|
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy #uword2decimal.ASCII_0_OFFSET
|
phx
|
||||||
bne uword2decimal.hex_try200
|
jsr conv.ubyte2decimal
|
||||||
|
sty string_out
|
||||||
|
sta string_out+1
|
||||||
|
stx string_out+2
|
||||||
|
lda #0
|
||||||
|
sta string_out+3
|
||||||
|
plx
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
|
||||||
; ---- convert 16 bit uword in A/Y to decimal
|
; ---- convert the ubyte in A in decimal string form, without left padding 0s
|
||||||
; 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 {{
|
%asm {{
|
||||||
|
phx
|
||||||
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
ldy #0
|
||||||
;By Omegamatrix Further optimizations by tepples
|
sty P8ZP_SCRATCH_B1
|
||||||
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
|
jsr conv.ubyte2decimal
|
||||||
|
_output_byte_digits
|
||||||
;HexToDec99
|
; hundreds?
|
||||||
; start in A
|
cpy #'0'
|
||||||
; end with A = 10's, decOnes (also in X)
|
beq +
|
||||||
|
pha
|
||||||
;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
|
tya
|
||||||
tax ;2 @14
|
ldy P8ZP_SCRATCH_B1
|
||||||
lsr a ;2 @16
|
sta string_out,y
|
||||||
lsr a ;2 @18 integer divide 1024 (result 0-63)
|
pla
|
||||||
|
inc P8ZP_SCRATCH_B1
|
||||||
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
|
; tens?
|
||||||
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
|
+ ldy P8ZP_SCRATCH_B1
|
||||||
|
cmp #'0'
|
||||||
;at this point we have a number 1-65 that we have to times by 24,
|
beq +
|
||||||
;add to original sum, and Mod 1024 to get a remainder 0-999
|
sta string_out,y
|
||||||
|
iny
|
||||||
|
+ ; ones.
|
||||||
sta temp ;3 @25
|
txa
|
||||||
asl a ;2 @27
|
sta string_out,y
|
||||||
adc temp ;3 @30 x3
|
iny
|
||||||
tay ;2 @32
|
lda #0
|
||||||
lsr a ;2 @34
|
sta string_out,y
|
||||||
lsr a ;2 @36
|
plx
|
||||||
lsr a ;2 @38
|
rts
|
||||||
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
|
|
||||||
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
|
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||||
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||||
; note: if the number is negative, you have to deal with the '-' yourself!
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
phx
|
||||||
|
ldy #0
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
cmp #0
|
cmp #0
|
||||||
bpl +
|
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
|
pha
|
||||||
and #$0f
|
lda #'-'
|
||||||
tax
|
sta string_out
|
||||||
ldy _hex_digits,x
|
inc P8ZP_SCRATCH_B1
|
||||||
pla
|
pla
|
||||||
lsr a
|
+ jsr conv.byte2decimal
|
||||||
lsr a
|
bra str_ub._output_byte_digits
|
||||||
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) {
|
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
; ---- convert the ubyte in A in hex string form
|
||||||
|
%asm {{
|
||||||
|
jsr conv.ubyte2hex
|
||||||
|
sta string_out
|
||||||
|
sty string_out+1
|
||||||
|
lda #0
|
||||||
|
sta string_out+2
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- convert the ubyte in A in binary string form
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
ldy #0
|
||||||
|
sty string_out+8
|
||||||
|
ldy #7
|
||||||
|
- lsr P8ZP_SCRATCH_B1
|
||||||
|
bcc +
|
||||||
|
lda #'1'
|
||||||
|
bne _digit
|
||||||
|
+ lda #'0'
|
||||||
|
_digit sta string_out,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert the uword in A/Y in binary string form
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
tya
|
tya
|
||||||
jsr ubyte2hex
|
jsr str_ubbin
|
||||||
sta output
|
ldy #0
|
||||||
sty output+1
|
sty string_out+16
|
||||||
lda P8ZP_SCRATCH_REG
|
ldy #7
|
||||||
jsr ubyte2hex
|
- lsr P8ZP_SCRATCH_REG
|
||||||
sta output+2
|
bcc +
|
||||||
sty output+3
|
lda #'1'
|
||||||
|
bne _digit
|
||||||
|
+ lda #'0'
|
||||||
|
_digit sta string_out+8,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
rts
|
rts
|
||||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
|
||||||
|
%asm {{
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr conv.ubyte2hex
|
||||||
|
sta string_out
|
||||||
|
sty string_out+1
|
||||||
|
pla
|
||||||
|
jsr conv.ubyte2hex
|
||||||
|
sta string_out+2
|
||||||
|
sty string_out+3
|
||||||
|
lda #0
|
||||||
|
sta string_out+4
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
sta string_out,y
|
||||||
|
beq +
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldx #0
|
||||||
|
_output_digits
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq _allzero
|
||||||
|
cmp #'0'
|
||||||
|
bne _gotdigit
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
_gotdigit sta string_out,x
|
||||||
|
inx
|
||||||
|
iny
|
||||||
|
lda conv.uword2decimal.decTenThousands,y
|
||||||
|
bne _gotdigit
|
||||||
|
_end lda #0
|
||||||
|
sta string_out,x
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
|
||||||
|
_allzero lda #'0'
|
||||||
|
sta string_out,x
|
||||||
|
inx
|
||||||
|
bne _end
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_w (word value @ AY) clobbers(A,Y) {
|
||||||
|
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||||
|
%asm {{
|
||||||
|
cpy #0
|
||||||
|
bpl str_uw
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
lda #'-'
|
||||||
|
sta string_out
|
||||||
|
tya
|
||||||
|
eor #255
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
eor #255
|
||||||
|
clc
|
||||||
|
adc #1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr conv.uword2decimal
|
||||||
|
ldx #1
|
||||||
|
bne str_uw._output_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,4 +494,243 @@ _stop
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- low level 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
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
floats {
|
floats {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 compatible floating point related functions ----
|
||||||
|
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
||||||
|
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
@ -43,46 +45,44 @@ romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
|||||||
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
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 $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 $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 $fe30 = CONUPK(uword mflpt @ AY) clobbers(A,X,Y) ; load mflpt value from memory in A/Y into fac2
|
||||||
romsub $fe36 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
romsub $fe33 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||||
romsub $fe3c = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
romsub $fe36 = 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 $fe39 = 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 $fe3c = 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 $fe42 = MOVFM(uword mflpt @ AY) clobbers(A,X,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 $fe45 = MOVMF(uword mflpt @ XY) clobbers(A,X,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||||
romsub $fe4e = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
romsub $fe48 = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||||
romsub $fe51 = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
romsub $fe4b = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||||
romsub $fe54 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
romsub $fe4e = 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 $fe54 = SIGN() clobbers(X,Y) -> 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 $fe57 = 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 $fe5a = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||||
romsub $fe6c = ABS() ; fac1 = ABS(fac1)
|
romsub $fe66 = ABS() clobbers(A,X,Y) ; 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 $fe69 = 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 $fe72 = 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 $fe78 = 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 $fe7b = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||||
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $fe81 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
romsub $fe84 = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||||
; note: there is no FPWR() on the Cx16
|
romsub $fe8a = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
|
||||||
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
romsub $fe8d = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||||
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
romsub $fe96 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||||
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
romsub $fe99 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||||
romsub $fea2 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
romsub $fe9c = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||||
romsub $fea5 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
romsub $fe9f = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||||
romsub $fea8 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
romsub $fea2 = ATN() clobbers(A,X,Y) ; fac1 = ATN(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) {
|
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_W2
|
sta _tmp
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
ldy P8ZP_SCRATCH_W2
|
ldy _tmp
|
||||||
jsr GIVAYF ; load it as signed... correct afterwards
|
jsr GIVAYF ; load it as signed... correct afterwards
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
bpl +
|
bpl +
|
||||||
@ -91,6 +91,7 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
jsr FADD
|
jsr FADD
|
||||||
+ plx
|
+ plx
|
||||||
rts
|
rts
|
||||||
|
_tmp .byte 0
|
||||||
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -128,6 +129,14 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub FREADUY (ubyte value @Y) {
|
||||||
|
; -- 8 bit unsigned Y -> float in fac1
|
||||||
|
%asm {{
|
||||||
|
lda #0
|
||||||
|
jmp GIVAYF
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
sub print_f (float value) {
|
||||||
; ---- prints the floating point value (without a newline).
|
; ---- prints the floating point value (without a newline).
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -36,7 +36,7 @@ gfx2 {
|
|||||||
sub screen_mode(ubyte mode) {
|
sub screen_mode(ubyte mode) {
|
||||||
when mode {
|
when mode {
|
||||||
1 -> {
|
1 -> {
|
||||||
; lores monchrome
|
; lores monochrome
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 64
|
cx16.VERA_DC_HSCALE = 64
|
||||||
cx16.VERA_DC_VSCALE = 64
|
cx16.VERA_DC_VSCALE = 64
|
||||||
@ -404,7 +404,7 @@ _done
|
|||||||
; TODO also mostly usable for lores 4c?
|
; TODO also mostly usable for lores 4c?
|
||||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||||
|
|
||||||
; TODO optimize the loop in pure assembly
|
; TODO optimize this vertical line loop in pure assembly
|
||||||
color &= 3
|
color &= 3
|
||||||
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||||
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||||
@ -433,48 +433,46 @@ _done
|
|||||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||||
; Bresenham algorithm.
|
; Bresenham algorithm.
|
||||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||||
; TODO there are some slight errors at the first/last pixels in certain slopes...
|
|
||||||
if y1>y2 {
|
if y1>y2 {
|
||||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
}
|
}
|
||||||
word @zp dx = x2-x1 as word
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = y2-y1 as word
|
word @zp dy = (y2 as word)-y1
|
||||||
|
|
||||||
if dx==0 {
|
if dx==0 {
|
||||||
vertical_line(x1, y1, abs(dy)+1 as uword, color)
|
vertical_line(x1, y1, abs(dy) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dy==0 {
|
if dy==0 {
|
||||||
if x1>x2
|
if x1>x2
|
||||||
x1=x2
|
x1=x2
|
||||||
horizontal_line(x1, y1, abs(dx)+1 as uword, color)
|
horizontal_line(x1, y1, abs(dx) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
; TODO rewrite the rest in optimized assembly (or reuse GRAPH_draw_line if we can get the FB replacement vector layer working)
|
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
cx16.r13 = true ; 'positive_ix'
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
positive_ix = false
|
cx16.r13 = false
|
||||||
}
|
}
|
||||||
dx *= 2
|
word @zp dx2 = dx*2
|
||||||
dy *= 2
|
word @zp dy2 = dy*2
|
||||||
cx16.r14 = x1 ; internal plot X
|
cx16.r14 = x1 ; internal plot X
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -483,25 +481,25 @@ _done
|
|||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -510,10 +508,10 @@ _done
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
; Bitmap pixel graphics module for the CommanderX16
|
; Bitmap pixel graphics module for the CommanderX16
|
||||||
; wraps the graphics functions that are in ROM.
|
; wraps the graphics functions that are in ROM.
|
||||||
; only black/white monchrome 320x200 for now. (i.e. truncated at the bottom)
|
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
|
||||||
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
|
||||||
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; re
|
|||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
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 $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 $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. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See MEMTOP2
|
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom 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 $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
@ -35,7 +35,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
|
|||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
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 $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
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 secondary @ 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(X,Y) -> ubyte @Pc, ubyte @A ; (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
|
||||||
@ -44,10 +44,10 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
|
|||||||
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(X, 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, uword @ XY ; (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 (A=lo,X=mid,Y=high)
|
||||||
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
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 $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
|
||||||
@ -74,7 +74,7 @@ asmsub STOP2() -> ubyte @A {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub RDTIM16() -> uword @AY {
|
asmsub RDTIM16() -> uword @AY {
|
||||||
; -- like RDTIM() but only returning the lower 16 bits for convenience
|
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
jsr c64.RDTIM
|
jsr c64.RDTIM
|
||||||
@ -87,17 +87,6 @@ asmsub RDTIM16() -> uword @AY {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub MEMTOP2() -> ubyte @A {
|
|
||||||
; -- uses MEMTOP's cx16 extension to query the number of available RAM banks.
|
|
||||||
%asm {{
|
|
||||||
phx
|
|
||||||
sec
|
|
||||||
jsr c64.MEMTOP
|
|
||||||
plx
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@ -172,7 +161,7 @@ cx16 {
|
|||||||
|
|
||||||
; I/O
|
; I/O
|
||||||
|
|
||||||
const uword via1 = $9f60 ;VIA 6522 #1
|
const uword via1 = $9f00 ;VIA 6522 #1
|
||||||
&ubyte d1prb = via1+0
|
&ubyte d1prb = via1+0
|
||||||
&ubyte d1pra = via1+1
|
&ubyte d1pra = via1+1
|
||||||
&ubyte d1ddrb = via1+2
|
&ubyte d1ddrb = via1+2
|
||||||
@ -190,7 +179,7 @@ cx16 {
|
|||||||
&ubyte d1ier = via1+14
|
&ubyte d1ier = via1+14
|
||||||
&ubyte d1ora = via1+15
|
&ubyte d1ora = via1+15
|
||||||
|
|
||||||
const uword via2 = $9f70 ;VIA 6522 #2
|
const uword via2 = $9f10 ;VIA 6522 #2
|
||||||
&ubyte d2prb = via2+0
|
&ubyte d2prb = via2+0
|
||||||
&ubyte d2pra = via2+1
|
&ubyte d2pra = via2+1
|
||||||
&ubyte d2ddrb = via2+2
|
&ubyte d2ddrb = via2+2
|
||||||
@ -208,6 +197,11 @@ cx16 {
|
|||||||
&ubyte d2ier = via2+14
|
&ubyte d2ier = via2+14
|
||||||
&ubyte d2ora = via2+15
|
&ubyte d2ora = via2+15
|
||||||
|
|
||||||
|
&ubyte ym2151adr = $9f40
|
||||||
|
&ubyte ym2151dat = $9f41
|
||||||
|
|
||||||
|
const uword extdev = $9f60
|
||||||
|
|
||||||
|
|
||||||
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||||
; spelling of the names is taken from the Commander X-16 rom sources
|
; spelling of the names is taken from the Commander X-16 rom sources
|
||||||
@ -294,16 +288,25 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
|||||||
inline asmsub rombank(ubyte rombank @A) {
|
inline asmsub rombank(ubyte rombank @A) {
|
||||||
; -- set the rom banks
|
; -- set the rom banks
|
||||||
%asm {{
|
%asm {{
|
||||||
sta $01 ; rom bank register (new)
|
sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
||||||
sta cx16.d1prb ; rom bank register (old)
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline asmsub rambank(ubyte rambank @A) {
|
inline asmsub rambank(ubyte rambank @A) {
|
||||||
; -- set the ram bank
|
; -- set the ram bank
|
||||||
%asm {{
|
%asm {{
|
||||||
sta $00 ; ram bank register (new)
|
sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
||||||
sta cx16.d1pra ; ram bank register (old)
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub numbanks() -> ubyte @A {
|
||||||
|
; -- uses MEMTOP's cx16 extension to query the number of available RAM banks. (each is 8 Kb)
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
sec
|
||||||
|
jsr c64.MEMTOP
|
||||||
|
plx
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,6 +422,57 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
|
||||||
|
; -- like the basic command VLOAD "filename",device,bank,address
|
||||||
|
; loads a file into video memory in the given bank:address, returns success in A
|
||||||
|
; !! NOTE !! the V38 ROMs contain a bug in the LOAD code that makes the load address not work correctly,
|
||||||
|
; it works fine when loading from local filesystem
|
||||||
|
%asm {{
|
||||||
|
; -- load a file into video ram
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
tax
|
||||||
|
lda #1
|
||||||
|
ldy #0
|
||||||
|
jsr c64.SETLFS
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
tya
|
||||||
|
ldx cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jsr c64.SETNAM
|
||||||
|
pla
|
||||||
|
clc
|
||||||
|
adc #2
|
||||||
|
ldx cx16.r1
|
||||||
|
ldy cx16.r1+1
|
||||||
|
stz P8ZP_SCRATCH_B1
|
||||||
|
jsr c64.LOAD
|
||||||
|
bcs +
|
||||||
|
inc P8ZP_SCRATCH_B1
|
||||||
|
+ jsr c64.CLRCHN
|
||||||
|
lda #1
|
||||||
|
jsr c64.CLOSE
|
||||||
|
plx
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmsub joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX {
|
||||||
|
; convenience routine to get the joystick state without requiring inline assembly that deals with the multiple return values.
|
||||||
|
; Also disables interrupts to avoid the IRQ race condition mentioned here: https://github.com/commanderx16/x16-rom/issues/203
|
||||||
|
; TODO once that issue is resolved, this routine can be redefined as: romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
jsr cx16.joystick_get
|
||||||
|
cli
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
||||||
%asm {{
|
%asm {{
|
||||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
||||||
@ -460,11 +514,9 @@ asmsub init_system() {
|
|||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
cld
|
cld
|
||||||
;stz $00
|
|
||||||
;stz $01
|
|
||||||
;stz d1prb ; select rom bank 0 (enable kernal)
|
|
||||||
lda #$80
|
lda #$80
|
||||||
sta VERA_CTRL
|
sta VERA_CTRL
|
||||||
|
stz $01 ; select rom bank 0 (enable kernal)
|
||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
jsr c64.RESTOR
|
jsr c64.RESTOR
|
||||||
jsr c64.CINT
|
jsr c64.CINT
|
||||||
@ -664,11 +716,10 @@ sys {
|
|||||||
|
|
||||||
|
|
||||||
asmsub reset_system() {
|
asmsub reset_system() {
|
||||||
; Soft-reset the system back to Basic prompt.
|
; Soft-reset the system back to initial power-on Basic prompt.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
stz $01 ; bank the kernal in (new rom bank register)
|
stz $01 ; bank the kernal in
|
||||||
stz cx16.d1prb ; bank the kernal in (old rom bank register)
|
|
||||||
jmp (cx16.RESET_VEC)
|
jmp (cx16.RESET_VEC)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -683,6 +734,21 @@ sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub waitvsync() clobbers(A, X, Y) {
|
||||||
|
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||||
|
; note: system vsync irq handler has to be active for this routine to work.
|
||||||
|
; note 2: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||||
|
%asm {{
|
||||||
|
jsr c64.RDTIM
|
||||||
|
sta _mod + 1
|
||||||
|
inc _mod + 1
|
||||||
|
_loop jsr c64.RDTIM
|
||||||
|
_mod cmp #255 ; modified
|
||||||
|
bne _loop
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
inline asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sta cx16.r2
|
sta cx16.r2
|
||||||
|
@ -420,7 +420,7 @@ _print_byte_digits
|
|||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
pla
|
pla
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
jmp _ones
|
bra _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq _ones
|
beq _ones
|
||||||
@ -443,7 +443,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
|
|||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
+ pla
|
+ pla
|
||||||
jsr conv.byte2decimal
|
jsr conv.byte2decimal
|
||||||
jmp print_ub._print_byte_digits
|
bra print_ub._print_byte_digits
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,7 +494,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
jsr print_ubbin
|
jsr print_ubbin
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
jmp print_ubbin
|
bra print_ubbin
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +507,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
|||||||
jsr print_ubhex
|
jsr print_ubhex
|
||||||
pla
|
pla
|
||||||
clc
|
clc
|
||||||
jmp print_ubhex
|
bra print_ubhex
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +570,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
|
|||||||
adc #1
|
adc #1
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jmp print_uw
|
+ bra print_uw
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,6 +365,7 @@ io_error:
|
|||||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
c64.SETLFS(1, drivenumber, 0)
|
||||||
uword end_address = address + size
|
uword end_address = address + size
|
||||||
|
first_byte = 0 ; result var reuse
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda address
|
lda address
|
||||||
@ -381,7 +382,6 @@ io_error:
|
|||||||
plp
|
plp
|
||||||
}}
|
}}
|
||||||
|
|
||||||
first_byte = 0 ; result var reuse
|
|
||||||
if_cc
|
if_cc
|
||||||
first_byte = c64.READST()==0
|
first_byte = c64.READST()==0
|
||||||
|
|
||||||
|
@ -244,8 +244,8 @@ randseed .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randbyte .proc
|
fast_randbyte .proc
|
||||||
; -- 8-bit pseudo random number generator into A
|
; -- fast but bad 8-bit pseudo random number generator into A
|
||||||
lda _seed
|
lda _seed
|
||||||
beq _eor
|
beq _eor
|
||||||
asl a
|
asl a
|
||||||
@ -263,6 +263,10 @@ _seed .byte $3a
|
|||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
randbyte .proc
|
||||||
|
; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||||
|
jmp randword
|
||||||
|
.pend
|
||||||
|
|
||||||
randword .proc
|
randword .proc
|
||||||
; -- 16 bit pseudo random number generator into AY
|
; -- 16 bit pseudo random number generator into AY
|
||||||
@ -1537,3 +1541,71 @@ _negative lsr a
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
square .proc
|
||||||
|
; -- calculate square root of signed word in AY, result in AY
|
||||||
|
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
|
||||||
|
; using this routine is about twice as fast as doing a regular multiplication.
|
||||||
|
;
|
||||||
|
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
|
||||||
|
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
|
||||||
|
; Squarel/Squareh
|
||||||
|
;
|
||||||
|
; The maximum input range is only +/-255 and no checking is done to ensure that
|
||||||
|
; this is so.
|
||||||
|
;
|
||||||
|
; This routine is useful if you are trying to draw circles as for any circle
|
||||||
|
;
|
||||||
|
; x^2+y^2=r^2 where x and y are the co-ordinates of any point on the circle and
|
||||||
|
; r is the circle radius
|
||||||
|
|
||||||
|
numberl = P8ZP_SCRATCH_W1 ; number to square low byte
|
||||||
|
numberh = P8ZP_SCRATCH_W1+1 ; number to square high byte
|
||||||
|
squarel = P8ZP_SCRATCH_W2 ; square low byte
|
||||||
|
squareh = P8ZP_SCRATCH_W2+1 ; square high byte
|
||||||
|
tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
|
||||||
|
|
||||||
|
sta numberl
|
||||||
|
sty numberh
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
|
||||||
|
lda #$00 ; clear a
|
||||||
|
sta squarel ; clear square low byte
|
||||||
|
; (no need to clear the high byte, it gets shifted out)
|
||||||
|
lda numberl ; get number low byte
|
||||||
|
ldx numberh ; get number high byte
|
||||||
|
bpl _nonneg ; if +ve don't negate it
|
||||||
|
; else do a two's complement
|
||||||
|
eor #$ff ; invert
|
||||||
|
sec ; +1
|
||||||
|
adc #$00 ; and add it
|
||||||
|
|
||||||
|
_nonneg:
|
||||||
|
sta tempsq ; save abs(number)
|
||||||
|
ldx #$08 ; set bit count
|
||||||
|
|
||||||
|
_nextr2bit:
|
||||||
|
asl squarel ; low byte *2
|
||||||
|
rol squareh ; high byte *2+carry from low
|
||||||
|
asl a ; shift number byte
|
||||||
|
bcc _nosqadd ; don't do add if c = 0
|
||||||
|
tay ; save a
|
||||||
|
clc ; clear carry for add
|
||||||
|
lda tempsq ; get number
|
||||||
|
adc squarel ; add number^2 low byte
|
||||||
|
sta squarel ; save number^2 low byte
|
||||||
|
lda #$00 ; clear a
|
||||||
|
adc squareh ; add number^2 high byte
|
||||||
|
sta squareh ; save number^2 high byte
|
||||||
|
tya ; get a back
|
||||||
|
|
||||||
|
_nosqadd:
|
||||||
|
dex ; decrement bit count
|
||||||
|
bne _nextr2bit ; go do next bit
|
||||||
|
|
||||||
|
lda squarel
|
||||||
|
ldy squareh
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
.pend
|
||||||
|
@ -387,6 +387,14 @@ func_sqrt16_into_A .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
func_fastrnd8_stack .proc
|
||||||
|
; -- put a random ubyte on the estack (using fast but bad RNG)
|
||||||
|
jsr math.fast_randbyte
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
func_rnd_stack .proc
|
func_rnd_stack .proc
|
||||||
; -- put a random ubyte on the estack
|
; -- put a random ubyte on the estack
|
||||||
jsr math.randbyte
|
jsr math.randbyte
|
||||||
@ -432,6 +440,7 @@ func_min_ub_stack .proc
|
|||||||
func_min_b_into_A .proc
|
func_min_b_into_A .proc
|
||||||
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #127
|
lda #127
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
@ -548,6 +557,7 @@ func_min_w_stack .proc
|
|||||||
func_max_ub_into_A .proc
|
func_max_ub_into_A .proc
|
||||||
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #0
|
lda #0
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
@ -1072,3 +1072,14 @@ sign_extend_AY_byte .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
strlen .proc
|
||||||
|
; -- returns the number of bytes in the string in AY, in Y.
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
6.2
|
6.5-BETA
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -8,27 +9,28 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
subroutineVariables.add(decl.name to decl)
|
subroutineVariables.add(decl.name to decl)
|
||||||
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
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,
|
// A numeric vardecl without an initial value is initialized with zero,
|
||||||
// unless there's already an assignment below, that initializes the value
|
// unless there's already an assignment below, that initializes the value.
|
||||||
|
// This allows you to restart the program and have the same starting values of the variables
|
||||||
if(decl.allowInitializeWithZero)
|
if(decl.allowInitializeWithZero)
|
||||||
{
|
{
|
||||||
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
|
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
|
||||||
if (nextAssign != null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY)))
|
if (nextAssign != null && nextAssign.target isSameAs IdentifierReference(listOf(decl.name), Position.DUMMY))
|
||||||
decl.value = null
|
decl.value = null
|
||||||
else
|
else {
|
||||||
decl.value = decl.zeroElementValue()
|
decl.value = decl.zeroElementValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,9 +69,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val subroutineVariables = mutableListOf<Pair<String, VarDecl>>()
|
private val subroutineVariables = mutableListOf<Pair<String, VarDecl>>()
|
||||||
|
private val addedIfConditionVars = mutableSetOf<Pair<Subroutine, String>>()
|
||||||
|
|
||||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
subroutineVariables.clear()
|
subroutineVariables.clear()
|
||||||
|
addedIfConditionVars.clear()
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,17 +84,22 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
val sub = scope.definingSubroutine()
|
val sub = scope.definingSubroutine()
|
||||||
if (sub != null) {
|
if (sub != null) {
|
||||||
// move vardecls of the scope into the upper scope. Make sure the position remains the same!
|
// 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 replacements = mutableListOf<IAstModification>()
|
||||||
val replaceVardecls =numericVarsWithValue.map {
|
val movements = mutableListOf<IAstModification.InsertFirst>()
|
||||||
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)
|
for(decl in decls) {
|
||||||
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
if(decl.value!=null && decl.datatype in NumericDatatypes) {
|
||||||
val assign = Assignment(target, initValue, it.position)
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
initValue.parent = assign
|
val assign = Assignment(target, decl.value!!, decl.position)
|
||||||
IAstModification.ReplaceNode(it, assign, scope)
|
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||||
|
decl.value = null
|
||||||
|
decl.allowInitializeWithZero = false
|
||||||
|
} else {
|
||||||
|
replacements.add(IAstModification.Remove(decl, scope))
|
||||||
}
|
}
|
||||||
val moveVardeclsUp = decls.map { IAstModification.InsertFirst(it, sub) }
|
movements.add(IAstModification.InsertFirst(decl, sub))
|
||||||
return replaceVardecls + moveVardeclsUp
|
}
|
||||||
|
return replacements + movements
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -154,16 +163,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
// The only place for now where we can do this is for:
|
// The only place for now where we can do this is for:
|
||||||
// asmsub register pair parameter.
|
// asmsub register pair parameter.
|
||||||
|
|
||||||
if(typecast.type in WordDatatypes) {
|
|
||||||
val fcall = typecast.parent as? IFunctionCall
|
|
||||||
if (fcall != null) {
|
|
||||||
val sub = fcall.target.targetStatement(program) as? Subroutine
|
|
||||||
if (sub != null && sub.isAsmSubroutine) {
|
|
||||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
if(sourceDt in PassByReferenceDatatypes) {
|
||||||
if(typecast.type==DataType.UWORD) {
|
if(typecast.type==DataType.UWORD) {
|
||||||
if(typecast.expression is IdentifierReference) {
|
if(typecast.expression is IdentifierReference) {
|
||||||
@ -194,9 +193,53 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
|
val booleanExpr = BinaryExpression(ifStatement.condition, "!=", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
|
||||||
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
|
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((binExpr.left as? NumericLiteralValue)?.number==0)
|
||||||
|
throw CompilerException("if 0==X should have been swapped to if X==0")
|
||||||
|
|
||||||
|
// split the conditional expression into separate variables if the operand(s) is not simple.
|
||||||
|
// DISABLED FOR NOW AS IT GENEREATES LARGER CODE IN THE SIMPLE CASES LIKE IF X {...} or IF NOT X {...}
|
||||||
|
// val modifications = mutableListOf<IAstModification>()
|
||||||
|
// if(!binExpr.left.isSimple) {
|
||||||
|
// val sub = binExpr.definingSubroutine()!!
|
||||||
|
// val (variable, isNew, assignment) = addIfOperandVar(sub, "left", binExpr.left)
|
||||||
|
// if(isNew)
|
||||||
|
// modifications.add(IAstModification.InsertFirst(variable, sub))
|
||||||
|
// modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope))
|
||||||
|
// modifications.add(IAstModification.ReplaceNode(binExpr.left, IdentifierReference(listOf(variable.name), binExpr.position), binExpr))
|
||||||
|
// addedIfConditionVars.add(Pair(sub, variable.name))
|
||||||
|
// }
|
||||||
|
// if(!binExpr.right.isSimple) {
|
||||||
|
// val sub = binExpr.definingSubroutine()!!
|
||||||
|
// val (variable, isNew, assignment) = addIfOperandVar(sub, "right", binExpr.right)
|
||||||
|
// if(isNew)
|
||||||
|
// modifications.add(IAstModification.InsertFirst(variable, sub))
|
||||||
|
// modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope))
|
||||||
|
// modifications.add(IAstModification.ReplaceNode(binExpr.right, IdentifierReference(listOf(variable.name), binExpr.position), binExpr))
|
||||||
|
// addedIfConditionVars.add(Pair(sub, variable.name))
|
||||||
|
// }
|
||||||
|
// return modifications
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private fun addIfOperandVar(sub: Subroutine, side: String, operand: Expression): Triple<VarDecl, Boolean, Assignment> {
|
||||||
|
// val dt = operand.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
// val varname = "prog8_ifvar_${side}_${dt.name.toLowerCase()}"
|
||||||
|
// val tgt = AssignTarget(IdentifierReference(listOf(varname), operand.position), null, null, operand.position)
|
||||||
|
// val assign = Assignment(tgt, operand, operand.position)
|
||||||
|
// if(Pair(sub, varname) in addedIfConditionVars) {
|
||||||
|
// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position)
|
||||||
|
// return Triple(vardecl, false, assign)
|
||||||
|
// }
|
||||||
|
// val existing = sub.statements.firstOrNull { it is VarDecl && it.name == varname} as VarDecl?
|
||||||
|
// return if (existing == null) {
|
||||||
|
// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position)
|
||||||
|
// Triple(vardecl, true, assign)
|
||||||
|
// } else {
|
||||||
|
// Triple(existing, false, assign)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
val binExpr = untilLoop.condition as? BinaryExpression
|
val binExpr = untilLoop.condition as? BinaryExpression
|
||||||
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
@ -216,4 +259,93 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(functionCallStatement.target.nameInSource==listOf("cmp")) {
|
||||||
|
// if the datatype of the arguments of cmp() are different, cast the byte one to word.
|
||||||
|
val arg1 = functionCallStatement.args[0]
|
||||||
|
val arg2 = functionCallStatement.args[1]
|
||||||
|
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if(dt1 in ByteDatatypes) {
|
||||||
|
if(dt2 in ByteDatatypes)
|
||||||
|
return noModifications
|
||||||
|
val cast1 = TypecastExpression(arg1, if(dt1==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(arg1, cast1, functionCallStatement))
|
||||||
|
} else {
|
||||||
|
if(dt2 in WordDatatypes)
|
||||||
|
return noModifications
|
||||||
|
val cast2 = TypecastExpression(arg2, if(dt2==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(arg2, cast2, functionCallStatement))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||||
|
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||||
|
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val index = arrayIndexedExpression.indexer.indexExpr
|
||||||
|
if(index !is NumericLiteralValue && index !is IdentifierReference) {
|
||||||
|
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||||
|
return getAutoIndexerVarFor(arrayIndexedExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getComplexArrayIndexedExpressions(stmt: Statement): List<ArrayIndexedExpression> {
|
||||||
|
|
||||||
|
class Searcher : IAstVisitor {
|
||||||
|
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
|
||||||
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
|
val ix = arrayIndexedExpression.indexer.indexExpr
|
||||||
|
if(ix !is NumericLiteralValue && ix !is IdentifierReference)
|
||||||
|
complexArrayIndexedExpressions.add(arrayIndexedExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(branchStatement: BranchStatement) {}
|
||||||
|
|
||||||
|
override fun visit(forLoop: ForLoop) {}
|
||||||
|
|
||||||
|
override fun visit(ifStatement: IfStatement) {
|
||||||
|
ifStatement.condition.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
|
untilLoop.condition.accept(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val searcher = Searcher()
|
||||||
|
stmt.accept(searcher)
|
||||||
|
return searcher.complexArrayIndexedExpressions
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContainingStatement(expression: Expression): Statement {
|
||||||
|
var node: Node = expression
|
||||||
|
while(node !is Statement)
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
val statement = expr.containingStatement()
|
||||||
|
// replace the indexer with just the variable (simply use a cx16 virtual register r9, that we HOPE is not used for other things in the expression...)
|
||||||
|
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf("cx16", "r9"), expr.indexer.position), null, null, expr.indexer.position)
|
||||||
|
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
|
||||||
|
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
|
||||||
|
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
|
||||||
|
return modifications
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ fun compileProgram(filepath: Path,
|
|||||||
importedFiles = imported
|
importedFiles = imported
|
||||||
processAst(programAst, errors, compilationOptions)
|
processAst(programAst, errors, compilationOptions)
|
||||||
if (compilationOptions.optimize)
|
if (compilationOptions.optimize)
|
||||||
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
|
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compilationOptions)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
@ -157,7 +157,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(func.known_returntype==null)
|
else if(func.known_returntype==null)
|
||||||
throw IllegalArgumentException("builtin function $name can't be used here because it doesn't return a value")
|
return null // builtin function $name can't be used here because it doesn't return a value
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -214,7 +214,7 @@ private fun determineCompilationOptions(program: Program, compTarget: ICompilati
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||||
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
|
System.err.println("Warning: zp option floatsafe changed to basicsafe for cx16 target")
|
||||||
zpType = ZeropageType.BASICSAFE
|
zpType = ZeropageType.BASICSAFE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,40 +253,54 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
|
|||||||
errors.report()
|
errors.report()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.variousCleanups()
|
programAst.variousCleanups(programAst, errors)
|
||||||
|
errors.report()
|
||||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget, options: CompilationOptions) {
|
||||||
// optimize the parse tree
|
// optimize the parse tree
|
||||||
println("Optimizing...")
|
println("Optimizing...")
|
||||||
|
|
||||||
|
val remover = UnusedCodeRemover(programAst, errors, compTarget)
|
||||||
|
remover.visit(programAst)
|
||||||
|
remover.applyModifications()
|
||||||
|
|
||||||
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.splitBinaryExpressions(compTarget)
|
val optsDone2 = programAst.splitBinaryExpressions(compTarget)
|
||||||
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget, ::loadAsmIncludeFile)
|
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget)
|
||||||
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||||
errors.report()
|
errors.report()
|
||||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
|
val inliner = SubroutineInliner(programAst, errors, options)
|
||||||
remover.visit(programAst)
|
inliner.visit(programAst)
|
||||||
remover.applyModifications()
|
errors.report()
|
||||||
|
if(errors.noErrors()) {
|
||||||
|
inliner.applyModifications()
|
||||||
|
inliner.fixCallsToInlinedSubroutines()
|
||||||
|
val remover2 = UnusedCodeRemover(programAst, errors, compTarget)
|
||||||
|
remover2.visit(programAst)
|
||||||
|
remover2.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.variousCleanups()
|
programAst.variousCleanups(programAst, errors)
|
||||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
||||||
errors.report()
|
errors.report()
|
||||||
val callGraph = CallGraph(programAst, ::loadAsmIncludeFile)
|
val callGraph = CallGraph(programAst)
|
||||||
callGraph.checkRecursiveCalls(errors)
|
callGraph.checkRecursiveCalls(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.verifyFunctionArgTypes()
|
programAst.verifyFunctionArgTypes()
|
||||||
@ -297,7 +311,7 @@ private fun writeAssembly(programAst: Program,
|
|||||||
errors: IErrorReporter,
|
errors: IErrorReporter,
|
||||||
outputDir: Path,
|
outputDir: Path,
|
||||||
compilerOptions: CompilationOptions): String {
|
compilerOptions: CompilationOptions): String {
|
||||||
// asm generation directly from the Ast,
|
// asm generation directly from the Ast
|
||||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import prog8.parser.ParsingFailedError
|
|||||||
interface IErrorReporter {
|
interface IErrorReporter {
|
||||||
fun err(msg: String, position: Position)
|
fun err(msg: String, position: Position)
|
||||||
fun warn(msg: String, position: Position)
|
fun warn(msg: String, position: Position)
|
||||||
fun isEmpty(): Boolean
|
fun noErrors(): Boolean
|
||||||
fun report()
|
fun report()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,5 +53,5 @@ internal class ErrorReporter: IErrorReporter {
|
|||||||
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEmpty() = messages.isEmpty()
|
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
|
import prog8.compiler.ZeropageType
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
import prog8.compiler.functions.builtinFunctionReturnType
|
import prog8.compiler.functions.builtinFunctionReturnType
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
@ -41,6 +42,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(compilerOptions.floats) {
|
||||||
|
if (compilerOptions.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
|
errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.mainModule.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(program)
|
super.visit(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +76,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(expectedReturnValues.size==1 && returnStmt.value!=null) {
|
if(expectedReturnValues.size==1 && returnStmt.value!=null) {
|
||||||
val valueDt = returnStmt.value!!.inferType(program)
|
val valueDt = returnStmt.value!!.inferType(program)
|
||||||
if(!valueDt.isKnown) {
|
if(!valueDt.isKnown) {
|
||||||
errors.err("return value type mismatch", returnStmt.value!!.position)
|
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
|
||||||
} else {
|
} else {
|
||||||
if (expectedReturnValues[0] != valueDt.typeOrElse(DataType.STRUCT))
|
if (expectedReturnValues[0] != valueDt.typeOrElse(DataType.STRUCT))
|
||||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}", returnStmt.value!!.position)
|
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}", returnStmt.value!!.position)
|
||||||
@ -118,7 +124,7 @@ 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()) {
|
if(errors.noErrors()) {
|
||||||
// check loop range values
|
// check loop range values
|
||||||
val range = forLoop.iterable as? RangeExpr
|
val range = forLoop.iterable as? RangeExpr
|
||||||
if(range!=null) {
|
if(range!=null) {
|
||||||
@ -203,13 +209,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(uniqueNames.size!=subroutine.parameters.size)
|
if(uniqueNames.size!=subroutine.parameters.size)
|
||||||
err("parameter names must be unique")
|
err("parameter names must be unique")
|
||||||
|
|
||||||
if(subroutine.inline) {
|
|
||||||
if (subroutine.containsDefinedVariables())
|
|
||||||
err("can't inline a subroutine that defines variables")
|
|
||||||
if (!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty())
|
|
||||||
err("can't inline a non-asm subroutine that has parameters")
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(subroutine)
|
super.visit(subroutine)
|
||||||
|
|
||||||
// user-defined subroutines can only have zero or one return type
|
// user-defined subroutines can only have zero or one return type
|
||||||
@ -218,13 +217,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("subroutines can only have one return value")
|
err("subroutines can only have one return value")
|
||||||
|
|
||||||
// subroutine must contain at least one 'return' or 'goto'
|
// subroutine must contain at least one 'return' or 'goto'
|
||||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp')
|
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
|
||||||
if(subroutine.statements.count { it is Return || it is Jump } == 0) {
|
if(subroutine.statements.count { it is Return || it is Jump } == 0) {
|
||||||
if (subroutine.amountOfRtsInAsm() == 0) {
|
if (subroutine.amountOfRtsInAsm() == 0) {
|
||||||
if (subroutine.returntypes.isNotEmpty()) {
|
if (subroutine.returntypes.isNotEmpty()) {
|
||||||
// for asm subroutines with an address, no statement check is possible.
|
// for asm subroutines with an address, no statement check is possible.
|
||||||
if (subroutine.asmAddress == null && !subroutine.inline)
|
if (subroutine.asmAddress == null && !subroutine.inline)
|
||||||
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)")
|
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +373,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!idt.isKnown) {
|
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() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) {
|
if(stmt.returntypes.isEmpty() || (stmt.returntypes.size == 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE))) {
|
||||||
errors.err("return type mismatch", assignment.value.position)
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,7 +492,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
fun err(msg: String, position: Position?=null) = 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?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true)
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.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)
|
||||||
@ -953,6 +952,20 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// functions that don't return a value, can't be used in an expression or assignment
|
||||||
|
if(targetStatement is Subroutine) {
|
||||||
|
if(targetStatement.returntypes.isEmpty()) {
|
||||||
|
if(functionCall.parent is Expression || functionCall.parent is Assignment)
|
||||||
|
errors.err("subroutine doesn't return a value", functionCall.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(targetStatement is BuiltinFunctionStatementPlaceholder) {
|
||||||
|
if(builtinFunctionReturnType(targetStatement.name, functionCall.args, program).isUnknown) {
|
||||||
|
if(functionCall.parent is Expression || functionCall.parent is Assignment)
|
||||||
|
errors.err("function doesn't return a value", functionCall.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1111,15 +1124,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
||||||
|
|
||||||
// check index value 0..255
|
// check index value 0..255
|
||||||
val dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
|
||||||
if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE)
|
if(!dtxNum.istype(DataType.UBYTE) && !dtxNum.istype(DataType.BYTE))
|
||||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
||||||
val dtxVar = arrayIndexedExpression.indexer.indexVar?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
|
||||||
if(dtxVar!=null && dtxVar != DataType.UBYTE && dtxVar != DataType.BYTE)
|
|
||||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
|
||||||
|
|
||||||
if(arrayIndexedExpression.indexer.origExpression!=null)
|
|
||||||
throw FatalAstException("array indexer should have been replaced with a temp var @ ${arrayIndexedExpression.indexer.position}")
|
|
||||||
|
|
||||||
super.visit(arrayIndexedExpression)
|
super.visit(arrayIndexedExpression)
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,20 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: IEr
|
|||||||
internal fun Program.processAstBeforeAsmGeneration(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
internal fun Program.processAstBeforeAsmGeneration(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
|
val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
|
||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
fixer.applyModifications()
|
while(errors.noErrors() && fixer.applyModifications()>0) {
|
||||||
|
fixer.visit(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||||
val reorder = StatementReorderer(this, errors)
|
val reorder = StatementReorderer(this, errors)
|
||||||
reorder.visit(this)
|
reorder.visit(this)
|
||||||
|
if(errors.noErrors()) {
|
||||||
reorder.applyModifications()
|
reorder.applyModifications()
|
||||||
|
reorder.visit(this)
|
||||||
|
if(errors.noErrors())
|
||||||
|
reorder.applyModifications()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.addTypecasts(errors: IErrorReporter) {
|
internal fun Program.addTypecasts(errors: IErrorReporter) {
|
||||||
@ -42,7 +49,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompi
|
|||||||
val checker2 = AstIdentifiersChecker(this, errors, compTarget)
|
val checker2 = AstIdentifiersChecker(this, errors, compTarget)
|
||||||
checker2.visit(this)
|
checker2.visit(this)
|
||||||
|
|
||||||
if(errors.isEmpty()) {
|
if(errors.noErrors()) {
|
||||||
val transforms = AstVariousTransforms(this)
|
val transforms = AstVariousTransforms(this)
|
||||||
transforms.visit(this)
|
transforms.visit(this)
|
||||||
transforms.applyModifications()
|
transforms.applyModifications()
|
||||||
@ -56,12 +63,14 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.variousCleanups() {
|
internal fun Program.variousCleanups(program: Program, errors: IErrorReporter) {
|
||||||
val process = VariousCleanups()
|
val process = VariousCleanups(program, errors)
|
||||||
process.visit(this)
|
process.visit(this)
|
||||||
|
if(errors.noErrors())
|
||||||
process.applyModifications()
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.moveMainAndStartToFirst() {
|
internal fun Program.moveMainAndStartToFirst() {
|
||||||
// the module containing the program entrypoint is moved to the first in the sequence.
|
// the module containing the program entrypoint is moved to the first in the sequence.
|
||||||
// the "main" block containing the entrypoint is moved to the top in there,
|
// the "main" block containing the entrypoint is moved to the top in there,
|
||||||
@ -69,7 +78,6 @@ internal fun Program.moveMainAndStartToFirst() {
|
|||||||
|
|
||||||
val directives = modules[0].statements.filterIsInstance<Directive>()
|
val directives = modules[0].statements.filterIsInstance<Directive>()
|
||||||
val start = this.entrypoint()
|
val start = this.entrypoint()
|
||||||
if(start!=null) {
|
|
||||||
val mod = start.definingModule()
|
val mod = start.definingModule()
|
||||||
val block = start.definingBlock()
|
val block = start.definingBlock()
|
||||||
if(!modules.remove(mod))
|
if(!modules.remove(mod))
|
||||||
@ -93,5 +101,4 @@ internal fun Program.moveMainAndStartToFirst() {
|
|||||||
modules[0].statements.removeAll { it is Directive && it.directive == directive.directive }
|
modules[0].statements.removeAll { it is Directive && it.directive == directive.directive }
|
||||||
modules[0].statements.add(0, directive)
|
modules[0].statements.add(0, directive)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import prog8.ast.walk.IAstModification
|
|||||||
|
|
||||||
|
|
||||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
@ -23,9 +22,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
val decls = decl.flattenStructMembers()
|
val decls = decl.flattenStructMembers()
|
||||||
decls.add(decl)
|
decls.add(decl)
|
||||||
val result = AnonymousScope(decls, decl.position)
|
val result = AnonymousScope(decls, decl.position)
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(decl, result, parent))
|
||||||
decl, result, parent
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
|
@ -13,7 +13,6 @@ import prog8.ast.walk.IAstModification
|
|||||||
|
|
||||||
|
|
||||||
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||||
|
@ -23,7 +23,6 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
// - 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> {
|
||||||
@ -89,8 +88,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||||
// rewrite pointervar[index] into @(pointervar+index)
|
// rewrite pointervar[index] into @(pointervar+index)
|
||||||
val indexer = arrayIndexedExpression.indexer
|
val indexer = arrayIndexedExpression.indexer
|
||||||
val index = (indexer.indexNum ?: indexer.indexVar)!!
|
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||||
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
|
|
||||||
return if(parent is AssignTarget) {
|
return if(parent is AssignTarget) {
|
||||||
// we're part of the target of an assignment, we have to actually change the assign target itself
|
// we're part of the target of an assignment, we have to actually change the assign target itself
|
||||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||||
@ -102,24 +100,8 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
arrayIndexedExpression.indexer.indexNum = expr2
|
|
||||||
arrayIndexedExpression.indexer.origExpression = null
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
|
||||||
arrayIndexedExpression.indexer.indexVar = expr2
|
|
||||||
arrayIndexedExpression.indexer.origExpression = null
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
is Expression -> {
|
|
||||||
// replace complex indexing with a temp variable
|
|
||||||
return getAutoIndexerVarFor(arrayIndexedExpression)
|
|
||||||
}
|
|
||||||
else -> return noModifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// when using a simple bit shift and assigning it to a variable of a different type,
|
// when using a simple bit shift and assigning it to a variable of a different type,
|
||||||
@ -202,38 +184,6 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
val subroutine = expr.definingSubroutine()!!
|
|
||||||
val statement = expr.containingStatement()
|
|
||||||
val indexerVarPrefix = "prog8_autovar_index_"
|
|
||||||
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
|
|
||||||
|
|
||||||
// TODO make this a bit smarter so it can reuse indexer variables. BUT BEWARE of scoping+initialization problems then
|
|
||||||
// add another loop index var to be used for this expression
|
|
||||||
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
|
|
||||||
val indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer)
|
|
||||||
repo.add(indexerVar)
|
|
||||||
// create the indexer var at block level scope
|
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
|
|
||||||
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
|
|
||||||
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
|
|
||||||
|
|
||||||
// replace the indexer with just the variable
|
|
||||||
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
|
||||||
val indexerExpression = expr.indexer.origExpression!!
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
|
|
||||||
val assign = Assignment(target, indexerExpression, indexerExpression.position)
|
|
||||||
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
|
|
||||||
modifications.add(IAstModification.SetExpression( {
|
|
||||||
expr.indexer.indexVar = it as IdentifierReference
|
|
||||||
expr.indexer.indexNum = null
|
|
||||||
expr.indexer.origExpression = null
|
|
||||||
}, target.identifier!!.copy(), expr.indexer))
|
|
||||||
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
it.first?.first() ?: Int.MAX_VALUE
|
it.first?.first() ?: Int.MAX_VALUE
|
||||||
@ -248,52 +198,40 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
val declConstValue = declValue.constValue(program)
|
val declConstValue = declValue.constValue(program)
|
||||||
if(declConstValue==null) {
|
if(declConstValue==null) {
|
||||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
// move the vardecl (without value) to the scope of the defining subroutine and put a regular assignment in its place here.
|
||||||
// Unless we're dealing with a floating point variable because that will actually make things less efficient at the moment (because floats are mostly calcualated via the stack)
|
|
||||||
if(decl.datatype!=DataType.FLOAT) {
|
|
||||||
decl.value = null
|
decl.value = null
|
||||||
decl.allowInitializeWithZero = false
|
decl.allowInitializeWithZero = false
|
||||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
val assign = Assignment(target, declValue, decl.position)
|
val assign = Assignment(target, declValue, decl.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
IAstModification.ReplaceNode(decl, assign, parent),
|
||||||
IAstModification.InsertFirst(decl, decl.definingScope())
|
IAstModification.InsertFirst(decl, decl.definingSubroutine() as INameScope)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
val valueType = assignment.value.inferType(program)
|
val valueType = assignment.value.inferType(program)
|
||||||
val targetType = assignment.target.inferType(program)
|
val targetType = assignment.target.inferType(program)
|
||||||
var assignments = emptyList<Assignment>()
|
|
||||||
|
|
||||||
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
|
errors.err("cannot assign array literal here, use separate assignment per field", assignment.position)
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
return copyStructValue(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
errors.err("cannot assign array literal here, use separate assignment per element", assignment.position)
|
||||||
} else {
|
} else {
|
||||||
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
return copyArrayValue(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(assignments.isNotEmpty()) {
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
val scope = assignment.definingScope()
|
|
||||||
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) }
|
|
||||||
modifications.add(IAstModification.Remove(assignment, scope))
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,114 +284,98 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
|
private fun copyArrayValue(assign: Assignment): List<IAstModification> {
|
||||||
val identifier = assign.target.identifier!!
|
val identifier = assign.target.identifier!!
|
||||||
val targetVar = identifier.targetVarDecl(program)!!
|
val targetVar = identifier.targetVarDecl(program)!!
|
||||||
val alv = assign.value as? ArrayLiteralValue
|
|
||||||
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
|
if(targetVar.arraysize==null)
|
||||||
val identifier = assign.target.identifier!!
|
errors.err("array has no defined size", assign.position)
|
||||||
val targetVar = identifier.targetVarDecl(program)!!
|
|
||||||
|
if(assign.value !is IdentifierReference) {
|
||||||
|
errors.err("invalid array value to assign to other array", assign.value.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
val sourceIdent = assign.value as IdentifierReference
|
val sourceIdent = assign.value as IdentifierReference
|
||||||
val sourceVar = sourceIdent.targetVarDecl(program)!!
|
val sourceVar = sourceIdent.targetVarDecl(program)!!
|
||||||
if(!sourceVar.isArray) {
|
if(!sourceVar.isArray) {
|
||||||
errors.err("value must be an array", sourceIdent.position)
|
errors.err("value must be an array", sourceIdent.position)
|
||||||
return emptyList()
|
} else {
|
||||||
}
|
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
|
||||||
val alv = sourceVar.value as? ArrayLiteralValue
|
errors.err("element count mismatch", assign.position)
|
||||||
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
if (sourceVar.datatype != targetVar.datatype)
|
||||||
|
errors.err("element type mismatch", assign.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> {
|
if(!errors.noErrors())
|
||||||
if(targetVar.arraysize==null) {
|
return noModifications
|
||||||
errors.err("array has no defined size", identifier.position)
|
|
||||||
return emptyList()
|
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position),
|
||||||
|
mutableListOf(
|
||||||
|
AddressOf(sourceIdent, assign.position),
|
||||||
|
AddressOf(identifier, assign.position),
|
||||||
|
NumericLiteralValue.optimalInteger(targetVar.arraysize!!.constIndex()!!, assign.position)
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
assign.position
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
private fun copyStructValue(structAssignment: Assignment): List<IAstModification> {
|
||||||
errors.err("element count mismatch", position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO use memcopy instead of individual assignments after certain amount of elements
|
|
||||||
// TODO what does assigning a struct var use?
|
|
||||||
return alv.value.mapIndexed { index, value ->
|
|
||||||
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
|
|
||||||
Assignment(AssignTarget(null, idx, null, 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 targetVar = identifier.targetVarDecl(program)!!
|
|
||||||
val struct = targetVar.struct!!
|
|
||||||
|
|
||||||
val slv = structAssignment.value as? ArrayLiteralValue
|
|
||||||
if(slv==null || slv.value.size != struct.numberOfElements) {
|
|
||||||
errors.err("element count mismatch", structAssignment.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
|
||||||
targetDecl as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
|
||||||
sourceValue, sourceValue.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
|
|
||||||
// TODO use memcopy beyond a certain number of elements
|
|
||||||
val identifier = structAssignment.target.identifier!!
|
|
||||||
val identifierName = identifier.nameInSource.single()
|
|
||||||
val targetVar = identifier.targetVarDecl(program)!!
|
val targetVar = identifier.targetVarDecl(program)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
when (structAssignment.value) {
|
when (structAssignment.value) {
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
|
||||||
|
val memsize = struct.memsize(program.memsizer)
|
||||||
when {
|
when {
|
||||||
sourceVar.struct!=null -> {
|
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
|
errors.err("struct type mismatch", structAssignment.position)
|
||||||
return listOf() // error will be printed elsewhere
|
return listOf()
|
||||||
}
|
}
|
||||||
if(struct.statements.size!=sourceStruct.statements.size)
|
if(struct.statements.size!=sourceStruct.statements.size) {
|
||||||
return listOf() // error will be printed elsewhere
|
errors.err("struct element count mismatch", structAssignment.position)
|
||||||
return struct.statements.zip(sourceStruct.statements).map { member ->
|
return listOf()
|
||||||
val targetDecl = member.first as VarDecl
|
|
||||||
val sourceDecl = member.second as VarDecl
|
|
||||||
if(targetDecl.name != sourceDecl.name)
|
|
||||||
throw FatalAstException("struct member mismatch")
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
|
||||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
}
|
||||||
|
if(memsize!=sourceStruct.memsize(program.memsizer)) {
|
||||||
|
errors.err("memory size mismatch", structAssignment.position)
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
|
||||||
|
mutableListOf(
|
||||||
|
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
|
||||||
|
AddressOf(identifier, structAssignment.position),
|
||||||
|
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
structAssignment.position
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
|
||||||
}
|
}
|
||||||
sourceVar.isArray -> {
|
sourceVar.isArray -> {
|
||||||
val array = (sourceVar.value as ArrayLiteralValue).value
|
val array = sourceVar.value as ArrayLiteralValue
|
||||||
if(struct.statements.size!=array.size)
|
if(struct.statements.size!=array.value.size) {
|
||||||
return listOf() // error will be printed elsewhere
|
errors.err("struct element count mismatch", structAssignment.position)
|
||||||
return struct.statements.zip(array).map {
|
return listOf()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
if(memsize!=array.memsize(program.memsizer)) {
|
||||||
|
errors.err("memory size mismatch", structAssignment.position)
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
|
||||||
|
mutableListOf(
|
||||||
|
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
|
||||||
|
AddressOf(identifier, structAssignment.position),
|
||||||
|
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
structAssignment.position
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
throw FatalAstException("can only assign arrays or structs to structs")
|
||||||
@ -461,7 +383,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
throw IllegalArgumentException("not going to do a structLv assignment here")
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("strange struct value")
|
else -> throw FatalAstException("strange struct value")
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,6 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
|||||||
* (this includes function call arguments)
|
* (this includes function call arguments)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
||||||
|
@ -3,18 +3,17 @@ package prog8.compiler.astprocessing
|
|||||||
import prog8.ast.IFunctionCall
|
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.base.FatalAstException
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.DirectMemoryRead
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.FunctionCall
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.TypecastExpression
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.compiler.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal class VariousCleanups: AstWalker() {
|
internal class VariousCleanups(val program: Program, val errors: IErrorReporter): AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
|
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
|
||||||
@ -32,21 +31,12 @@ internal class VariousCleanups: AstWalker() {
|
|||||||
val idx = into.statements.indexOf(scope)
|
val idx = into.statements.indexOf(scope)
|
||||||
if(idx>=0) {
|
if(idx>=0) {
|
||||||
into.statements.addAll(idx+1, scope.statements)
|
into.statements.addAll(idx+1, scope.statements)
|
||||||
|
scope.statements.forEach { it.parent = into as Node }
|
||||||
into.statements.remove(scope)
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return before(functionCallStatement as IFunctionCall, parent, functionCallStatement.position)
|
return before(functionCallStatement as IFunctionCall, parent, functionCallStatement.position)
|
||||||
}
|
}
|
||||||
@ -70,4 +60,62 @@ internal class VariousCleanups: AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(typecast.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $typecast")
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourceDt = typecast.expression.inferType(program)
|
||||||
|
if(sourceDt.istype(typecast.type))
|
||||||
|
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(subroutine.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $subroutine")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignment.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $assignment")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(assignTarget.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $assignTarget")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(decl.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $decl")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(scope.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $scope")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(returnStmt.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $returnStmt")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(identifier.parent!==parent)
|
||||||
|
throw FatalAstException("parent node mismatch at $identifier")
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
|
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
|
||||||
// these few have a return value depending on the argument(s):
|
// these few have a return value depending on the argument(s):
|
||||||
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||||
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||||
@ -137,6 +138,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
|
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
|
||||||
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
|
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
|
||||||
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
|
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
|
||||||
|
FSignature("fastrnd8" , false, emptyList(), DataType.UBYTE),
|
||||||
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
|
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
|
||||||
FSignature("rndw" , false, emptyList(), DataType.UWORD),
|
FSignature("rndw" , false, emptyList(), DataType.UWORD),
|
||||||
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
||||||
@ -320,8 +322,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
|||||||
val target = (args[0] as IdentifierReference).targetStatement(program)
|
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||||
?: throw CannotEvaluateException("sizeof", "no target")
|
?: throw CannotEvaluateException("sizeof", "no target")
|
||||||
|
|
||||||
fun structSize(target: StructDecl) =
|
fun structSize(target: StructDecl) = NumericLiteralValue(DataType.UBYTE, target.memsize(memsizer), position)
|
||||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum(), position)
|
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||||
|
@ -7,13 +7,15 @@ 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.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.cbm.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||||
|
import java.io.CharConversionException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
@ -70,9 +72,17 @@ internal object C64Target: ICompilationTarget {
|
|||||||
override val name = "c64"
|
override val name = "c64"
|
||||||
override val machine = C64MachineDefinition
|
override val machine = C64MachineDefinition
|
||||||
override fun encodeString(str: String, altEncoding: Boolean) =
|
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
try {
|
||||||
|
if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
|
||||||
|
}
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
try {
|
||||||
|
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
throw AssemblyError("There was a problem decoding to a string: ${x.message}")
|
||||||
|
}
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
override fun memorySize(dt: DataType): Int {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
@ -89,9 +99,17 @@ internal object Cx16Target: ICompilationTarget {
|
|||||||
override val name = "cx16"
|
override val name = "cx16"
|
||||||
override val machine = CX16MachineDefinition
|
override val machine = CX16MachineDefinition
|
||||||
override fun encodeString(str: String, altEncoding: Boolean) =
|
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
try {
|
||||||
|
if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
|
||||||
|
}
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
try {
|
||||||
|
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
throw AssemblyError("There was a problem decoding to a string: ${x.message}")
|
||||||
|
}
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
override fun memorySize(dt: DataType): Int {
|
||||||
return when(dt) {
|
return when(dt) {
|
||||||
@ -114,5 +132,6 @@ internal fun asmGeneratorFor(
|
|||||||
outputDir: Path
|
outputDir: Path
|
||||||
): IAssemblyGenerator
|
): IAssemblyGenerator
|
||||||
{
|
{
|
||||||
|
// at the moment we only have one code generation backend (for 6502 and 65c02)
|
||||||
return AsmGen(program, errors, zp, options, compTarget, outputDir)
|
return AsmGen(program, errors, zp, options, compTarget, outputDir)
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ internal 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(
|
||||||
|
0x22, 0x23, 0x24, 0x25,
|
||||||
0x10, 0x11, 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,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.cbm
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.OutputType
|
import prog8.compiler.OutputType
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.cbm
|
||||||
|
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
|
|
||||||
@ -1051,49 +1051,75 @@ object Petscii {
|
|||||||
|
|
||||||
|
|
||||||
fun encodePetscii(text: String, lowercase: Boolean = false): List<Short> {
|
fun encodePetscii(text: String, lowercase: Boolean = false): List<Short> {
|
||||||
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
fun encodeChar(chr: Char, lowercase: Boolean): Short {
|
||||||
return text.map {
|
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
||||||
val petscii = lookup[it]
|
return screencode?.toShort() ?: when (chr) {
|
||||||
petscii?.toShort() ?: when (it) {
|
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(it.toInt() - 0x8000).toShort()
|
(chr.toInt() - 0x8000).toShort()
|
||||||
}
|
}
|
||||||
else -> {
|
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}Petscii character for '$chr' (${chr.toShort()})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return text.map{
|
||||||
|
try {
|
||||||
|
encodeChar(it, lowercase)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
encodeChar(it, !lowercase)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return petscii.map {
|
||||||
return petscii.map { decodeTable[it.toInt()] }.joinToString("")
|
val code = it.toInt()
|
||||||
|
try {
|
||||||
|
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
||||||
|
} catch(x: CharConversionException) {
|
||||||
|
if(lowercase) decodingPetsciiUppercase[code] else decodingPetsciiLowercase[code]
|
||||||
|
}
|
||||||
|
}.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodeScreencode(text: String, lowercase: Boolean = false): List<Short> {
|
fun encodeScreencode(text: String, lowercase: Boolean = false): List<Short> {
|
||||||
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
fun encodeChar(chr: Char, lowercase: Boolean): Short {
|
||||||
return text.map{
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
val screencode = lookup[it]
|
return screencode?.toShort() ?: when (chr) {
|
||||||
screencode?.toShort() ?: when (it) {
|
|
||||||
'\u0000' -> 0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
in '\u8000'..'\u80ff' -> {
|
in '\u8000'..'\u80ff' -> {
|
||||||
// special case: take the lower 8 bit hex value directly
|
// special case: take the lower 8 bit hex value directly
|
||||||
(it.toInt() - 0x8000).toShort()
|
(chr.toInt() - 0x8000).toShort()
|
||||||
}
|
}
|
||||||
else -> {
|
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 '$chr' (${chr.toShort()})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return text.map{
|
||||||
|
try {
|
||||||
|
encodeChar(it, lowercase)
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
encodeChar(it, !lowercase)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
return screencode.map {
|
||||||
return screencode.map { decodeTable[it.toInt()] }.joinToString("")
|
val code = it.toInt()
|
||||||
|
try {
|
||||||
|
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
||||||
|
} catch (x: CharConversionException) {
|
||||||
|
if (lowercase) decodingScreencodeUppercase[code] else decodingScreencodeLowercase[code]
|
||||||
|
}
|
||||||
|
}.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Short {
|
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Short {
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
@ -9,11 +9,10 @@ import prog8.compiler.*
|
|||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
import prog8.compiler.functions.FSignature
|
import prog8.compiler.functions.FSignature
|
||||||
import prog8.compiler.target.*
|
import prog8.compiler.target.*
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.cbm.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
|
import prog8.optimizer.CallGraph
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -32,6 +31,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
// for expressions and augmented assignments:
|
// for expressions and augmented assignments:
|
||||||
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
|
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
|
||||||
|
private val callGraph = CallGraph(program)
|
||||||
|
|
||||||
private val assemblyLines = mutableListOf<String>()
|
private val assemblyLines = mutableListOf<String>()
|
||||||
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||||
@ -157,7 +157,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
pha""")
|
pha""")
|
||||||
}
|
}
|
||||||
|
|
||||||
out(" jmp main.start ; start program / force start proc to be included")
|
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
|
||||||
|
when(compTarget.name) {
|
||||||
|
Cx16Target.name -> {
|
||||||
|
if(options.floats)
|
||||||
|
out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
|
||||||
|
out(" jsr main.start | lda #4 | sta $01 | rts")
|
||||||
|
}
|
||||||
|
C64Target.name -> out(" jsr main.start | lda #31 | sta $01 | rts")
|
||||||
|
else -> jmp("main.start")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun slaballocations() {
|
private fun slaballocations() {
|
||||||
@ -246,15 +255,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
} else assemblyLines.add(fragment)
|
} else assemblyLines.add(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun encode(str: String, altEncoding: Boolean): List<Short> {
|
|
||||||
try {
|
|
||||||
val bytes = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
return bytes.plus(0)
|
|
||||||
} catch(x: CharConversionException) {
|
|
||||||
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun zeropagevars2asm(statements: List<Statement>) {
|
private fun zeropagevars2asm(statements: List<Statement>) {
|
||||||
out("; vars allocated on zeropage")
|
out("; vars allocated on zeropage")
|
||||||
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
@ -293,7 +293,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.STRUCT -> {} // is flattened
|
DataType.STRUCT -> {} // is flattened
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
val str = decl.value as StringLiteralValue
|
val str = decl.value as StringLiteralValue
|
||||||
outputStringvar(decl, encode(str.value, str.altEncoding))
|
outputStringvar(decl, compTarget.encodeString(str.value, str.altEncoding).plus(0))
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
val data = makeArrayFillDataUnsigned(decl)
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
@ -390,7 +390,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
.filter {it.datatype == DataType.STR }
|
.filter {it.datatype == DataType.STR }
|
||||||
.map {
|
.map {
|
||||||
val str = it.value as StringLiteralValue
|
val str = it.value as StringLiteralValue
|
||||||
it to encode(str.value, str.altEncoding)
|
it to compTarget.encodeString(str.value, str.altEncoding).plus(0)
|
||||||
}
|
}
|
||||||
.groupBy({it.second}, {it.first})
|
.groupBy({it.second}, {it.first})
|
||||||
for((encoded, variables) in encodedstringVars) {
|
for((encoded, variables) in encodedstringVars) {
|
||||||
@ -695,7 +695,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
is Break -> {
|
is Break -> {
|
||||||
if(loopEndLabels.isEmpty())
|
if(loopEndLabels.isEmpty())
|
||||||
throw AssemblyError("break statement out of context ${stmt.position}")
|
throw AssemblyError("break statement out of context ${stmt.position}")
|
||||||
out(" jmp ${loopEndLabels.peek()}")
|
jmp(loopEndLabels.peek())
|
||||||
}
|
}
|
||||||
is WhileLoop -> translate(stmt)
|
is WhileLoop -> translate(stmt)
|
||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
@ -720,7 +720,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val indexName = asmVariableName(expr.indexer.indexVar!!)
|
val indexVar = expr.indexer.indexExpr as? IdentifierReference
|
||||||
|
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
|
||||||
|
|
||||||
|
val indexName = asmVariableName(indexVar)
|
||||||
if(addOneExtra) {
|
if(addOneExtra) {
|
||||||
// add 1 to the result
|
// add 1 to the result
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -818,9 +821,18 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
|
|
||||||
private fun translateSubroutine(sub: Subroutine) {
|
private fun translateSubroutine(sub: Subroutine) {
|
||||||
|
var onlyVariables = false
|
||||||
|
|
||||||
if(sub.inline) {
|
if(sub.inline) {
|
||||||
if(options.optimize)
|
if(options.optimize) {
|
||||||
return // inline subroutines don't exist anymore on their own
|
if(sub.isAsmSubroutine ||callGraph.unused(sub))
|
||||||
|
return
|
||||||
|
|
||||||
|
// from an inlined subroutine only the local variables are generated,
|
||||||
|
// all other code statements are omitted in the subroutine itself
|
||||||
|
// (they've been inlined at the call site, remember?)
|
||||||
|
onlyVariables = true
|
||||||
|
}
|
||||||
else if(sub.amountOfRtsInAsm()==0) {
|
else if(sub.amountOfRtsInAsm()==0) {
|
||||||
// make sure the NOT INLINED subroutine actually does an rts at the end
|
// make sure the NOT INLINED subroutine actually does an rts at the end
|
||||||
sub.statements.add(Return(null, Position.DUMMY))
|
sub.statements.add(Return(null, Position.DUMMY))
|
||||||
@ -837,7 +849,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
// asmsub with most likely just an inline asm in it
|
// asmsub with most likely just an inline asm in it
|
||||||
out("${sub.name}\t.proc")
|
out("${sub.name}\t.proc")
|
||||||
sub.statements.forEach{ translate(it) }
|
sub.statements.forEach { translate(it) }
|
||||||
out(" .pend\n")
|
out(" .pend\n")
|
||||||
} else {
|
} else {
|
||||||
// regular subroutine
|
// regular subroutine
|
||||||
@ -861,8 +873,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
clc""")
|
clc""")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!onlyVariables) {
|
||||||
out("; statements")
|
out("; statements")
|
||||||
sub.statements.forEach{ translate(it) }
|
sub.statements.forEach { translate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
for(removal in removals.toList()) {
|
for(removal in removals.toList()) {
|
||||||
if(removal.second==sub) {
|
if(removal.second==sub) {
|
||||||
@ -917,6 +931,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
|
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
|
||||||
val booleanCondition = stmt.condition as BinaryExpression
|
val booleanCondition = stmt.condition as BinaryExpression
|
||||||
|
|
||||||
|
// DISABLED FOR NOW:
|
||||||
|
// if(!booleanCondition.left.isSimple || !booleanCondition.right.isSimple)
|
||||||
|
// throw AssemblyError("both operands for if comparison expression should have been simplified")
|
||||||
|
|
||||||
if (stmt.elsepart.containsNoCodeNorVars()) {
|
if (stmt.elsepart.containsNoCodeNorVars()) {
|
||||||
// empty else
|
// empty else
|
||||||
val endLabel = makeLabel("if_end")
|
val endLabel = makeLabel("if_end")
|
||||||
@ -930,7 +948,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val endLabel = makeLabel("if_end")
|
val endLabel = makeLabel("if_end")
|
||||||
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
|
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
out(" jmp $endLabel")
|
jmp(endLabel)
|
||||||
out(elseLabel)
|
out(elseLabel)
|
||||||
translate(stmt.elsepart)
|
translate(stmt.elsepart)
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
@ -952,7 +970,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
// endless loop
|
// endless loop
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(stmt.body)
|
translate(stmt.body)
|
||||||
out(" jmp $repeatLabel")
|
jmp(repeatLabel)
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
@ -976,10 +994,12 @@ internal class AsmGen(private val program: Program,
|
|||||||
val name = asmVariableName(stmt.iterations as IdentifierReference)
|
val name = asmVariableName(stmt.iterations as IdentifierReference)
|
||||||
when(vardecl.datatype) {
|
when(vardecl.datatype) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
repeatByteCountVar(name, repeatLabel, endLabel, stmt.body)
|
assignVariableToRegister(name, RegisterOrPair.A)
|
||||||
|
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
repeatWordCountVar(name, repeatLabel, endLabel, stmt.body)
|
assignVariableToRegister(name, RegisterOrPair.AY)
|
||||||
|
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
|
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
|
||||||
}
|
}
|
||||||
@ -1006,10 +1026,11 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
// note: A/Y must have been loaded with the number of iterations!
|
||||||
if(constIterations==0)
|
if(constIterations==0)
|
||||||
return
|
return
|
||||||
// note: A/Y must have been loaded with the number of iterations already!
|
// no need to explicitly test for 0 iterations as this is done in the count down logic below
|
||||||
// TODO can be even more optimized by iterating over pages
|
|
||||||
val counterVar = makeLabel("repeatcounter")
|
val counterVar = makeLabel("repeatcounter")
|
||||||
out("""
|
out("""
|
||||||
sta $counterVar
|
sta $counterVar
|
||||||
@ -1018,80 +1039,48 @@ $repeatLabel lda $counterVar
|
|||||||
bne +
|
bne +
|
||||||
lda $counterVar+1
|
lda $counterVar+1
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ lda $counterVar
|
lda $counterVar
|
||||||
bne +
|
bne +
|
||||||
dec $counterVar+1
|
dec $counterVar+1
|
||||||
+ dec $counterVar
|
+ dec $counterVar
|
||||||
""")
|
""")
|
||||||
translate(body)
|
translate(body)
|
||||||
out(" jmp $repeatLabel")
|
jmp(repeatLabel)
|
||||||
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
|
|
||||||
// allocate count var on ZP
|
|
||||||
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
|
|
||||||
out("""$counterVar = $zpAddr ; auto zp UWORD""")
|
|
||||||
} else {
|
|
||||||
out("""
|
|
||||||
$counterVar .word 0""")
|
|
||||||
}
|
|
||||||
out(endLabel)
|
|
||||||
|
|
||||||
|
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
|
||||||
|
// allocate count var on ZP TODO can be shared with countervars from other subroutines
|
||||||
|
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
|
||||||
|
out("$counterVar = $zpAddr ; auto zp UWORD")
|
||||||
|
} else {
|
||||||
|
out("$counterVar .word 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
out(endLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
// note: A must have been loaded with the number of iterations!
|
||||||
if(constIterations==0)
|
if(constIterations==0)
|
||||||
return
|
return
|
||||||
// note: A must have been loaded with the number of iterations already!
|
|
||||||
val counterVar = makeLabel("repeatcounter")
|
|
||||||
if(constIterations==null)
|
if(constIterations==null)
|
||||||
out(" beq $endLabel")
|
out(" beq $endLabel ; skip loop if zero iters")
|
||||||
|
val counterVar = makeLabel("repeatcounter")
|
||||||
out(" sta $counterVar")
|
out(" sta $counterVar")
|
||||||
out(repeatLabel)
|
out(repeatLabel)
|
||||||
translate(body)
|
translate(body)
|
||||||
out("""
|
out("""
|
||||||
dec $counterVar
|
dec $counterVar
|
||||||
bne $repeatLabel
|
bne $repeatLabel
|
||||||
beq $endLabel
|
beq $endLabel""")
|
||||||
$counterVar .byte 0""")
|
|
||||||
out(endLabel)
|
if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
|
||||||
|
// allocate count var on ZP TODO can be shared with countervars from other subroutines
|
||||||
|
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
|
||||||
|
out("$counterVar = $zpAddr ; auto zp UBYTE")
|
||||||
|
} else {
|
||||||
|
out("$counterVar .byte 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun repeatByteCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
|
||||||
// note: cannot use original counter variable because it should retain its original value
|
|
||||||
val counterVar = makeLabel("repeatcounter")
|
|
||||||
out(" lda $repeatCountVar | beq $endLabel | sta $counterVar")
|
|
||||||
out(repeatLabel)
|
|
||||||
translate(body)
|
|
||||||
out(" dec $counterVar | bne $repeatLabel")
|
|
||||||
// inline countervar:
|
|
||||||
out("""
|
|
||||||
beq $endLabel
|
|
||||||
$counterVar .byte 0""")
|
|
||||||
out(endLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun repeatWordCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
|
||||||
// TODO can be even more optimized by iterating over pages
|
|
||||||
// note: cannot use original counter variable because it should retain its original value
|
|
||||||
val counterVar = makeLabel("repeatcounter")
|
|
||||||
out("""
|
|
||||||
lda $repeatCountVar
|
|
||||||
sta $counterVar
|
|
||||||
ora $repeatCountVar+1
|
|
||||||
beq $endLabel
|
|
||||||
lda $repeatCountVar+1
|
|
||||||
sta $counterVar+1""")
|
|
||||||
out(repeatLabel)
|
|
||||||
translate(body)
|
|
||||||
out("""
|
|
||||||
lda $counterVar
|
|
||||||
bne +
|
|
||||||
dec $counterVar+1
|
|
||||||
+ dec $counterVar
|
|
||||||
lda $counterVar
|
|
||||||
ora $counterVar+1
|
|
||||||
bne $repeatLabel
|
|
||||||
beq $endLabel
|
|
||||||
$counterVar .word 0""")
|
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1104,7 +1093,7 @@ $counterVar .word 0""")
|
|||||||
out(whileLabel)
|
out(whileLabel)
|
||||||
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
|
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
|
||||||
translate(stmt.body)
|
translate(stmt.body)
|
||||||
out(" jmp $whileLabel")
|
jmp(whileLabel)
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
loopEndLabels.pop()
|
loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
@ -1138,7 +1127,7 @@ $counterVar .word 0""")
|
|||||||
if(choice.values==null) {
|
if(choice.values==null) {
|
||||||
// the else choice
|
// the else choice
|
||||||
translate(choice.statements)
|
translate(choice.statements)
|
||||||
out(" jmp $endLabel")
|
jmp(endLabel)
|
||||||
} else {
|
} else {
|
||||||
choiceBlocks.add(choiceLabel to choice.statements)
|
choiceBlocks.add(choiceLabel to choice.statements)
|
||||||
for (cv in choice.values!!) {
|
for (cv in choice.values!!) {
|
||||||
@ -1157,11 +1146,11 @@ $counterVar .word 0""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out(" jmp $endLabel")
|
jmp(endLabel)
|
||||||
for(choiceBlock in choiceBlocks) {
|
for(choiceBlock in choiceBlocks) {
|
||||||
out(choiceBlock.first)
|
out(choiceBlock.first)
|
||||||
translate(choiceBlock.second)
|
translate(choiceBlock.second)
|
||||||
out(" jmp $endLabel")
|
jmp(endLabel)
|
||||||
}
|
}
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
}
|
}
|
||||||
@ -1220,7 +1209,7 @@ $counterVar .word 0""")
|
|||||||
val endLabel = makeLabel("branch_end")
|
val endLabel = makeLabel("branch_end")
|
||||||
out(" $instruction $elseLabel")
|
out(" $instruction $elseLabel")
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
out(" jmp $endLabel")
|
jmp(endLabel)
|
||||||
out(elseLabel)
|
out(elseLabel)
|
||||||
translate(stmt.elsepart)
|
translate(stmt.elsepart)
|
||||||
out(endLabel)
|
out(endLabel)
|
||||||
@ -1277,14 +1266,12 @@ $label nop""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(jmp: Jump) {
|
private fun translate(jump: Jump) = jmp(getJumpTarget(jump))
|
||||||
out(" jmp ${getJumpTarget(jmp)}")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getJumpTarget(jmp: Jump): String {
|
private fun getJumpTarget(jump: Jump): String {
|
||||||
val ident = jmp.identifier
|
val ident = jump.identifier
|
||||||
val label = jmp.generatedLabel
|
val label = jump.generatedLabel
|
||||||
val addr = jmp.address
|
val addr = jump.address
|
||||||
return when {
|
return when {
|
||||||
ident!=null -> {
|
ident!=null -> {
|
||||||
val target = ident.targetStatement(program)
|
val target = ident.targetStatement(program)
|
||||||
@ -1300,7 +1287,7 @@ $label nop""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(ret: Return) {
|
internal fun translate(ret: Return, withRts: Boolean=true) {
|
||||||
ret.value?.let { returnvalue ->
|
ret.value?.let { returnvalue ->
|
||||||
val sub = ret.definingSubroutine()!!
|
val sub = ret.definingSubroutine()!!
|
||||||
val returnType = sub.returntypes.single()
|
val returnType = sub.returntypes.single()
|
||||||
@ -1319,6 +1306,8 @@ $label nop""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(withRts)
|
||||||
out(" rts")
|
out(" rts")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1378,6 +1367,13 @@ $label nop""")
|
|||||||
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
|
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun jmp(asmLabel: String) {
|
||||||
|
if(isTargetCpu(CpuType.CPU65c02))
|
||||||
|
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
|
||||||
|
else
|
||||||
|
out(" jmp $asmLabel")
|
||||||
|
}
|
||||||
|
|
||||||
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
|
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
|
||||||
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
|
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
|
||||||
val leftDt = pointerOffsetExpr.left.inferType(program)
|
val leftDt = pointerOffsetExpr.left.inferType(program)
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
|
|
||||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
@ -13,7 +13,7 @@ import prog8.ast.toHex
|
|||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.functions.FSignature
|
import prog8.compiler.functions.FSignature
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.assignment.*
|
import prog8.compiler.target.cpu6502.codegen.assignment.*
|
||||||
import prog8.compiler.target.subroutineFloatEvalResultVar2
|
import prog8.compiler.target.subroutineFloatEvalResultVar2
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
|
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
|
||||||
@ -51,7 +51,7 @@ 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",
|
||||||
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
|
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
"fastrnd8", "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
@ -64,7 +64,77 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||||
"pokew" -> funcPokeW(fcall)
|
"pokew" -> funcPokeW(fcall)
|
||||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||||
else -> TODO("missing asmgen for builtin func ${func.name}")
|
"cmp" -> funcCmp(fcall)
|
||||||
|
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(fcall: IFunctionCall) {
|
||||||
|
val arg1 = fcall.args[0]
|
||||||
|
val arg2 = fcall.args[1]
|
||||||
|
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if(dt1 in ByteDatatypes) {
|
||||||
|
if(dt2 in ByteDatatypes) {
|
||||||
|
when (arg2) {
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp #${arg2.number}")
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
if(arg2.addressExpression is NumericLiteralValue) {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("args for cmp() should have same dt")
|
||||||
|
} else {
|
||||||
|
// dt1 is a word
|
||||||
|
if(dt2 in WordDatatypes) {
|
||||||
|
when (arg2) {
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy ${asmgen.asmVariableName(arg2)}+1
|
||||||
|
bne +
|
||||||
|
cmp ${asmgen.asmVariableName(arg2)}
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy #>${arg2.number}
|
||||||
|
bne +
|
||||||
|
cmp #<${arg2.number}
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("args for cmp() should have same dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +464,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
||||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
val indexerExpr = if(indexer.indexVar!=null) indexer.indexVar!! else indexer.indexNum!!
|
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
@ -478,11 +547,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
||||||
@ -509,19 +578,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
DataType.ARRAY_UB, DataType.STR -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_sum_f_fac1")
|
asmgen.out(" jsr floats.func_sum_f_fac1")
|
||||||
@ -610,10 +679,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
val firstNum = first.indexer.indexNum
|
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
|
||||||
val firstVar = first.indexer.indexVar
|
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
||||||
val secondNum = second.indexer.indexNum
|
val secondNum = second.indexer.indexExpr as? NumericLiteralValue
|
||||||
val secondVar = second.indexer.indexVar
|
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
||||||
|
|
||||||
if(firstNum!=null && secondNum!=null) {
|
if(firstNum!=null && secondNum!=null) {
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
||||||
@ -924,15 +993,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" jsr floats.abs_f_fac1")
|
asmgen.out(" jsr floats.abs_f_fac1")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1)
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -941,6 +1010,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
when(func.name) {
|
when(func.name) {
|
||||||
|
"fastrnd8" -> {
|
||||||
|
if(resultToStack)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_fastrnd8_stack")
|
||||||
|
else {
|
||||||
|
asmgen.out(" jsr math.fast_randbyte")
|
||||||
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
"rnd" -> {
|
"rnd" -> {
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ArrayElementTypes
|
import prog8.ast.base.ArrayElementTypes
|
||||||
@ -57,9 +57,9 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
$incdec $varname
|
$incdec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -117,16 +117,15 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.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.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
@ -386,12 +385,14 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
-2 -> {
|
-2 -> {
|
||||||
when (range.last) {
|
when (range.last) {
|
||||||
0 -> asmgen.out("""
|
0 -> {
|
||||||
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
dec $varname
|
dec $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
|
}
|
||||||
1 -> asmgen.out("""
|
1 -> asmgen.out("""
|
||||||
dec $varname
|
dec $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
@ -413,8 +414,8 @@ $loopLabel""")
|
|||||||
beq $endLabel
|
beq $endLabel
|
||||||
clc
|
clc
|
||||||
adc #${range.step}
|
adc #${range.step}
|
||||||
sta $varname
|
sta $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
@ -450,9 +451,9 @@ $loopLabel""")
|
|||||||
sta $varname
|
sta $varname
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>${range.step}
|
adc #>${range.step}
|
||||||
sta $varname+1
|
sta $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,9 +503,9 @@ $loopLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -545,9 +546,9 @@ $loopLabel""")
|
|||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,9 +574,9 @@ $loopLabel""")
|
|||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname
|
+ dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
@ -8,10 +8,10 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -111,17 +111,24 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sub.inline && asmgen.options.optimize) {
|
if(!sub.inline || !asmgen.options.optimize) {
|
||||||
if(sub.containsDefinedVariables())
|
|
||||||
throw AssemblyError("can't inline sub with vars")
|
|
||||||
if(!sub.isAsmSubroutine && sub.parameters.isNotEmpty())
|
|
||||||
throw AssemblyError("can't inline a non-asm subroutine with parameters")
|
|
||||||
asmgen.out(" \t; inlined routine follows: ${sub.name} from ${sub.position}")
|
|
||||||
val statements = sub.statements.filter { it !is ParameterVarDecl && it !is Directive }
|
|
||||||
statements.forEach { asmgen.translate(it) }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
|
} else {
|
||||||
|
// inline the subroutine.
|
||||||
|
// we do this by copying the subroutine's statements at the call site.
|
||||||
|
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
|
||||||
|
// (this condition has been enforced by an ast check earlier)
|
||||||
|
asmgen.out(" \t; inlined routine follows: ${sub.name}")
|
||||||
|
val statements = sub.statements.filter { it !is ParameterVarDecl && it !is Directive }
|
||||||
|
statements.forEach {
|
||||||
|
if(it is Return) {
|
||||||
|
asmgen.translate(it, false) // don't use RTS for the inlined return statement
|
||||||
|
} else {
|
||||||
|
if(!sub.inline || it !is VarDecl)
|
||||||
|
asmgen.translate(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asmgen.out(" \t; inlined routine end: ${sub.name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
|
||||||
@ -177,7 +184,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||||
}
|
}
|
||||||
in WordDatatypes ->
|
in WordDatatypes, in IterableDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda P8ESTACK_LO$plusIdxStr,x
|
lda P8ESTACK_LO$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -66,8 +66,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if(targetArrayIdx.indexer.indexNum!=null) {
|
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
|
if(constIndex!=null) {
|
||||||
|
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
import prog8.ast.IMemSizer
|
import prog8.ast.IMemSizer
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
@ -6,7 +6,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
@ -120,13 +120,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
|
||||||
return when {
|
|
||||||
indexer.indexNum!=null -> fromAstSource(indexer.indexNum!!, program, asmgen)
|
|
||||||
indexer.indexVar!=null -> fromAstSource(indexer.indexVar!!, program, asmgen)
|
|
||||||
else -> throw AssemblyError("weird indexer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||||
val cv = value.constValue(program)
|
val cv = value.constValue(program)
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -9,12 +9,13 @@ import prog8.compiler.AssemblyError
|
|||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
import prog8.compiler.functions.builtinFunctionReturnType
|
import prog8.compiler.functions.builtinFunctionReturnType
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
|
||||||
private val exprAsmgen: ExpressionsAsmGen) {
|
private val exprAsmgen: ExpressionsAsmGen
|
||||||
|
) {
|
||||||
|
|
||||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
|
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
|
||||||
|
|
||||||
@ -64,9 +65,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val value = assign.source.array!!
|
val value = assign.source.array!!
|
||||||
val elementDt = assign.source.datatype
|
val elementDt = assign.source.datatype
|
||||||
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
||||||
if (value.indexer.indexNum!=null) {
|
val constIndex = value.indexer.constIndex()
|
||||||
|
if (constIndex!=null) {
|
||||||
// constant array index value
|
// constant array index value
|
||||||
val indexValue = value.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
|
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||||
when (elementDt) {
|
when (elementDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue")
|
asmgen.out(" lda $arrayVarName+$indexValue")
|
||||||
@ -356,7 +358,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
|
|
||||||
// special case optimizations
|
// special case optimizations
|
||||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
if(target.kind== TargetStorageKind.VARIABLE) {
|
||||||
if(value is IdentifierReference && valueDt != DataType.STRUCT)
|
if(value is IdentifierReference && valueDt != DataType.STRUCT)
|
||||||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
||||||
|
|
||||||
@ -429,8 +431,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
// give up, do it via eval stack
|
// give up, do it via eval stack
|
||||||
// TODO optimize typecasts for more special cases?
|
|
||||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||||
|
// TODO optimize typecasts for more special cases?
|
||||||
if(this.asmgen.options.slowCodegenWarnings)
|
if(this.asmgen.options.slowCodegenWarnings)
|
||||||
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
|
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
|
||||||
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
|
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
|
||||||
@ -569,11 +571,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> throw AssemblyError("cannot typecast a string value")
|
||||||
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
|
|
||||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
|
||||||
TODO("assign typecasted string into target var")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -708,11 +706,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> throw AssemblyError("cannot typecast a string value")
|
||||||
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
|
|
||||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
|
||||||
TODO("assign typecasted string into target var")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1073,11 +1067,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1""")
|
sty P8ZP_SCRATCH_W1+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float_from_fac1")
|
asmgen.out(" jsr floats.set_array_float_from_fac1")
|
||||||
@ -1109,11 +1103,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1""")
|
sty P8ZP_SCRATCH_W2+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float")
|
asmgen.out(" jsr floats.set_array_float")
|
||||||
@ -1156,11 +1150,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1""")
|
sty P8ZP_SCRATCH_W2+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float")
|
asmgen.out(" jsr floats.set_array_float")
|
||||||
@ -1242,11 +1236,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
// TODO optimize slow stack evaluation for this case, see assignVariableUByteIntoWord
|
if (wordtarget.constArrayIndexValue!=null) {
|
||||||
if(this.asmgen.options.slowCodegenWarnings)
|
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
||||||
println("warning: slow stack evaluation used for sign-extend byte typecast at ${bytevar.position}")
|
asmgen.out(" lda $sourceName")
|
||||||
asmgen.translateExpression(wordtarget.origAssign.source.expression!!)
|
asmgen.signExtendAYlsb(DataType.BYTE)
|
||||||
assignStackValue(wordtarget)
|
asmgen.out(" sta ${wordtarget.asmVarname}+$scaledIdx | sty ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!)
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.X)
|
||||||
|
asmgen.out(" lda $sourceName")
|
||||||
|
asmgen.signExtendAYlsb(DataType.BYTE)
|
||||||
|
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
when(wordtarget.register!!) {
|
when(wordtarget.register!!) {
|
||||||
@ -1339,8 +1342,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||||
if(target.register !in Cx16VirtualRegisters)
|
// we make an exception in the type check for assigning something to a cx16 virtual register
|
||||||
|
if(target.register !in Cx16VirtualRegisters) {
|
||||||
|
if(target.kind==TargetStorageKind.VARIABLE) {
|
||||||
|
val parts = target.asmVarname.split('.')
|
||||||
|
if (parts.size != 2 || parts[0] != "cx16")
|
||||||
require(target.datatype in ByteDatatypes)
|
require(target.datatype in ByteDatatypes)
|
||||||
|
} else {
|
||||||
|
require(target.datatype in ByteDatatypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -1368,7 +1379,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
CpuRegister.X -> asmgen.out(" txa")
|
CpuRegister.X -> asmgen.out(" txa")
|
||||||
CpuRegister.Y -> asmgen.out(" tya")
|
CpuRegister.Y -> asmgen.out(" tya")
|
||||||
}
|
}
|
||||||
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
|
val indexVar = target.array!!.indexer.indexExpr as IdentifierReference
|
||||||
|
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
@ -1776,8 +1788,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
|
if (constIndex!=null) {
|
||||||
|
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
|
||||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
stz ${target.asmVarname}+$indexValue
|
stz ${target.asmVarname}+$indexValue
|
||||||
@ -1796,7 +1809,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta ${target.asmVarname}+$indexValue+4
|
sta ${target.asmVarname}+$indexValue+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}
|
lda #<${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
@ -1841,8 +1854,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val arrayVarName = target.asmVarname
|
val arrayVarName = target.asmVarname
|
||||||
if (target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
|
if (constIndex!=null) {
|
||||||
|
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $constFloat
|
lda $constFloat
|
||||||
sta $arrayVarName+$indexValue
|
sta $arrayVarName+$indexValue
|
||||||
@ -1856,7 +1870,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta $arrayVarName+$indexValue+4
|
sta $arrayVarName+$indexValue+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${constFloat}
|
lda #<${constFloat}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -7,13 +7,14 @@ import prog8.ast.statements.Subroutine
|
|||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
private val exprAsmGen: ExpressionsAsmGen,
|
private val exprAsmGen: ExpressionsAsmGen,
|
||||||
private val asmgen: AsmGen) {
|
private val asmgen: AsmGen
|
||||||
|
) {
|
||||||
fun translate(assign: AsmAssignment) {
|
fun translate(assign: AsmAssignment) {
|
||||||
require(assign.isAugmentable)
|
require(assign.isAugmentable)
|
||||||
require(assign.source.kind== SourceStorageKind.EXPRESSION)
|
require(assign.source.kind== SourceStorageKind.EXPRESSION)
|
||||||
@ -159,7 +160,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
|
||||||
// TODO more specialized code for types such as memory read etc. -> inplaceModification_byte_memread_to_variable()
|
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryRemoveRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
|
||||||
@ -198,9 +199,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
with(target.array!!.indexer) {
|
with(target.array!!.indexer) {
|
||||||
|
val indexNum = indexExpr as? NumericLiteralValue
|
||||||
|
val indexVar = indexExpr as? IdentifierReference
|
||||||
when {
|
when {
|
||||||
indexNum!=null -> {
|
indexNum!=null -> {
|
||||||
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
when {
|
when {
|
||||||
@ -267,8 +270,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg in-place modification")
|
||||||
TargetStorageKind.STACK -> TODO("stack in-place modification")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack in-place modification")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +322,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -360,7 +362,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" and $otherName")
|
"&", "and" -> asmgen.out(" and $otherName")
|
||||||
"|", "or" -> asmgen.out(" ora $otherName")
|
"|", "or" -> asmgen.out(" ora $otherName")
|
||||||
"^", "xor" -> asmgen.out(" eor $otherName")
|
"^", "xor" -> asmgen.out(" eor $otherName")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -463,7 +464,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else
|
else
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,7 +539,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -597,7 +596,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -669,7 +667,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,8 +688,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_B1
|
sbc P8ZP_SCRATCH_B1
|
||||||
sta $name""")
|
sta $name""")
|
||||||
// TODO: tuned code for more operators
|
|
||||||
}
|
}
|
||||||
|
// TODO: tuned code for more operators
|
||||||
else -> {
|
else -> {
|
||||||
inplaceModification_byte_value_to_variable(name, dt, operator, memread)
|
inplaceModification_byte_value_to_variable(name, dt, operator, memread)
|
||||||
}
|
}
|
||||||
@ -722,8 +719,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bcc +
|
bcc +
|
||||||
dec $name+1
|
dec $name+1
|
||||||
+""")
|
+""")
|
||||||
// TODO: tuned code for more operators
|
|
||||||
}
|
}
|
||||||
|
// TODO: tuned code for more operators
|
||||||
else -> {
|
else -> {
|
||||||
inplaceModification_word_value_to_variable(name, dt, operator, memread)
|
inplaceModification_word_value_to_variable(name, dt, operator, memread)
|
||||||
}
|
}
|
||||||
@ -977,7 +974,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1053,8 +1049,49 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
lda math.multiply_words.result+1
|
lda math.multiply_words.result+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"/" -> TODO("div (u)wordvar/bytevar")
|
"/" -> {
|
||||||
"%" -> TODO("(u)word remainder bytevar")
|
if(dt==DataType.UWORD) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_w_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"%" -> {
|
||||||
|
if(valueDt!=DataType.UBYTE || dt!=DataType.UWORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
sta $name
|
||||||
|
lda P8ZP_SCRATCH_W2+1
|
||||||
|
sta $name+1
|
||||||
|
""") }
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -1099,7 +1136,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1173,7 +1209,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1364,7 +1399,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1405,7 +1439,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1453,7 +1486,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1534,7 +1566,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
@ -1619,7 +1650,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
@ -1729,9 +1759,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place not of ubyte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of ubyte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg not")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
||||||
TargetStorageKind.STACK -> TODO("stack not")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1748,9 +1778,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place not of uword array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of uword array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg not")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
||||||
TargetStorageKind.STACK -> TODO("stack not")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("boolean-not of invalid type")
|
else -> throw AssemblyError("boolean-not of invalid type")
|
||||||
@ -1795,9 +1825,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place invert ubyte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert ubyte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg invert")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
||||||
TargetStorageKind.STACK -> TODO("stack invert")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1812,9 +1842,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place invert uword array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert uword array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg invert")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
||||||
TargetStorageKind.STACK -> TODO("stack invert")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
else -> throw AssemblyError("invert of invalid type")
|
||||||
@ -1833,9 +1863,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}""")
|
sta ${target.asmVarname}""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate byte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate byte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg negate")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
||||||
TargetStorageKind.STACK -> TODO("stack negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -1850,10 +1880,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc ${target.asmVarname}+1
|
sbc ${target.asmVarname}+1
|
||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate word array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate word array")
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg negate")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
||||||
TargetStorageKind.STACK -> TODO("stack negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -1866,8 +1896,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1
|
sta ${target.asmVarname}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate float array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate float array")
|
||||||
TargetStorageKind.STACK -> TODO("stack float negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack float negate")
|
||||||
else -> throw AssemblyError("weird target kind for float")
|
else -> throw AssemblyError("weird target kind for float")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -11,7 +12,6 @@ import prog8.compiler.target.ICompilationTarget
|
|||||||
|
|
||||||
|
|
||||||
internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
// override fun after(decl: VarDecl, parent: Node): Iterable<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...:
|
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
||||||
@ -57,13 +57,14 @@ X = BinExpr X = LeftExpr
|
|||||||
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
|
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
if(isSimpleExpression(binExpr.right) && !assignment.isAugmentable) {
|
if(binExpr.right.isSimple && !assignment.isAugmentable) {
|
||||||
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
|
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, binExpr.left.position)
|
||||||
val targetExpr = assignment.target.toExpression()
|
val targetExpr = assignment.target.toExpression()
|
||||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.definingScope()),
|
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||||
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
|
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as INameScope)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +76,6 @@ X = BinExpr X = LeftExpr
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSimpleExpression(expr: Expression) =
|
|
||||||
expr is IdentifierReference || expr is NumericLiteralValue || expr is AddressOf || expr is DirectMemoryRead || expr is StringLiteralValue || expr is ArrayLiteralValue || expr is RangeExpr
|
|
||||||
|
|
||||||
private fun isSimpleTarget(target: AssignTarget, program: Program) =
|
private fun isSimpleTarget(target: AssignTarget, program: Program) =
|
||||||
if (target.identifier!=null || target.memoryAddress!=null)
|
if (target.identifier!=null || target.memoryAddress!=null)
|
||||||
compTarget.isInRegularRAM(target, program)
|
compTarget.isInRegularRAM(target, program)
|
||||||
|
@ -1,139 +1,72 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
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.DataType
|
||||||
import prog8.ast.base.ParentSentinel
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.AddressOf
|
import prog8.ast.expressions.AddressOf
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
private val alwaysKeepSubroutines = setOf(
|
|
||||||
Pair("main", "start")
|
|
||||||
)
|
|
||||||
|
|
||||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
|
||||||
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
|
||||||
|
|
||||||
|
|
||||||
class CallGraph(private val program: Program, private val asmFileLoader: (filename: String, source: Path)->String) : IAstVisitor {
|
class CallGraph(private val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, Set<Module>>().withDefault { setOf() }
|
||||||
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, Set<Module>>().withDefault { setOf() }
|
||||||
val calls = mutableMapOf<Subroutine, List<Subroutine>>().withDefault { mutableListOf() }
|
val calls = mutableMapOf<Subroutine, Set<Subroutine>>().withDefault { setOf() }
|
||||||
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val calledBy = mutableMapOf<Subroutine, Set<Node>>().withDefault { setOf() }
|
||||||
|
private val allIdentifiersAndTargets = mutableMapOf<Pair<IdentifierReference, Position>, Statement>()
|
||||||
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
private val allAssemblyNodes = mutableListOf<InlineAssembly>()
|
||||||
val usedSymbols = mutableSetOf<Statement>()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
visit(program)
|
visit(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
|
private val usedSubroutines: Set<Subroutine> by lazy {
|
||||||
fun findSubs(scope: INameScope) {
|
calledBy.keys + program.entrypoint()
|
||||||
scope.statements.forEach {
|
|
||||||
if (it is Subroutine)
|
|
||||||
sub(it)
|
|
||||||
if (it is INameScope)
|
|
||||||
findSubs(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
findSubs(scope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
private val usedBlocks: Set<Block> by lazy {
|
||||||
super.visit(program)
|
val blocksFromSubroutines = usedSubroutines.map { it.definingBlock() }
|
||||||
|
val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary }
|
||||||
|
val used = mutableSetOf<Block>()
|
||||||
|
|
||||||
program.modules.forEach {
|
allIdentifiersAndTargets.forEach {
|
||||||
it.importedBy.clear()
|
if(it.key.first.definingBlock() in blocksFromSubroutines) {
|
||||||
it.imports.clear()
|
val target = it.value.definingBlock()
|
||||||
|
used.add(target)
|
||||||
it.importedBy.addAll(importedBy.getValue(it))
|
}
|
||||||
it.imports.addAll(imports.getValue(it))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootmodule = program.modules.first()
|
used + blocksFromLibraries + program.entrypoint().definingBlock()
|
||||||
rootmodule.importedBy.add(rootmodule) // don't discard root module
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
private val usedModules: Set<Module> by lazy {
|
||||||
if (block.definingModule().isLibraryModule) {
|
usedBlocks.map { it.definingModule() }.toSet()
|
||||||
// make sure the block is not removed
|
|
||||||
addNodeAndParentScopes(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(block)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(directive: Directive) {
|
override fun visit(directive: Directive) {
|
||||||
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 }
|
||||||
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
imports[thisModule] = imports.getValue(thisModule) + importedModule
|
||||||
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule) + thisModule
|
||||||
} else if (directive.directive == "%asminclude") {
|
|
||||||
val asm = asmFileLoader(directive.args[0].str!!, thisModule.source)
|
|
||||||
val scope = directive.definingSubroutine()
|
|
||||||
if(scope!=null) {
|
|
||||||
scanAssemblyCode(asm, directive, scope)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visit(directive)
|
super.visit(directive)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(identifier: IdentifierReference) {
|
|
||||||
// track symbol usage
|
|
||||||
val target = identifier.targetStatement(program)
|
|
||||||
if (target != null) {
|
|
||||||
addNodeAndParentScopes(target)
|
|
||||||
}
|
|
||||||
super.visit(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addNodeAndParentScopes(stmt: Statement) {
|
|
||||||
usedSymbols.add(stmt)
|
|
||||||
var node: Node = stmt
|
|
||||||
do {
|
|
||||||
if (node is INameScope && node is Statement) {
|
|
||||||
usedSymbols.add(node)
|
|
||||||
}
|
|
||||||
node = node.parent
|
|
||||||
} while (node !is Module && node !is ParentSentinel)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
|
||||||
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
|
||||||
|| subroutine.definingModule().isLibraryModule) {
|
|
||||||
// make sure the entrypoint is mentioned in the used symbols
|
|
||||||
addNodeAndParentScopes(subroutine)
|
|
||||||
}
|
|
||||||
super.visit(subroutine)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
|
||||||
if (decl.autogeneratedDontRemove || decl.datatype==DataType.STRUCT)
|
|
||||||
addNodeAndParentScopes(decl)
|
|
||||||
else if(decl.parent is Block && decl.definingModule().isLibraryModule)
|
|
||||||
addNodeAndParentScopes(decl)
|
|
||||||
|
|
||||||
super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall) {
|
override fun visit(functionCall: FunctionCall) {
|
||||||
val otherSub = functionCall.target.targetSubroutine(program)
|
val otherSub = functionCall.target.targetSubroutine(program)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCall.definingSubroutine()?.let { thisSub ->
|
functionCall.definingSubroutine()?.let { thisSub ->
|
||||||
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||||
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCall)
|
calledBy[otherSub] = calledBy.getValue(otherSub) + functionCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
@ -143,8 +76,8 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
|
|||||||
val otherSub = functionCallStatement.target.targetSubroutine(program)
|
val otherSub = functionCallStatement.target.targetSubroutine(program)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||||
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||||
calledBy[otherSub] = calledBy.getValue(otherSub).plus(functionCallStatement)
|
calledBy[otherSub] = calledBy.getValue(otherSub) + functionCallStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(functionCallStatement)
|
super.visit(functionCallStatement)
|
||||||
@ -154,8 +87,8 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
|
|||||||
val otherSub = addressOf.identifier.targetSubroutine(program)
|
val otherSub = addressOf.identifier.targetSubroutine(program)
|
||||||
if(otherSub!=null) {
|
if(otherSub!=null) {
|
||||||
addressOf.definingSubroutine()?.let { thisSub ->
|
addressOf.definingSubroutine()?.let { thisSub ->
|
||||||
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||||
calledBy[otherSub] = calledBy.getValue(otherSub).plus(thisSub)
|
calledBy[otherSub] = calledBy.getValue(otherSub) + thisSub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(addressOf)
|
super.visit(addressOf)
|
||||||
@ -165,63 +98,19 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
|
|||||||
val otherSub = jump.identifier?.targetSubroutine(program)
|
val otherSub = jump.identifier?.targetSubroutine(program)
|
||||||
if (otherSub != null) {
|
if (otherSub != null) {
|
||||||
jump.definingSubroutine()?.let { thisSub ->
|
jump.definingSubroutine()?.let { thisSub ->
|
||||||
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||||
calledBy[otherSub] = calledBy.getValue(otherSub).plus(jump)
|
calledBy[otherSub] = calledBy.getValue(otherSub) + jump
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.visit(jump)
|
super.visit(jump)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl) {
|
override fun visit(identifier: IdentifierReference) {
|
||||||
usedSymbols.add(structDecl)
|
allIdentifiersAndTargets[Pair(identifier, identifier.position)] = identifier.targetStatement(program)!!
|
||||||
usedSymbols.addAll(structDecl.statements)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(inlineAssembly: InlineAssembly) {
|
override fun visit(inlineAssembly: InlineAssembly) {
|
||||||
// parse inline asm for subroutine calls (jmp, jsr)
|
allAssemblyNodes.add(inlineAssembly)
|
||||||
val scope = inlineAssembly.definingSubroutine()
|
|
||||||
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
|
||||||
super.visit(inlineAssembly)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scanAssemblyCode(asm: String, context: Statement, scope: Subroutine?) {
|
|
||||||
asm.lines().forEach { line ->
|
|
||||||
val matches = asmJumpRx.matchEntire(line)
|
|
||||||
if (matches != null) {
|
|
||||||
val jumptarget = matches.groups[2]?.value
|
|
||||||
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
|
||||||
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
|
||||||
if (node is Subroutine) {
|
|
||||||
if(scope!=null)
|
|
||||||
calls[scope] = calls.getValue(scope).plus(node)
|
|
||||||
calledBy[node] = calledBy.getValue(node).plus(context)
|
|
||||||
} else if (jumptarget.contains('.')) {
|
|
||||||
// maybe only the first part already refers to a subroutine
|
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
|
||||||
if (node2 is Subroutine) {
|
|
||||||
if(scope!=null)
|
|
||||||
calls[scope] = calls.getValue(scope).plus(node2)
|
|
||||||
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val matches2 = asmRefRx.matchEntire(line)
|
|
||||||
if (matches2 != null) {
|
|
||||||
val target = matches2.groups[2]?.value
|
|
||||||
if (target != null && (target[0].isLetter() || target[0] == '_')) {
|
|
||||||
if (target.contains('.')) {
|
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
|
||||||
if (node is Subroutine) {
|
|
||||||
if(scope!=null)
|
|
||||||
calls[scope] = calls.getValue(scope).plus(node)
|
|
||||||
calledBy[node] = calledBy.getValue(node).plus(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkRecursiveCalls(errors: IErrorReporter) {
|
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||||
@ -274,4 +163,39 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
|
|||||||
recStack[sub] = false
|
recStack[sub] = false
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unused(module: Module) = module !in usedModules
|
||||||
|
|
||||||
|
fun unused(sub: Subroutine): Boolean {
|
||||||
|
return sub !in usedSubroutines && !nameInAssemblyCode(sub.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unused(block: Block): Boolean {
|
||||||
|
return block !in usedBlocks && !nameInAssemblyCode(block.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unused(decl: VarDecl): Boolean {
|
||||||
|
if(decl.type!=VarDeclType.VAR || decl.datatype==DataType.STRUCT || decl.autogeneratedDontRemove || decl.parent is StructDecl)
|
||||||
|
return false
|
||||||
|
|
||||||
|
if(decl.definingBlock() !in usedBlocks)
|
||||||
|
return false
|
||||||
|
|
||||||
|
val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet()
|
||||||
|
return decl !in allReferencedVardecls && !nameInAssemblyCode(decl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
|
||||||
|
|
||||||
|
inline fun unused(label: Label) = false // just always output labels
|
||||||
|
|
||||||
|
fun unused(stmt: ISymbolStatement): Boolean {
|
||||||
|
return when(stmt) {
|
||||||
|
is Subroutine -> unused(stmt)
|
||||||
|
is Block -> unused(stmt)
|
||||||
|
is VarDecl -> unused(stmt)
|
||||||
|
is Label -> false // just always output labels
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import kotlin.math.pow
|
|||||||
class ConstExprEvaluator {
|
class ConstExprEvaluator {
|
||||||
|
|
||||||
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
||||||
|
try {
|
||||||
return when(operator) {
|
return when(operator) {
|
||||||
"+" -> plus(left, right)
|
"+" -> plus(left, right)
|
||||||
"-" -> minus(left, right)
|
"-" -> minus(left, right)
|
||||||
@ -32,6 +33,9 @@ class ConstExprEvaluator {
|
|||||||
">>" -> shiftedright(left, right)
|
">>" -> shiftedright(left, right)
|
||||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||||
}
|
}
|
||||||
|
} catch (ax: FatalAstException) {
|
||||||
|
throw ExpressionError(ax.message, left.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||||
|
@ -14,7 +14,6 @@ import kotlin.math.pow
|
|||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
||||||
|
@ -15,7 +15,6 @@ import prog8.compiler.target.ICompilationTarget
|
|||||||
|
|
||||||
// Fix up the literal value's type to match that of the vardecl
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
try {
|
try {
|
||||||
@ -40,7 +39,6 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
|||||||
// and the array var initializer values and sizes.
|
// and the array var initializer values and sizes.
|
||||||
// This is needed because further constant optimizations depend on those.
|
// This is needed because further constant optimizations depend on those.
|
||||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
|
|
||||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
// replace identifiers that refer to const value, with the value itself
|
// replace identifiers that refer to const value, with the value itself
|
||||||
@ -75,7 +73,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call graph for this?
|
// TODO: use call graph for this?
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) {
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
|
||||||
errors.err("recursive var declaration", decl.position)
|
errors.err("recursive var declaration", decl.position)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -93,19 +91,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
decl
|
decl
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else if(arraysize.constIndex()==null) {
|
|
||||||
// see if we can calculate the size from other fields
|
|
||||||
try {
|
|
||||||
val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program)
|
|
||||||
if (cval != null) {
|
|
||||||
arraysize.indexVar = null
|
|
||||||
arraysize.origExpression = null
|
|
||||||
arraysize.indexNum = cval
|
|
||||||
}
|
|
||||||
} catch (x: UndefinedSymbolError) {
|
|
||||||
errors.err(x.message, x.position)
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ 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(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
@ -491,9 +490,9 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position)
|
return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position)
|
||||||
} else if (cv == 2.0) {
|
} else if (cv in powersOfTwo) {
|
||||||
expr.operator = "&"
|
expr.operator = "&"
|
||||||
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
|
expr.right = NumericLiteralValue.optimalInteger(cv!!.toInt()-1, expr.position)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,31 +4,30 @@ import prog8.ast.IBuiltinFunctions
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.noErrors()) {
|
||||||
valuetypefixer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||||
replacer.visit(this)
|
replacer.visit(this)
|
||||||
if (errors.isEmpty()) {
|
if (errors.noErrors()) {
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
|
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.noErrors()) {
|
||||||
valuetypefixer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
|
|
||||||
val optimizer = ConstantFoldingOptimizer(this, compTarget)
|
val optimizer = ConstantFoldingOptimizer(this, compTarget)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.isEmpty()) {
|
if (errors.noErrors()) {
|
||||||
replacer.visit(this)
|
replacer.visit(this)
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
}
|
}
|
||||||
@ -36,16 +35,15 @@ internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.isEmpty())
|
if(errors.noErrors())
|
||||||
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.optimizeStatements(errors: IErrorReporter,
|
internal fun Program.optimizeStatements(errors: IErrorReporter,
|
||||||
functions: IBuiltinFunctions,
|
functions: IBuiltinFunctions,
|
||||||
compTarget: ICompilationTarget,
|
compTarget: ICompilationTarget): Int {
|
||||||
asmFileLoader: (filename: String, source: Path)->String): Int {
|
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
||||||
val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader)
|
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
|
@ -12,67 +12,25 @@ import prog8.ast.walk.IAstModification
|
|||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
internal const val retvarName = "prog8_retval"
|
||||||
|
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program,
|
internal class StatementOptimizer(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val functions: IBuiltinFunctions,
|
private val functions: IBuiltinFunctions,
|
||||||
private val compTarget: ICompilationTarget,
|
private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
asmFileLoader: (filename: String, source: Path)->String
|
|
||||||
) : AstWalker() {
|
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val subsThatNeedReturnVariable = mutableSetOf<Triple<INameScope, DataType, Position>>()
|
||||||
private val callgraph = CallGraph(program, asmFileLoader)
|
|
||||||
|
|
||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
|
||||||
if("force_output" !in block.options()) {
|
|
||||||
if (block.containsNoCodeNorVars()) {
|
|
||||||
if(block.name != program.internedStringsModuleName)
|
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
for(returnvar in subsThatNeedReturnVariable) {
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
val decl = VarDecl(VarDeclType.VAR, returnvar.second, ZeropageWish.DONTCARE, null, retvarName, null, null, false, true, returnvar.third)
|
||||||
if(subroutine.containsNoCodeNorVars() && !subroutine.inline) {
|
returnvar.first.statements.add(0, decl)
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
|
||||||
val removals = callgraph.calledBy.getValue(subroutine).map {
|
|
||||||
IAstModification.Remove(it, it.definingScope())
|
|
||||||
}.toMutableList()
|
|
||||||
removals += IAstModification.Remove(subroutine, subroutine.definingScope())
|
|
||||||
return removals
|
|
||||||
}
|
}
|
||||||
}
|
subsThatNeedReturnVariable.clear()
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
|
||||||
if(!subroutine.isAsmSubroutine)
|
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
|
||||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
|
||||||
if(decl.type == VarDeclType.VAR)
|
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
|
||||||
|
|
||||||
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +351,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
// 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)
|
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
incs.statements.add(PostIncrDecr(assignment.target.copy(), "++", assignment.position))
|
||||||
}
|
}
|
||||||
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, incs, parent))
|
||||||
}
|
}
|
||||||
@ -407,7 +365,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
// 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)
|
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||||
repeat(rightCv.toInt()) {
|
repeat(rightCv.toInt()) {
|
||||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
decs.statements.add(PostIncrDecr(assignment.target.copy(), "--", assignment.position))
|
||||||
}
|
}
|
||||||
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
return listOf(IAstModification.ReplaceNode(assignment, decs, parent))
|
||||||
}
|
}
|
||||||
@ -435,21 +393,17 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
fun returnViaIntermediary(value: Expression): Iterable<IAstModification>? {
|
fun returnViaIntermediaryVar(value: Expression): Iterable<IAstModification>? {
|
||||||
val returnDt = returnStmt.definingSubroutine()!!.returntypes.single()
|
val subr = returnStmt.definingSubroutine()!!
|
||||||
|
val returnDt = subr.returntypes.single()
|
||||||
if (returnDt in IntegerDatatypes) {
|
if (returnDt in IntegerDatatypes) {
|
||||||
// first assign to intermediary, then return that register
|
// first assign to intermediary variable, then return that
|
||||||
val returnValueIntermediary =
|
subsThatNeedReturnVariable.add(Triple(subr, returnDt, returnStmt.position))
|
||||||
when(returnDt) {
|
val returnValueIntermediary1 = IdentifierReference(listOf(retvarName), returnStmt.position)
|
||||||
DataType.UBYTE -> IdentifierReference(listOf("prog8_lib", "retval_interm_ub"), returnStmt.position)
|
val returnValueIntermediary2 = IdentifierReference(listOf(retvarName), returnStmt.position)
|
||||||
DataType.BYTE -> IdentifierReference(listOf("prog8_lib", "retval_interm_b"), returnStmt.position)
|
val tgt = AssignTarget(returnValueIntermediary1, null, null, returnStmt.position)
|
||||||
DataType.UWORD -> IdentifierReference(listOf("prog8_lib", "retval_interm_uw"), returnStmt.position)
|
|
||||||
DataType.WORD -> IdentifierReference(listOf("prog8_lib", "retval_interm_w"), returnStmt.position)
|
|
||||||
else -> throw FatalAstException("weird return dt")
|
|
||||||
}
|
|
||||||
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
|
|
||||||
val assign = Assignment(tgt, value, returnStmt.position)
|
val assign = Assignment(tgt, value, returnStmt.position)
|
||||||
val returnReplacement = Return(returnValueIntermediary, returnStmt.position)
|
val returnReplacement = Return(returnValueIntermediary2, returnStmt.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as INameScope),
|
IAstModification.InsertBefore(returnStmt, assign, parent as INameScope),
|
||||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||||
@ -460,12 +414,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
when(returnStmt.value) {
|
when(returnStmt.value) {
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
val mod = returnViaIntermediary(returnStmt.value!!)
|
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||||
if(mod!=null)
|
if(mod!=null)
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
val mod = returnViaIntermediary(returnStmt.value!!)
|
val mod = returnViaIntermediaryVar(returnStmt.value!!)
|
||||||
if(mod!=null)
|
if(mod!=null)
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
96
compiler/src/prog8/optimizer/SubroutineInliner.kt
Normal file
96
compiler/src/prog8/optimizer/SubroutineInliner.kt
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.ast.walk.AstWalker
|
||||||
|
import prog8.ast.walk.IAstModification
|
||||||
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
|
internal class SubroutineInliner(private val program: Program, val errors: IErrorReporter, private val compilerOptions: CompilationOptions): AstWalker() {
|
||||||
|
private var callsToInlinedSubroutines = mutableListOf<Pair<IFunctionCall, Node>>()
|
||||||
|
|
||||||
|
fun fixCallsToInlinedSubroutines() {
|
||||||
|
for((call, parent) in callsToInlinedSubroutines) {
|
||||||
|
val sub = call.target.targetSubroutine(program)!!
|
||||||
|
val intermediateReturnValueVar = sub.statements.filterIsInstance<VarDecl>().singleOrNull { it.name.endsWith(retvarName) }
|
||||||
|
if(intermediateReturnValueVar!=null) {
|
||||||
|
val scope = parent.definingScope()
|
||||||
|
if(!scope.statements.filterIsInstance<VarDecl>().any { it.name==intermediateReturnValueVar.name}) {
|
||||||
|
val decl = intermediateReturnValueVar.copy()
|
||||||
|
scope.statements.add(0, decl)
|
||||||
|
decl.linkParents(scope as Node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
return if(compilerOptions.optimize && subroutine.inline && !subroutine.isAsmSubroutine)
|
||||||
|
annotateInlinedSubroutineIdentifiers(subroutine)
|
||||||
|
else
|
||||||
|
noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
return after(functionCallStatement as IFunctionCall, parent, functionCallStatement.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
return after(functionCall as IFunctionCall, parent, functionCall.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun after(functionCall: IFunctionCall, parent: Node, position: Position): Iterable<IAstModification> {
|
||||||
|
val sub = functionCall.target.targetSubroutine(program)
|
||||||
|
if(sub != null && compilerOptions.optimize && sub.inline && !sub.isAsmSubroutine)
|
||||||
|
callsToInlinedSubroutines.add(Pair(functionCall, parent))
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun annotateInlinedSubroutineIdentifiers(sub: Subroutine): List<IAstModification> {
|
||||||
|
// this adds name prefixes to the identifiers used in the subroutine,
|
||||||
|
// so that the statements can be inlined (=copied) in the call site and still reference
|
||||||
|
// the correct symbols as seen from the scope of the subroutine.
|
||||||
|
|
||||||
|
class Annotator: AstWalker() {
|
||||||
|
var numReturns=0
|
||||||
|
|
||||||
|
override fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
|
val stmt = identifier.targetStatement(program)!!
|
||||||
|
if(stmt is BuiltinFunctionStatementPlaceholder)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
val prefixed = stmt.makeScopedName(identifier.nameInSource.last()).split('.')
|
||||||
|
val withPrefix = IdentifierReference(prefixed, identifier.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(identifier, withPrefix, parent))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
|
numReturns++
|
||||||
|
if(parent !== sub || sub.indexOfChild(returnStmt)<sub.statements.size-1)
|
||||||
|
errors.err("return statement must be the very last statement in the inlined subroutine", sub.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
fun theModifications(): List<IAstModification> {
|
||||||
|
return this.modifications.map { it.first }.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val annotator = Annotator()
|
||||||
|
sub.accept(annotator, sub.parent)
|
||||||
|
if(annotator.numReturns>1) {
|
||||||
|
errors.err("inlined subroutine can only have one return statement", sub.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
return annotator.theModifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.expressions.PrefixExpression
|
import prog8.ast.expressions.PrefixExpression
|
||||||
@ -12,43 +14,20 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
internal class UnusedCodeRemover(private val program: Program,
|
internal class UnusedCodeRemover(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val compTarget: ICompilationTarget,
|
private val compTarget: ICompilationTarget): AstWalker() {
|
||||||
private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() {
|
|
||||||
|
|
||||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
private val callgraph = CallGraph(program)
|
||||||
val callgraph = CallGraph(program, asmFileLoader)
|
|
||||||
val removals = mutableListOf<IAstModification>()
|
|
||||||
|
|
||||||
// remove all subroutines that aren't called, or are empty
|
override fun before(module: Module, parent: Node): Iterable<IAstModification> {
|
||||||
val entrypoint = program.entrypoint()
|
return if (!module.isLibraryModule && (module.containsNoCodeNorVars() || callgraph.unused(module)))
|
||||||
program.modules.forEach {
|
listOf(IAstModification.Remove(module, module.definingScope()))
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
else
|
||||||
val forceOutput = "force_output" in sub.definingBlock().options()
|
noModifications
|
||||||
if (sub !== entrypoint && !forceOutput && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
|
||||||
removals.add(IAstModification.Remove(sub, sub.definingScope()))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
|
||||||
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
|
||||||
removals.add(IAstModification.Remove(block, block.definingScope()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove modules that are not imported, or are empty (unless it's a library modules)
|
|
||||||
program.modules.forEach {
|
|
||||||
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
|
||||||
removals.add(IAstModification.Remove(it, it.definingScope()))
|
|
||||||
}
|
|
||||||
|
|
||||||
return removals
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(breakStmt, parent as INameScope)
|
reportUnreachable(breakStmt, parent as INameScope)
|
||||||
@ -84,15 +63,58 @@ internal class UnusedCodeRemover(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||||
|
if("force_output" !in block.options()) {
|
||||||
|
if (block.containsNoCodeNorVars()) {
|
||||||
|
if(block.name != program.internedStringsModuleName)
|
||||||
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||||
|
}
|
||||||
|
if(callgraph.unused(block)) {
|
||||||
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
|
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val removeDoubleAssignments = deduplicateAssignments(block.statements)
|
val removeDoubleAssignments = deduplicateAssignments(block.statements)
|
||||||
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
|
return removeDoubleAssignments.map { IAstModification.Remove(it, block) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
|
if (subroutine !== program.entrypoint() && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
|
||||||
|
if(callgraph.unused(subroutine)) {
|
||||||
|
if(!subroutine.definingModule().isLibraryModule)
|
||||||
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
||||||
|
}
|
||||||
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
|
if(!subroutine.definingModule().isLibraryModule)
|
||||||
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
||||||
|
callgraph.calledBy[subroutine]?.let {
|
||||||
|
for(node in it)
|
||||||
|
removals.add(IAstModification.Remove(node, node.definingScope()))
|
||||||
|
}
|
||||||
|
return removals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
|
||||||
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
|
return removeDoubleAssignments.map { IAstModification.Remove(it, subroutine) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
|
if(!forceOutput && !decl.autogeneratedDontRemove && callgraph.unused(decl)) {
|
||||||
|
if(decl.type == VarDeclType.VAR && !decl.definingBlock().isInLibrary)
|
||||||
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
|
||||||
|
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
|
private fun deduplicateAssignments(statements: List<Statement>): List<Assignment> {
|
||||||
// removes 'duplicate' assignments that assign the same target directly after another
|
// removes 'duplicate' assignments that assign the same target directly after another
|
||||||
val linesToRemove = mutableListOf<Assignment>()
|
val linesToRemove = mutableListOf<Assignment>()
|
||||||
|
@ -19,7 +19,7 @@ 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.cbm.Petscii
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -191,7 +191,7 @@ class TestC64Zeropage {
|
|||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||||
assertEquals(18, zp1.available())
|
assertEquals(18, zp1.available())
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||||
assertEquals(89, zp2.available())
|
assertEquals(85, zp2.available())
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||||
assertEquals(125, zp3.available())
|
assertEquals(125, zp3.available())
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
@ -348,8 +348,8 @@ class TestPetscii {
|
|||||||
listOf<Short>(72, 69, 76, 76, 79, 32, 0xd7, 0xcf, 0xd2, 0xcc, 0xc4, 32, 49, 50, 51, 32, 64, 33, 0x5c)))
|
listOf<Short>(72, 69, 76, 76, 79, 32, 0xd7, 0xcf, 0xd2, 0xcc, 0xc4, 32, 49, 50, 51, 32, 64, 33, 0x5c)))
|
||||||
assertThat(Petscii.encodePetscii("\uf11a", true), equalTo(listOf<Short>(0x12))) // reverse vid
|
assertThat(Petscii.encodePetscii("\uf11a", true), equalTo(listOf<Short>(0x12))) // reverse vid
|
||||||
assertThat(Petscii.encodePetscii("✓", true), equalTo(listOf<Short>(0xfa)))
|
assertThat(Petscii.encodePetscii("✓", true), equalTo(listOf<Short>(0xfa)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodePetscii("π", true) }
|
assertThat("expect lowercase error fallback", Petscii.encodePetscii("π", true), equalTo(listOf<Short>(255)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodePetscii("♥", true) }
|
assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(listOf<Short>(0xd3)))
|
||||||
|
|
||||||
assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A"))
|
assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1), true) }
|
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1), true) }
|
||||||
@ -363,7 +363,7 @@ class TestPetscii {
|
|||||||
assertThat(Petscii.encodePetscii("\uf11a"), equalTo(listOf<Short>(0x12))) // reverse vid
|
assertThat(Petscii.encodePetscii("\uf11a"), equalTo(listOf<Short>(0x12))) // reverse vid
|
||||||
assertThat(Petscii.encodePetscii("♥"), equalTo(listOf<Short>(0xd3)))
|
assertThat(Petscii.encodePetscii("♥"), equalTo(listOf<Short>(0xd3)))
|
||||||
assertThat(Petscii.encodePetscii("π"), equalTo(listOf<Short>(0xff)))
|
assertThat(Petscii.encodePetscii("π"), equalTo(listOf<Short>(0xff)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodePetscii("✓") }
|
assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(listOf<Short>(250)))
|
||||||
|
|
||||||
assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π"))
|
assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1)) }
|
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1)) }
|
||||||
@ -376,8 +376,8 @@ class TestPetscii {
|
|||||||
listOf<Short>(0x08, 0x05, 0x0c, 0x0c, 0x0f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c)
|
listOf<Short>(0x08, 0x05, 0x0c, 0x0c, 0x0f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c)
|
||||||
))
|
))
|
||||||
assertThat(Petscii.encodeScreencode("✓", true), equalTo(listOf<Short>(0x7a)))
|
assertThat(Petscii.encodeScreencode("✓", true), equalTo(listOf<Short>(0x7a)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodeScreencode("♥", true) }
|
assertThat("expect fallback", Petscii.encodeScreencode("♥", true), equalTo(listOf<Short>(83)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodeScreencode("π", true) }
|
assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(listOf<Short>(94)))
|
||||||
|
|
||||||
assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓"))
|
assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1), true) }
|
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1), true) }
|
||||||
@ -390,8 +390,9 @@ class TestPetscii {
|
|||||||
listOf<Short>(0x17, 0x0f, 0x12, 0x0c, 0x04, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c)))
|
listOf<Short>(0x17, 0x0f, 0x12, 0x0c, 0x04, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c)))
|
||||||
assertThat(Petscii.encodeScreencode("♥"), equalTo(listOf<Short>(0x53)))
|
assertThat(Petscii.encodeScreencode("♥"), equalTo(listOf<Short>(0x53)))
|
||||||
assertThat(Petscii.encodeScreencode("π"), equalTo(listOf<Short>(0x5e)))
|
assertThat(Petscii.encodeScreencode("π"), equalTo(listOf<Short>(0x5e)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodeScreencode("✓") }
|
assertThat(Petscii.encodeScreencode("HELLO"), equalTo(listOf<Short>(8, 5, 12, 12, 15)))
|
||||||
assertFailsWith<CharConversionException> { Petscii.encodeScreencode("hello") }
|
assertThat("expecting fallback", Petscii.encodeScreencode("hello"), equalTo(listOf<Short>(8, 5, 12, 12, 15)))
|
||||||
|
assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(listOf<Short>(122)))
|
||||||
|
|
||||||
assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π"))
|
assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1)) }
|
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1)) }
|
||||||
|
13
compiler/test/arithmetic/Makefile
Normal file
13
compiler/test/arithmetic/Makefile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.PHONY: all clean test
|
||||||
|
|
||||||
|
all: test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.prg *.asm *.vice-*
|
||||||
|
|
||||||
|
test: clean
|
||||||
|
p8compile -target cx16 *.p8 >/dev/null
|
||||||
|
for program in *.prg; do \
|
||||||
|
echo "RUNNING:" $$program ; \
|
||||||
|
x16emu -run -prg $$program >/dev/null ; \
|
||||||
|
done
|
18
compiler/test/comparisons/Makefile
Normal file
18
compiler/test/comparisons/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.PHONY: all clean test
|
||||||
|
|
||||||
|
all: test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.prg *.asm *.vice-* test_*.p8
|
||||||
|
|
||||||
|
test: clean generate test_prgs
|
||||||
|
|
||||||
|
generate:
|
||||||
|
python make_tests.py
|
||||||
|
p8compile -noopt -target cx16 *.p8 >/dev/null
|
||||||
|
|
||||||
|
test_prgs:
|
||||||
|
for program in *.prg; do \
|
||||||
|
echo "RUNNING:" $$program ; \
|
||||||
|
x16emu -run -prg $$program >/dev/null ; \
|
||||||
|
done
|
508
compiler/test/comparisons/make_tests.py
Normal file
508
compiler/test/comparisons/make_tests.py
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
# generates various Prog8 files with a huge amount of number comparion tests,
|
||||||
|
# for all supported datatypes and all comparison operators.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
|
||||||
|
def minmaxvalues(dt):
|
||||||
|
if dt == "ubyte":
|
||||||
|
return 0, 255
|
||||||
|
elif dt == "uword":
|
||||||
|
return 0, 65535
|
||||||
|
elif dt == "byte":
|
||||||
|
return -128, 127
|
||||||
|
elif dt == "word":
|
||||||
|
return -32768, 32767
|
||||||
|
elif dt == "float":
|
||||||
|
return -99999999, 99999999
|
||||||
|
else:
|
||||||
|
raise ValueError(dt)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_test(dt, comparison, left, right, expected):
|
||||||
|
global index
|
||||||
|
etxt = f"{left} {comparison} {right}"
|
||||||
|
if eval(etxt) != expected:
|
||||||
|
raise ValueError("invalid comparison: "+etxt+" for "+dt)
|
||||||
|
if expected:
|
||||||
|
stmt_ok = lambda ix: "num_successes++"
|
||||||
|
stmt_else = lambda ix: f"error({ix})"
|
||||||
|
else:
|
||||||
|
stmt_ok = lambda ix: f"error({ix})"
|
||||||
|
stmt_else = lambda ix: "num_successes++"
|
||||||
|
|
||||||
|
def c(number):
|
||||||
|
if dt not in ("byte", "ubyte"):
|
||||||
|
return f"({number} as {dt})"
|
||||||
|
return str(number)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f""" left = {c(left)}
|
||||||
|
right = {c(right)}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
# const <op> const
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if {c(left)} {comparison} {c(right)} {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# const <op> var
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if {c(left)} {comparison} right {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# const <op> expr
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if {c(left)} {comparison} right+zero {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# var <op> const
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left {comparison} {c(right)} {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# var <op> var
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left {comparison} right {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# var <op> expr
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left {comparison} right+zero {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# expr <op> const
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left+zero {comparison} {c(right)} {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# expr <op> var
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left+zero {comparison} right {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
# expr <op> expr
|
||||||
|
index += 1
|
||||||
|
print(
|
||||||
|
f""" ; test #{index}
|
||||||
|
if left+zero {comparison} right+zero {{
|
||||||
|
{stmt_ok(index)}
|
||||||
|
}} else {{
|
||||||
|
{stmt_else(index)}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_header(dt, operator):
|
||||||
|
print(" ; tests: ", dt, operator)
|
||||||
|
print(" comparison = \""+operator+"\"")
|
||||||
|
print(" txt.print(datatype)")
|
||||||
|
print(" txt.spc()")
|
||||||
|
print(" txt.print(comparison)")
|
||||||
|
print(" txt.nl()")
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_equal(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, "==")
|
||||||
|
gen_test(dt, "==", 0, 0, True)
|
||||||
|
gen_test(dt, "==", 0, 1, False)
|
||||||
|
gen_test(dt, "==", 100, 100, True)
|
||||||
|
gen_test(dt, "==", 100, 101, False)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, "==", 200, 200, True)
|
||||||
|
gen_test(dt, "==", 200, 201, False)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, "==", 9999, 9999, True)
|
||||||
|
gen_test(dt, "==", 9999, 10000, False)
|
||||||
|
gen_test(dt, "==", 0x5000, 0x5000, True)
|
||||||
|
gen_test(dt, "==", 0x5000, 0x5001, False)
|
||||||
|
gen_test(dt, "==", 0x5000, 0x4fff, False)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, "==", 30000, 30000, True)
|
||||||
|
gen_test(dt, "==", 30000, 30001, False)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, "==", 0xf000, 0xf000, True)
|
||||||
|
gen_test(dt, "==", 0xf000, 0xf001, False)
|
||||||
|
gen_test(dt, "==", 0xf000, 0xffff, False)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, "==", 0, -1, False)
|
||||||
|
gen_test(dt, "==", -100, -100, True)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, "==", -200, -200, True)
|
||||||
|
gen_test(dt, "==", -200, -201, False)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, "==", -0x5000, -0x5000, True)
|
||||||
|
gen_test(dt, "==", -0x5000, -0x5001, False)
|
||||||
|
gen_test(dt, "==", -0x5000, -0x4fff, False)
|
||||||
|
gen_test(dt, "==", -9999, -9999, True)
|
||||||
|
gen_test(dt, "==", -9999, -10000, False)
|
||||||
|
gen_test(dt, "==", minval, minval, True)
|
||||||
|
gen_test(dt, "==", minval, minval+1, False)
|
||||||
|
gen_test(dt, "==", maxval, maxval, True)
|
||||||
|
gen_test(dt, "==", maxval, maxval-1, False)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_notequal(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, "!=")
|
||||||
|
gen_test(dt, "!=", 0, 0, False)
|
||||||
|
gen_test(dt, "!=", 0, 1, True)
|
||||||
|
gen_test(dt, "!=", 100, 100, False)
|
||||||
|
gen_test(dt, "!=", 100, 101, True)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, "!=", 200, 200, False)
|
||||||
|
gen_test(dt, "!=", 200, 201, True)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, "!=", 9999, 9999, False)
|
||||||
|
gen_test(dt, "!=", 9999, 10000, True)
|
||||||
|
gen_test(dt, "!=", 0x5000, 0x5000, False)
|
||||||
|
gen_test(dt, "!=", 0x5000, 0x5001, True)
|
||||||
|
gen_test(dt, "!=", 0x5000, 0x4fff, True)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, "!=", 30000, 30000, False)
|
||||||
|
gen_test(dt, "!=", 30000, 30001, True)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, "!=", 0xf000, 0xf000, False)
|
||||||
|
gen_test(dt, "!=", 0xf000, 0xf001, True)
|
||||||
|
gen_test(dt, "!=", 0xf000, 0xffff, True)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, "!=", 0, -1, True)
|
||||||
|
gen_test(dt, "!=", -100, -100, False)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, "!=", -200, -200, False)
|
||||||
|
gen_test(dt, "!=", -200, -201, True)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, "!=", -0x5000, -0x5000, False)
|
||||||
|
gen_test(dt, "!=", -0x5000, -0x5001, True)
|
||||||
|
gen_test(dt, "!=", -0x5000, -0x4fff, True)
|
||||||
|
gen_test(dt, "!=", -9999, -9999, False)
|
||||||
|
gen_test(dt, "!=", -9999, -10000, True)
|
||||||
|
gen_test(dt, "!=", minval, minval, False)
|
||||||
|
gen_test(dt, "!=", minval, minval+1, True)
|
||||||
|
gen_test(dt, "!=", maxval, maxval, False)
|
||||||
|
gen_test(dt, "!=", maxval, maxval-1, True)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_less(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, "<")
|
||||||
|
gen_test(dt, "<", 0, 0, False)
|
||||||
|
gen_test(dt, "<", 0, 1, True)
|
||||||
|
gen_test(dt, "<", 100, 100, False)
|
||||||
|
gen_test(dt, "<", 100, 101, True)
|
||||||
|
gen_test(dt, "<", 100, 99, False)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, "<", 200, 200, False)
|
||||||
|
gen_test(dt, "<", 200, 201, True)
|
||||||
|
gen_test(dt, "<", 200, 199, False)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, "<", 9999, 9999, False)
|
||||||
|
gen_test(dt, "<", 9999, 10000, True)
|
||||||
|
gen_test(dt, "<", 9999, 9998, False)
|
||||||
|
gen_test(dt, "<", 0x5000, 0x5000, False)
|
||||||
|
gen_test(dt, "<", 0x5000, 0x5001, True)
|
||||||
|
gen_test(dt, "<", 0x5000, 0x4fff, False)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, "<", 30000, 30000, False)
|
||||||
|
gen_test(dt, "<", 30000, 30001, True)
|
||||||
|
gen_test(dt, "<", 30000, 29999, False)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, "<", 0xf000, 0xf000, False)
|
||||||
|
gen_test(dt, "<", 0xf000, 0xf001, True)
|
||||||
|
gen_test(dt, "<", 0xf000, 0xefff, False)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, "<", 0, -1, False)
|
||||||
|
gen_test(dt, "<", -100, -100, False)
|
||||||
|
gen_test(dt, "<", -100, -101, False)
|
||||||
|
gen_test(dt, "<", -100, -99, True)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, "<", -200, -200, False)
|
||||||
|
gen_test(dt, "<", -200, -201, False)
|
||||||
|
gen_test(dt, "<", -200, -199, True)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, "<", -0x5000, -0x5000, False)
|
||||||
|
gen_test(dt, "<", -0x5000, -0x5001, False)
|
||||||
|
gen_test(dt, "<", -0x5000, -0x4fff, True)
|
||||||
|
gen_test(dt, "<", -9999, -9999, False)
|
||||||
|
gen_test(dt, "<", -9999, -10000, False)
|
||||||
|
gen_test(dt, "<", -9999, -9998, True)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_greater(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, ">")
|
||||||
|
gen_test(dt, ">", 0, 0, False)
|
||||||
|
gen_test(dt, ">", 0, 1, False)
|
||||||
|
gen_test(dt, ">", 100, 100, False)
|
||||||
|
gen_test(dt, ">", 100, 101, False)
|
||||||
|
gen_test(dt, ">", 100, 99, True)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, ">", 200, 200, False)
|
||||||
|
gen_test(dt, ">", 200, 201, False)
|
||||||
|
gen_test(dt, ">", 200, 199, True)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, ">", 9999, 9999, False)
|
||||||
|
gen_test(dt, ">", 9999, 10000, False)
|
||||||
|
gen_test(dt, ">", 9999, 9998, True)
|
||||||
|
gen_test(dt, ">", 0x5000, 0x5000, False)
|
||||||
|
gen_test(dt, ">", 0x5000, 0x5001, False)
|
||||||
|
gen_test(dt, ">", 0x5000, 0x4fff, True)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, ">", 30000, 30000, False)
|
||||||
|
gen_test(dt, ">", 30000, 30001, False)
|
||||||
|
gen_test(dt, ">", 30000, 29999, True)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, ">", 0xf000, 0xf000, False)
|
||||||
|
gen_test(dt, ">", 0xf000, 0xf001, False)
|
||||||
|
gen_test(dt, ">", 0xf000, 0xefff, True)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, ">", 0, -1, True)
|
||||||
|
gen_test(dt, ">", -100, -100, False)
|
||||||
|
gen_test(dt, ">", -100, -101, True)
|
||||||
|
gen_test(dt, ">", -100, -99, False)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, ">", -200, -200, False)
|
||||||
|
gen_test(dt, ">", -200, -201, True)
|
||||||
|
gen_test(dt, ">", -200, -199, False)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, ">", -0x5000, -0x5000, False)
|
||||||
|
gen_test(dt, ">", -0x5000, -0x5001, True)
|
||||||
|
gen_test(dt, ">", -0x5000, -0x4fff, False)
|
||||||
|
gen_test(dt, ">", -9999, -9999, False)
|
||||||
|
gen_test(dt, ">", -9999, -10000, True)
|
||||||
|
gen_test(dt, ">", -9999, -9998, False)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_lessequal(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, "<=")
|
||||||
|
gen_test(dt, "<=", 0, 0, True)
|
||||||
|
gen_test(dt, "<=", 0, 1, True)
|
||||||
|
gen_test(dt, "<=", 100, 100, True)
|
||||||
|
gen_test(dt, "<=", 100, 101, True)
|
||||||
|
gen_test(dt, "<=", 100, 99, False)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, "<=", 200, 200, True)
|
||||||
|
gen_test(dt, "<=", 200, 201, True)
|
||||||
|
gen_test(dt, "<=", 200, 199, False)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, "<=", 9999, 9999, True)
|
||||||
|
gen_test(dt, "<=", 9999, 10000, True)
|
||||||
|
gen_test(dt, "<=", 9999, 9998, False)
|
||||||
|
gen_test(dt, "<=", 0x5000, 0x5000, True)
|
||||||
|
gen_test(dt, "<=", 0x5000, 0x5001, True)
|
||||||
|
gen_test(dt, "<=", 0x5000, 0x4fff, False)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, "<=", 30000, 30000, True)
|
||||||
|
gen_test(dt, "<=", 30000, 30001, True)
|
||||||
|
gen_test(dt, "<=", 30000, 29999, False)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, "<=", 0xf000, 0xf000, True)
|
||||||
|
gen_test(dt, "<=", 0xf000, 0xf001, True)
|
||||||
|
gen_test(dt, "<=", 0xf000, 0xefff, False)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, "<=", 0, -1, False)
|
||||||
|
gen_test(dt, "<=", -100, -100, True)
|
||||||
|
gen_test(dt, "<=", -100, -101, False)
|
||||||
|
gen_test(dt, "<=", -100, -99, True)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, "<=", -200, -200, True)
|
||||||
|
gen_test(dt, "<=", -200, -201, False)
|
||||||
|
gen_test(dt, "<=", -200, -199, True)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, "<=", -0x5000, -0x5000, True)
|
||||||
|
gen_test(dt, "<=", -0x5000, -0x5001, False)
|
||||||
|
gen_test(dt, "<=", -0x5000, -0x4fff, True)
|
||||||
|
gen_test(dt, "<=", -9999, -9999, True)
|
||||||
|
gen_test(dt, "<=", -9999, -10000, False)
|
||||||
|
gen_test(dt, "<=", -9999, -9998, True)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comp_greaterequal(dt):
|
||||||
|
minval, maxval = minmaxvalues(dt)
|
||||||
|
gen_comp_header(dt, ">=")
|
||||||
|
gen_test(dt, ">=", 0, 0, True)
|
||||||
|
gen_test(dt, ">=", 0, 1, False)
|
||||||
|
gen_test(dt, ">=", 100, 100, True)
|
||||||
|
gen_test(dt, ">=", 100, 101, False)
|
||||||
|
gen_test(dt, ">=", 100, 99, True)
|
||||||
|
if maxval >= 200:
|
||||||
|
gen_test(dt, ">=", 200, 200, True)
|
||||||
|
gen_test(dt, ">=", 200, 201, False)
|
||||||
|
gen_test(dt, ">=", 200, 199, True)
|
||||||
|
if maxval >= 9999:
|
||||||
|
gen_test(dt, ">=", 9999, 9999, True)
|
||||||
|
gen_test(dt, ">=", 9999, 10000, False)
|
||||||
|
gen_test(dt, ">=", 9999, 9998, True)
|
||||||
|
gen_test(dt, ">=", 0x5000, 0x5000, True)
|
||||||
|
gen_test(dt, ">=", 0x5000, 0x5001, False)
|
||||||
|
gen_test(dt, ">=", 0x5000, 0x4fff, True)
|
||||||
|
if maxval >= 30000:
|
||||||
|
gen_test(dt, ">=", 30000, 30000, True)
|
||||||
|
gen_test(dt, ">=", 30000, 30001, False)
|
||||||
|
gen_test(dt, ">=", 30000, 29999, True)
|
||||||
|
if maxval >= 40000:
|
||||||
|
gen_test(dt, ">=", 0xf000, 0xf000, True)
|
||||||
|
gen_test(dt, ">=", 0xf000, 0xf001, False)
|
||||||
|
gen_test(dt, ">=", 0xf000, 0xefff, True)
|
||||||
|
if minval < 0:
|
||||||
|
gen_test(dt, ">=", 0, -1, True)
|
||||||
|
gen_test(dt, ">=", -100, -100, True)
|
||||||
|
gen_test(dt, ">=", -100, -101, True)
|
||||||
|
gen_test(dt, ">=", -100, -99, False)
|
||||||
|
if minval < -200:
|
||||||
|
gen_test(dt, ">=", -200, -200, True)
|
||||||
|
gen_test(dt, ">=", -200, -201, True)
|
||||||
|
gen_test(dt, ">=", -200, -199, False)
|
||||||
|
if minval < -9999:
|
||||||
|
gen_test(dt, ">=", -0x5000, -0x5000, True)
|
||||||
|
gen_test(dt, ">=", -0x5000, -0x5001, True)
|
||||||
|
gen_test(dt, ">=", -0x5000, -0x4fff, False)
|
||||||
|
gen_test(dt, ">=", -9999, -9999, True)
|
||||||
|
gen_test(dt, ">=", -9999, -10000, True)
|
||||||
|
gen_test(dt, ">=", -9999, -9998, False)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_test_routine_equalsnotequals(dt):
|
||||||
|
print(f"""
|
||||||
|
sub test_comparisons() {{
|
||||||
|
{dt} left
|
||||||
|
{dt} right
|
||||||
|
{dt} zero = 0
|
||||||
|
""")
|
||||||
|
gen_comp_equal(dt)
|
||||||
|
gen_comp_notequal(dt)
|
||||||
|
print(" }")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_test_routine_lessgreater(dt):
|
||||||
|
print(f"""
|
||||||
|
sub test_comparisons() {{
|
||||||
|
{dt} left
|
||||||
|
{dt} right
|
||||||
|
{dt} zero = 0
|
||||||
|
""")
|
||||||
|
gen_comp_less(dt)
|
||||||
|
gen_comp_greater(dt)
|
||||||
|
print(" }")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_test_routine_lessequalsgreaterequals(dt):
|
||||||
|
print(f"""
|
||||||
|
sub test_comparisons() {{
|
||||||
|
{dt} left
|
||||||
|
{dt} right
|
||||||
|
{dt} zero = 0
|
||||||
|
""")
|
||||||
|
gen_comp_lessequal(dt)
|
||||||
|
gen_comp_greaterequal(dt)
|
||||||
|
print(" }")
|
||||||
|
|
||||||
|
|
||||||
|
def generate(dt, operators):
|
||||||
|
global index
|
||||||
|
index = 0
|
||||||
|
print(f"""
|
||||||
|
%import textio
|
||||||
|
%import floats
|
||||||
|
%import test_stack
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
main {{
|
||||||
|
uword num_errors = 0
|
||||||
|
uword num_successes = 0
|
||||||
|
str datatype = "{dt}"
|
||||||
|
uword comparison
|
||||||
|
|
||||||
|
sub start() {{
|
||||||
|
test_comparisons()
|
||||||
|
print_results()
|
||||||
|
test_stack.test()
|
||||||
|
}}
|
||||||
|
|
||||||
|
sub error(uword index) {{
|
||||||
|
txt.print(" ! error in test ")
|
||||||
|
txt.print_uw(index)
|
||||||
|
txt.chrout(' ')
|
||||||
|
txt.print(datatype)
|
||||||
|
txt.chrout(' ')
|
||||||
|
txt.print(comparison)
|
||||||
|
txt.nl()
|
||||||
|
num_errors++
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
|
||||||
|
if operators=="eq":
|
||||||
|
generate_test_routine_equalsnotequals(dt)
|
||||||
|
elif operators=="lt":
|
||||||
|
generate_test_routine_lessgreater(dt)
|
||||||
|
elif operators=="lteq":
|
||||||
|
generate_test_routine_lessequalsgreaterequals(dt)
|
||||||
|
else:
|
||||||
|
raise ValueError(operators)
|
||||||
|
|
||||||
|
print(f"""
|
||||||
|
sub print_results() {{
|
||||||
|
txt.nl()
|
||||||
|
txt.print("total {index}: ")
|
||||||
|
txt.print_uw(num_successes)
|
||||||
|
txt.print(" good, ")
|
||||||
|
txt.print_uw(num_errors)
|
||||||
|
txt.print(" errors!\\n")
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for dt in ["ubyte", "uword", "byte", "word", "float"]:
|
||||||
|
sys.stdout = open(f"test_{dt}_eq.p8", "wt")
|
||||||
|
generate(dt, "eq")
|
||||||
|
sys.stdout = open(f"test_{dt}_lt.p8", "wt")
|
||||||
|
generate(dt, "lt")
|
||||||
|
sys.stdout = open(f"test_{dt}_lteq.p8", "wt")
|
||||||
|
generate(dt, "lteq")
|
@ -396,14 +396,18 @@ galaxy10 {
|
|||||||
sub init(ubyte galaxy10num) {
|
sub init(ubyte galaxy10num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet10.number = 255
|
planet10.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy10num-1 {
|
repeat galaxy10num-1 {
|
||||||
nextgalaxy10()
|
nextgalaxy10()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy10() {
|
sub nextgalaxy10() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -576,8 +580,10 @@ galaxy10 {
|
|||||||
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
planet10.goatsoup_seed[0] = lsb(seed[1])
|
||||||
planet10.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet10.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet10.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet10.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy2 {
|
|||||||
sub init(ubyte galaxy2num) {
|
sub init(ubyte galaxy2num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet2.number = 255
|
planet2.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy2num-1 {
|
repeat galaxy2num-1 {
|
||||||
nextgalaxy2()
|
nextgalaxy2()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy2() {
|
sub nextgalaxy2() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy2 {
|
|||||||
planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet2.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet2.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet2.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet2.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet2.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy3 {
|
|||||||
sub init(ubyte galaxy3num) {
|
sub init(ubyte galaxy3num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet3.number = 255
|
planet3.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy3num-1 {
|
repeat galaxy3num-1 {
|
||||||
nextgalaxy3()
|
nextgalaxy3()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy3() {
|
sub nextgalaxy3() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy3 {
|
|||||||
planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet3.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet3.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet3.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet3.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet3.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy4 {
|
|||||||
sub init(ubyte galaxy4num) {
|
sub init(ubyte galaxy4num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet4.number = 255
|
planet4.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy4num-1 {
|
repeat galaxy4num-1 {
|
||||||
nextgalaxy4()
|
nextgalaxy4()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy4() {
|
sub nextgalaxy4() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy4 {
|
|||||||
planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet4.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet4.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet4.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet4.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet4.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy5 {
|
|||||||
sub init(ubyte galaxy5num) {
|
sub init(ubyte galaxy5num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet5.number = 255
|
planet5.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy5num-1 {
|
repeat galaxy5num-1 {
|
||||||
nextgalaxy5()
|
nextgalaxy5()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy5() {
|
sub nextgalaxy5() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy5 {
|
|||||||
planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet5.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet5.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet5.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet5.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet5.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy6 {
|
|||||||
sub init(ubyte galaxy6num) {
|
sub init(ubyte galaxy6num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet6.number = 255
|
planet6.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy6num-1 {
|
repeat galaxy6num-1 {
|
||||||
nextgalaxy6()
|
nextgalaxy6()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy6() {
|
sub nextgalaxy6() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy6 {
|
|||||||
planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet6.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet6.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet6.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet6.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet6.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy7 {
|
|||||||
sub init(ubyte galaxy7num) {
|
sub init(ubyte galaxy7num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet7.number = 255
|
planet7.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy7num-1 {
|
repeat galaxy7num-1 {
|
||||||
nextgalaxy7()
|
nextgalaxy7()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy7() {
|
sub nextgalaxy7() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy7 {
|
|||||||
planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet7.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet7.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet7.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet7.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet7.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy8 {
|
|||||||
sub init(ubyte galaxy8num) {
|
sub init(ubyte galaxy8num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet8.number = 255
|
planet8.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy8num-1 {
|
repeat galaxy8num-1 {
|
||||||
nextgalaxy8()
|
nextgalaxy8()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy8() {
|
sub nextgalaxy8() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy8 {
|
|||||||
planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet8.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet8.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet8.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet8.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet8.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy9 {
|
|||||||
sub init(ubyte galaxy9num) {
|
sub init(ubyte galaxy9num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet9.number = 255
|
planet9.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy9num-1 {
|
repeat galaxy9num-1 {
|
||||||
nextgalaxy9()
|
nextgalaxy9()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy9() {
|
sub nextgalaxy9() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -576,8 +580,10 @@ galaxy9 {
|
|||||||
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
planet9.goatsoup_seed[0] = lsb(seed[1])
|
||||||
planet9.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet9.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet9.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet9.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -406,14 +406,18 @@ galaxy {
|
|||||||
sub init(ubyte galaxynum) {
|
sub init(ubyte galaxynum) {
|
||||||
number = 1
|
number = 1
|
||||||
planet.number = 255
|
planet.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxynum-1 {
|
repeat galaxynum-1 {
|
||||||
nextgalaxy()
|
nextgalaxy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy() {
|
sub nextgalaxy() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -587,7 +591,10 @@ galaxy {
|
|||||||
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'antlr'
|
id 'antlr'
|
||||||
id 'java'
|
id 'java'
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.4.30"
|
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCompatibility = 11
|
targetCompatibility = 11
|
||||||
|
@ -121,8 +121,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.indexNum?.accept(this)
|
decl.arraysize!!.indexExpr.accept(this)
|
||||||
decl.arraysize!!.indexVar?.accept(this)
|
|
||||||
}
|
}
|
||||||
if(decl.isArray)
|
if(decl.isArray)
|
||||||
output("]")
|
output("]")
|
||||||
@ -138,8 +137,11 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
output("\n")
|
output("\n")
|
||||||
|
outputi("")
|
||||||
|
if(subroutine.inline)
|
||||||
|
output("inline ")
|
||||||
if(subroutine.isAsmSubroutine) {
|
if(subroutine.isAsmSubroutine) {
|
||||||
outputi("asmsub ${subroutine.name} (")
|
output("asmsub ${subroutine.name} (")
|
||||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||||
val reg =
|
val reg =
|
||||||
when {
|
when {
|
||||||
@ -153,7 +155,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
outputi("sub ${subroutine.name} (")
|
output("sub ${subroutine.name} (")
|
||||||
for(param in subroutine.parameters) {
|
for(param in subroutine.parameters) {
|
||||||
output("${datatypeString(param.type)} ${param.name}")
|
output("${datatypeString(param.type)} ${param.name}")
|
||||||
if(param!==subroutine.parameters.last())
|
if(param!==subroutine.parameters.last())
|
||||||
@ -365,8 +367,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
arrayIndexedExpression.arrayvar.accept(this)
|
arrayIndexedExpression.arrayvar.accept(this)
|
||||||
output("[")
|
output("[")
|
||||||
arrayIndexedExpression.indexer.indexNum?.accept(this)
|
arrayIndexedExpression.indexer.indexExpr.accept(this)
|
||||||
arrayIndexedExpression.indexer.indexVar?.accept(this)
|
|
||||||
output("]")
|
output("]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,6 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun containsDefinedVariables() = statements.any { it is VarDecl && (it !is ParameterVarDecl) }
|
|
||||||
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 containsNoCodeNorVars() = !containsCodeOrVars()
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
@ -274,24 +273,22 @@ class Program(val name: String,
|
|||||||
init {
|
init {
|
||||||
// insert a container module for all interned strings later
|
// insert a container module for all interned strings later
|
||||||
if(modules.firstOrNull()?.name != internedStringsModuleName) {
|
if(modules.firstOrNull()?.name != internedStringsModuleName) {
|
||||||
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of(""))
|
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, true, Path.of(""))
|
||||||
modules.add(0, internedStringsModule)
|
modules.add(0, internedStringsModule)
|
||||||
val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY)
|
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
||||||
internedStringsModule.statements.add(block)
|
internedStringsModule.statements.add(block)
|
||||||
internedStringsModule.linkParents(this)
|
internedStringsModule.linkParents(this)
|
||||||
internedStringsModule.program = this
|
internedStringsModule.program = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun entrypoint(): Subroutine? {
|
fun entrypoint(): Subroutine {
|
||||||
val mainBlocks = allBlocks().filter { it.name=="main" }
|
val mainBlocks = allBlocks().filter { it.name=="main" }
|
||||||
if(mainBlocks.size > 1)
|
if(mainBlocks.size > 1)
|
||||||
throw FatalAstException("more than one 'main' block")
|
throw FatalAstException("more than one 'main' block")
|
||||||
return if(mainBlocks.isEmpty()) {
|
if(mainBlocks.isEmpty())
|
||||||
null
|
throw FatalAstException("no 'main' block")
|
||||||
} else {
|
return mainBlocks[0].subScope("start") as Subroutine
|
||||||
mainBlocks[0].subScope("start") as Subroutine?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun internString(string: StringLiteralValue): List<String> {
|
fun internString(string: StringLiteralValue): List<String> {
|
||||||
@ -339,8 +336,6 @@ class Module(override val name: String,
|
|||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
lateinit var program: Program
|
lateinit var program: Program
|
||||||
val importedBy = mutableListOf<Module>()
|
|
||||||
val imports = mutableSetOf<Module>()
|
|
||||||
|
|
||||||
val loadAddress: Int by lazy {
|
val loadAddress: Int by lazy {
|
||||||
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
|
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
|
||||||
|
@ -60,7 +60,13 @@ enum class DataType {
|
|||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y
|
Y;
|
||||||
|
|
||||||
|
fun asRegisterOrPair(): RegisterOrPair = when(this) {
|
||||||
|
A -> RegisterOrPair.A
|
||||||
|
X -> RegisterOrPair.X
|
||||||
|
Y -> RegisterOrPair.Y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RegisterOrPair {
|
enum class RegisterOrPair {
|
||||||
|
@ -6,7 +6,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import java.util.*
|
import java.util.Objects
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ sealed class Expression: Node {
|
|||||||
abstract fun accept(visitor: AstWalker, parent: Node)
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifier(vararg scopedName: String): Boolean
|
abstract fun referencesIdentifier(vararg scopedName: String): Boolean
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
abstract val isSimple: Boolean
|
||||||
|
|
||||||
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
|
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
|
||||||
|
|
||||||
@ -105,6 +106,8 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = false
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Prefix($operator $expression)"
|
return "Prefix($operator $expression)"
|
||||||
}
|
}
|
||||||
@ -133,6 +136,8 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
return "[$left $operator $right]"
|
return "[$left $operator $right]"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = false
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
@ -242,6 +247,8 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
|||||||
indexer.linkParents(this)
|
indexer.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = indexer.indexExpr is NumericLiteralValue || indexer.indexExpr is IdentifierReference
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
when {
|
when {
|
||||||
node===arrayvar -> arrayvar = replacement as IdentifierReference
|
node===arrayvar -> arrayvar = replacement as IdentifierReference
|
||||||
@ -271,6 +278,8 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ArrayIndexed(ident=$arrayvar, arraysize=$indexer; pos=$position)"
|
return "ArrayIndexed(ident=$arrayvar, arraysize=$indexer; pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copy() = ArrayIndexedExpression(arrayvar.copy(), indexer.copy(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
|
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
|
||||||
@ -281,6 +290,8 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
expression.linkParents(this)
|
expression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = false
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression && node===expression)
|
require(replacement is Expression && node===expression)
|
||||||
expression = replacement
|
expression = replacement
|
||||||
@ -314,6 +325,8 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
identifier.parent=this
|
identifier.parent=this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is IdentifierReference && node===identifier)
|
require(replacement is IdentifierReference && node===identifier)
|
||||||
identifier = replacement
|
identifier = replacement
|
||||||
@ -335,6 +348,8 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
this.addressExpression.linkParents(this)
|
this.addressExpression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression && node===addressExpression)
|
require(replacement is Expression && node===addressExpression)
|
||||||
addressExpression = replacement
|
addressExpression = replacement
|
||||||
@ -351,6 +366,8 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DirectMemoryRead($addressExpression)"
|
return "DirectMemoryRead($addressExpression)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copy() = DirectMemoryRead(addressExpression, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||||
@ -358,6 +375,8 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
override val position: Position) : Expression() {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromBoolean(bool: Boolean, position: Position) =
|
fun fromBoolean(bool: Boolean, position: Position) =
|
||||||
NumericLiteralValue(DataType.UBYTE, if (bool) 1 else 0, position)
|
NumericLiteralValue(DataType.UBYTE, if (bool) 1 else 0, position)
|
||||||
@ -489,6 +508,8 @@ class StringLiteralValue(val value: String,
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
throw FatalAstException("can't replace here")
|
throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
@ -519,6 +540,8 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
value.forEach {it.linkParents(this)}
|
value.forEach {it.linkParents(this)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
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.indexOfFirst { it===node }
|
val idx = value.indexOfFirst { it===node }
|
||||||
@ -542,6 +565,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
return type==other.type && value.contentEquals(other.value)
|
return type==other.type && value.contentEquals(other.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun memsize(memsizer: IMemSizer): Int {
|
||||||
|
if(type.isKnown) {
|
||||||
|
val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT))
|
||||||
|
return memsizer.memorySize(eltType) * value.size
|
||||||
|
}
|
||||||
|
else throw IllegalArgumentException("array datatype is not yet known")
|
||||||
|
}
|
||||||
|
|
||||||
fun guessDatatype(program: Program): InferredTypes.InferredType {
|
fun guessDatatype(program: Program): InferredTypes.InferredType {
|
||||||
// Educated guess of the desired array literal's datatype.
|
// Educated guess of the desired array literal's datatype.
|
||||||
// If it's inside a for loop, assume the data type of the loop variable is what we want.
|
// If it's inside a for loop, assume the data type of the loop variable is what we want.
|
||||||
@ -617,6 +648,8 @@ class RangeExpr(var from: Expression,
|
|||||||
step.linkParents(this)
|
step.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression)
|
require(replacement is Expression)
|
||||||
when {
|
when {
|
||||||
@ -703,20 +736,21 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(),
|
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable {
|
||||||
IAssignable {
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
|
override val isSimple = true
|
||||||
|
|
||||||
fun targetStatement(program: Program) =
|
fun targetStatement(program: Program) =
|
||||||
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
||||||
BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
|
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
|
||||||
else
|
else
|
||||||
program.namespace.lookup(nameInSource, this)
|
program.namespace.lookup(nameInSource, this)
|
||||||
|
|
||||||
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
||||||
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
||||||
|
|
||||||
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
|
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource // NOTE: only compare by the name, not the position!
|
||||||
override fun hashCode() = nameInSource.hashCode()
|
override fun hashCode() = nameInSource.hashCode()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -786,6 +820,8 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
args.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSimple = target.nameInSource.size==1 && (target.nameInSource[0] in setOf("msb", "lsb", "peek", "peekw"))
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
if(node===target)
|
if(node===target)
|
||||||
target=replacement as IdentifierReference
|
target=replacement as IdentifierReference
|
||||||
|
@ -7,6 +7,10 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
|
||||||
|
|
||||||
|
interface ISymbolStatement {
|
||||||
|
val name: String
|
||||||
|
}
|
||||||
|
|
||||||
sealed class Statement : Node {
|
sealed class Statement : Node {
|
||||||
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 +35,7 @@ sealed class Statement : Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position, override var parent: Node) : Statement() {
|
||||||
override var parent: Node = ParentSentinel
|
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
override fun accept(visitor: IAstVisitor) = throw FatalAstException("should not iterate over this node")
|
override fun accept(visitor: IAstVisitor) = throw FatalAstException("should not iterate over this node")
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = throw FatalAstException("should not iterate over this node")
|
override fun accept(visitor: AstWalker, parent: Node) = throw FatalAstException("should not iterate over this node")
|
||||||
@ -48,7 +51,7 @@ class Block(override val name: String,
|
|||||||
val address: Int?,
|
val address: Int?,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope, ISymbolStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -95,7 +98,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(override val name: String, override val position: Position) : Statement(), ISymbolStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -153,17 +156,16 @@ enum class ZeropageWish {
|
|||||||
NOT_IN_ZEROPAGE
|
NOT_IN_ZEROPAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open class VarDecl(val type: VarDeclType,
|
open class VarDecl(val type: VarDeclType,
|
||||||
private val declaredDatatype: DataType,
|
private val declaredDatatype: DataType,
|
||||||
val zeropage: ZeropageWish,
|
val zeropage: ZeropageWish,
|
||||||
var arraysize: ArrayIndex?,
|
var arraysize: ArrayIndex?,
|
||||||
val name: String,
|
override val name: String,
|
||||||
private val structName: String?,
|
private val structName: String?,
|
||||||
var value: Expression?,
|
var value: Expression?,
|
||||||
val isArray: Boolean,
|
val isArray: Boolean,
|
||||||
val autogeneratedDontRemove: Boolean,
|
val autogeneratedDontRemove: Boolean,
|
||||||
override val position: Position) : Statement() {
|
override val position: Position) : Statement(), ISymbolStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
|
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
|
||||||
private set
|
private set
|
||||||
@ -221,14 +223,18 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
value?.linkParents(this)
|
value?.linkParents(this)
|
||||||
if(structName!=null) {
|
if(structName!=null) {
|
||||||
val structStmt = definingScope().lookup(listOf(structName), this)
|
val structStmt = definingScope().lookup(listOf(structName), this)
|
||||||
if(structStmt!=null)
|
if(structStmt!=null) {
|
||||||
struct = definingScope().lookup(listOf(structName), this) as StructDecl
|
val node = definingScope().lookup(listOf(structName), this)
|
||||||
|
if(node is StructDecl)
|
||||||
|
struct = node
|
||||||
|
else
|
||||||
|
datatypeErrors.add(SyntaxError("invalid datatype declaration", position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
// 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==null || node===value))
|
||||||
require(replacement is Expression)
|
|
||||||
value = replacement
|
value = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -250,7 +256,14 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
val result = struct!!.statements.mapIndexed { index, statement ->
|
val result = struct!!.statements.mapIndexed { index, statement ->
|
||||||
val member = statement as VarDecl
|
val member = statement as VarDecl
|
||||||
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[index] else null
|
val initvalueOrig = if(value!=null) (value as ArrayLiteralValue).value[index] else null
|
||||||
|
val initvalue = when(initvalueOrig) {
|
||||||
|
is AddressOf -> initvalueOrig.copy()
|
||||||
|
is ArrayIndexedExpression -> initvalueOrig.copy()
|
||||||
|
is DirectMemoryRead -> initvalueOrig.copy()
|
||||||
|
is IdentifierReference -> initvalueOrig.copy()
|
||||||
|
else -> initvalueOrig
|
||||||
|
}
|
||||||
VarDecl(
|
VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
member.datatype,
|
member.datatype,
|
||||||
@ -267,53 +280,32 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
structHasBeenFlattened = true
|
structHasBeenFlattened = true
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copy(): VarDecl {
|
||||||
|
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, value, isArray, autogeneratedDontRemove, position)
|
||||||
|
c.allowInitializeWithZero = this.allowInitializeWithZero
|
||||||
|
c.structHasBeenFlattened = this.structHasBeenFlattened
|
||||||
|
return c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.DONTCARE, null, name, null, null, false, true, position)
|
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||||
|
|
||||||
|
class ArrayIndex(var indexExpr: Expression,
|
||||||
class ArrayIndex(var origExpression: Expression?, // will be replaced later by either the number or the identifier
|
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
// for code simplicity, either indexed via a constant number or via a variable (no arbitrary expressions)
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
var indexNum: NumericLiteralValue? = origExpression as? NumericLiteralValue
|
|
||||||
var indexVar: IdentifierReference? = origExpression as? IdentifierReference
|
|
||||||
|
|
||||||
init {
|
|
||||||
if(indexNum!=null || indexVar!=null)
|
|
||||||
origExpression = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
origExpression?.linkParents(this)
|
indexExpr.linkParents(this)
|
||||||
indexNum?.linkParents(this)
|
|
||||||
indexVar?.linkParents(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression)
|
require(replacement is Expression)
|
||||||
when {
|
if (node===indexExpr) indexExpr = replacement
|
||||||
node===origExpression -> origExpression = replacement
|
else throw FatalAstException("invalid replace")
|
||||||
node===indexVar -> {
|
|
||||||
when (replacement) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
indexVar = null
|
|
||||||
indexNum = replacement
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
indexVar = replacement
|
|
||||||
indexNum = null
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw FatalAstException("invalid replace")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("invalid replace")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -323,29 +315,17 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) {
|
fun accept(visitor: IAstVisitor) = indexExpr.accept(visitor)
|
||||||
origExpression?.accept(visitor)
|
fun accept(visitor: AstWalker, parent: Node) = indexExpr.accept(visitor, this)
|
||||||
indexNum?.accept(visitor)
|
|
||||||
indexVar?.accept(visitor)
|
|
||||||
}
|
|
||||||
fun accept(visitor: AstWalker, parent: Node) {
|
|
||||||
origExpression?.accept(visitor, this)
|
|
||||||
indexNum?.accept(visitor, this)
|
|
||||||
indexVar?.accept(visitor, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("ArrayIndex($indexNum, $indexVar, pos=$position)")
|
return("ArrayIndex($indexExpr, pos=$position)")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun constIndex() = indexNum?.number?.toInt()
|
fun constIndex() = (indexExpr as? NumericLiteralValue)?.number?.toInt()
|
||||||
|
|
||||||
infix fun isSameAs(other: ArrayIndex): Boolean {
|
infix fun isSameAs(other: ArrayIndex): Boolean = indexExpr isSameAs other.indexExpr
|
||||||
return if(indexNum!=null || indexVar!=null)
|
fun copy() = ArrayIndex(indexExpr, position)
|
||||||
indexNum==other.indexNum && indexVar == other.indexVar
|
|
||||||
else
|
|
||||||
other.origExpression!=null && origExpression!! isSameAs other.origExpression!!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
||||||
@ -463,9 +443,10 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun toExpression(): Expression {
|
fun toExpression(): Expression {
|
||||||
|
// return a copy of the assignment target but as a source expression.
|
||||||
return when {
|
return when {
|
||||||
identifier != null -> identifier!!
|
identifier != null -> identifier!!.copy()
|
||||||
arrayindexed != null -> arrayindexed!!
|
arrayindexed != null -> arrayindexed!!.copy()
|
||||||
memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
|
memoryAddress != null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
|
||||||
else -> throw FatalAstException("invalid assignmenttarget $this")
|
else -> throw FatalAstException("invalid assignmenttarget $this")
|
||||||
}
|
}
|
||||||
@ -510,8 +491,9 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
fun copy() = AssignTarget(identifier?.copy(), arrayindexed?.copy(), memoryAddress?.copy(), position)
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -639,20 +621,16 @@ class NopStatement(override val position: Position): Statement() {
|
|||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AsmGenInfo {
|
class AsmGenInfo {
|
||||||
// This class contains various attributes that influence the assembly code generator.
|
// This class contains various attributes that influence the assembly code generator.
|
||||||
// Conceptually it should be part of any INameScope.
|
// Conceptually it should be part of any INameScope.
|
||||||
// But because the resulting code only creates "real" scopes on a subroutine level,
|
// But because the resulting code only creates "real" scopes on a subroutine level,
|
||||||
// it's more consistent to only define these attributes on a Subroutine node.
|
// it's more consistent to only define these attributes on a Subroutine node.
|
||||||
var usedAutoArrayIndexerForStatements = mutableListOf<ArrayIndexerInfo>()
|
|
||||||
var usedRegsaveA = false
|
var usedRegsaveA = false
|
||||||
var usedRegsaveX = false
|
var usedRegsaveX = false
|
||||||
var usedRegsaveY = false
|
var usedRegsaveY = false
|
||||||
var usedFloatEvalResultVar1 = false
|
var usedFloatEvalResultVar1 = false
|
||||||
var usedFloatEvalResultVar2 = false
|
var usedFloatEvalResultVar2 = false
|
||||||
|
|
||||||
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the subroutine class covers both the normal user-defined subroutines,
|
// the subroutine class covers both the normal user-defined subroutines,
|
||||||
@ -668,7 +646,7 @@ class Subroutine(override val name: String,
|
|||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope, ISymbolStatement {
|
||||||
|
|
||||||
constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
|
constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
|
||||||
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
|
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
|
||||||
@ -728,10 +706,9 @@ class Subroutine(override val name: String,
|
|||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it is InlineAssembly }
|
.filter { it is InlineAssembly }
|
||||||
.map { (it as InlineAssembly).assembly }
|
.map { (it as InlineAssembly).assembly }
|
||||||
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open class SubroutineParameter(val name: String,
|
open class SubroutineParameter(val name: String,
|
||||||
val type: DataType,
|
val type: DataType,
|
||||||
override val position: Position) : Node {
|
override val position: Position) : Node {
|
||||||
@ -978,7 +955,6 @@ class WhenChoice(var values: MutableList<Expression>?, // if null, th
|
|||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StructDecl(override val name: String,
|
class StructDecl(override val name: String,
|
||||||
override var statements: MutableList<Statement>, // actually, only vardecls here
|
override var statements: MutableList<Statement>, // actually, only vardecls here
|
||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
@ -1000,6 +976,9 @@ class StructDecl(override val name: String,
|
|||||||
val numberOfElements: Int
|
val numberOfElements: Int
|
||||||
get() = this.statements.size
|
get() = this.statements.size
|
||||||
|
|
||||||
|
fun memsize(memsizer: IMemSizer) =
|
||||||
|
statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum()
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
@ -1026,4 +1005,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: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
fun copy() = DirectMemoryWrite(addressExpression, position)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ interface IAstModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) :
|
class ReplaceNode(val node: Node, private val replacement: Node, private val parent: Node) :
|
||||||
IAstModification {
|
IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
parent.replaceChildNode(node, replacement)
|
parent.replaceChildNode(node, replacement)
|
||||||
@ -76,94 +76,117 @@ interface IAstModification {
|
|||||||
|
|
||||||
|
|
||||||
abstract class AstWalker {
|
abstract class AstWalker {
|
||||||
open fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList()
|
protected val noModifications = emptyList<IAstModification>()
|
||||||
open fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(block: Block, 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(decl: VarDecl, 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: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(forLoop: ForLoop, 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(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(label: Label, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(module: Module, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(numLiteral: NumericLiteralValue, 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(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(untilLoop: UntilLoop, 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(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(structDecl: StructDecl, 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(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
|
|
||||||
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(block: Block, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(label: Label, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(label: Label, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(module: Module, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(module: Module, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
|
|
||||||
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(block: Block, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(label: Label, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(module: Module, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
|
|
||||||
|
protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
||||||
|
|
||||||
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
|
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
|
||||||
for (it in mods) modifications += Triple(it, node, parent)
|
for (it in mods) {
|
||||||
|
// if(it is IAstModification.ReplaceNode) {
|
||||||
|
// val replaceKey = Pair(it.node, it.node.position)
|
||||||
|
// if(replaceKey in modificationsReplacedNodes)
|
||||||
|
// throw FatalAstException("there already is a node replacement for $replaceKey - optimizer can't deal with multiple replacements for same node yet. Split the ast modification?")
|
||||||
|
// else
|
||||||
|
// modificationsReplacedNodes.add(replaceKey)
|
||||||
|
// }
|
||||||
|
modifications += Triple(it, node, parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyModifications(): Int {
|
fun applyModifications(): Int {
|
||||||
|
// check if there are double removes, keep only the last one
|
||||||
|
val removals = modifications.filter { it.first is IAstModification.Remove }
|
||||||
|
if(removals.size>0) {
|
||||||
|
val doubles = removals.groupBy { (it.first as IAstModification.Remove).node }.filter { it.value.size>1 }
|
||||||
|
doubles.forEach {
|
||||||
|
for(doubleRemove in it.value.dropLast(1)) {
|
||||||
|
if(!modifications.removeIf { mod-> mod.first === doubleRemove.first })
|
||||||
|
throw FatalAstException("ast remove problem")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
modifications.forEach {
|
modifications.forEach {
|
||||||
it.first.perform()
|
it.first.perform()
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@ class ModuleImporter {
|
|||||||
if(!Files.isReadable(filePath))
|
if(!Files.isReadable(filePath))
|
||||||
throw ParsingFailedError("No such file: $filePath")
|
throw ParsingFailedError("No such file: $filePath")
|
||||||
|
|
||||||
val input = CharStreams.fromPath(filePath)
|
val content = filePath.toFile().readText().replace("\r\n", "\n")
|
||||||
return importModule(program, input, filePath, false, encoder, compilationTargetName)
|
return importModule(program, CharStreams.fromString(content), filePath, false, encoder, compilationTargetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importLibraryModule(program: Program, name: String,
|
fun importLibraryModule(program: Program, name: String,
|
||||||
@ -62,6 +62,8 @@ class ModuleImporter {
|
|||||||
is prog8Parser -> System.err.println("${recognizer.inputStream.sourceName}:$line:$charPositionInLine: $msg")
|
is prog8Parser -> System.err.println("${recognizer.inputStream.sourceName}:$line:$charPositionInLine: $msg")
|
||||||
else -> System.err.println("$line:$charPositionInLine $msg")
|
else -> System.err.println("$line:$charPositionInLine $msg")
|
||||||
}
|
}
|
||||||
|
if(numberOfErrors>=5)
|
||||||
|
throw ParsingFailedError("There are too many parse errors. Stopping.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,8 @@ class ModuleImporter {
|
|||||||
val (resource, resourcePath) = rsc
|
val (resource, resourcePath) = rsc
|
||||||
resource.use {
|
resource.use {
|
||||||
println("importing '$moduleName' (library)")
|
println("importing '$moduleName' (library)")
|
||||||
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$resourcePath"),
|
val content = it.reader().readText().replace("\r\n", "\n")
|
||||||
|
importModule(program, CharStreams.fromString(content), Paths.get("@embedded@/$resourcePath"),
|
||||||
true, encoder, compilationTargetName)
|
true, encoder, compilationTargetName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'application'
|
id 'application'
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.4.30"
|
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.1'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.2'
|
||||||
implementation "com.github.hypfvieh:dbus-java:3.2.4"
|
implementation "com.github.hypfvieh:dbus-java:3.2.4"
|
||||||
implementation "org.slf4j:slf4j-simple:1.7.30"
|
implementation "org.slf4j:slf4j-simple:1.7.30"
|
||||||
|
|
||||||
|
@ -42,20 +42,23 @@ Language features
|
|||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
||||||
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
|
It generates a machine code program runnable on actual 8-bit 6502 hardware.
|
||||||
- Provide a very convenient edit/compile/run cycle by being able to directly launch
|
- Fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||||
|
- Provides a very convenient edit/compile/run cycle by being able to directly launch
|
||||||
the compiled program in an emulator and provide debugging information to this emulator.
|
the compiled program in an emulator and provide debugging information to this emulator.
|
||||||
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
|
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
|
||||||
- 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 and ROM subroutines,
|
still able to directly use memory addresses and ROM subroutines,
|
||||||
and inline assembly to have full control when every register, cycle or byte matters
|
and inline assembly to have full control when every register, cycle or byte matters
|
||||||
- Arbitrary number of subroutine parameters, Complex nested expressions are possible
|
- Subroutines with parameters and return values
|
||||||
- No stack frame allocations because parameters and local variables are automatically allocated statically
|
- complex nested expressions are possible
|
||||||
|
- Variables are allocated statically
|
||||||
- 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
|
||||||
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
|
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
|
||||||
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
||||||
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||||
|
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||||
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the C64.
|
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the C64.
|
||||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
@ -145,6 +148,8 @@ For MacOS you can use the Homebrew system to install a recent version of OpenJDK
|
|||||||
Finally: an **emulator** (or a real machine ofcourse) to test and run your programs on.
|
Finally: an **emulator** (or a real machine ofcourse) to test and run your programs on.
|
||||||
In C64 mode, thhe compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
In C64 mode, thhe compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||||
If you're targeting the CommanderX16 instead, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
|
If you're targeting the CommanderX16 instead, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
|
||||||
|
Make sure you use cx16 emulator and roms **V39 or newer**! Starting from version 6.5, prog8 targets that system version.
|
||||||
|
Your program may work on V38 but that will only be by luck.
|
||||||
|
|
||||||
.. 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'*)
|
||||||
@ -161,11 +166,11 @@ If you're targeting the CommanderX16 instead, there's the `x16emu <https://githu
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents of this manual:
|
:caption: Contents of this manual:
|
||||||
|
|
||||||
targetsystem.rst
|
|
||||||
building.rst
|
building.rst
|
||||||
programming.rst
|
programming.rst
|
||||||
syntaxreference.rst
|
syntaxreference.rst
|
||||||
libraries.rst
|
libraries.rst
|
||||||
|
targetsystem.rst
|
||||||
technical.rst
|
technical.rst
|
||||||
todo.rst
|
todo.rst
|
||||||
|
|
||||||
|
@ -99,13 +99,32 @@ sys (part of syslib)
|
|||||||
Returns the last address of the program in memory + 1.
|
Returns the last address of the program in memory + 1.
|
||||||
Can be used to load dynamic data after the program, instead of hardcoding something.
|
Can be used to load dynamic data after the program, instead of hardcoding something.
|
||||||
|
|
||||||
|
``wait(uword jiffies)``
|
||||||
|
wait approximately the given number of jiffies (1/60th seconds)
|
||||||
|
note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||||
|
|
||||||
|
``waitvsync()``
|
||||||
|
busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||||
|
can be used to avoid screen flicker/tearing when updating screen contents.
|
||||||
|
note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||||
|
note for cx16: the system irq handler has to be active for this to work (this is not required on c64)
|
||||||
|
|
||||||
|
``waitrastborder()`` (c64 target only)
|
||||||
|
busy wait till the raster position has reached the bottom screen border (approximately)
|
||||||
|
can be used to avoid screen flicker/tearing when updating screen contents.
|
||||||
|
note: a more accurate way to do this is by using a raster irq handler instead.
|
||||||
|
|
||||||
|
``reset_system()``
|
||||||
|
Soft-reset the system back to initial power-on Basic prompt.
|
||||||
|
(called automatically by Prog8 when the main subroutine returns and the program is not using basicsafe zeropage option)
|
||||||
|
|
||||||
|
|
||||||
conv
|
conv
|
||||||
----
|
----
|
||||||
Routines to convert strings to numbers or vice versa.
|
Routines to convert strings to numbers or vice versa.
|
||||||
|
|
||||||
- numbers to strings, in various formats (binary, hex, decimal)
|
- numbers to strings, in various formats (binary, hex, decimal)
|
||||||
- strings in decimal, hex and binary format into numbers
|
- strings in decimal, hex and binary format into numbers (bytes, words)
|
||||||
|
|
||||||
|
|
||||||
textio (txt.*)
|
textio (txt.*)
|
||||||
|
@ -233,6 +233,10 @@ to worry about this yourself)
|
|||||||
|
|
||||||
The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (negative: **-1.7014118345e+38**)
|
The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (negative: **-1.7014118345e+38**)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
On the Commander X16, to use floating point operations, ROM bank 4 has to be enabled (BASIC).
|
||||||
|
Importing the ``floats`` library will do this for you if needed.
|
||||||
|
|
||||||
|
|
||||||
Arrays
|
Arrays
|
||||||
^^^^^^
|
^^^^^^
|
||||||
@ -451,6 +455,14 @@ Breaking out of a loop prematurely is possible with the ``break`` statement.
|
|||||||
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)
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
For efficiency reasons, it is assumed that the ending value of the for loop is actually >= the starting value
|
||||||
|
(or <= if the step is negative). This means that for loops in prog8 behave differently than in other
|
||||||
|
languages if this is *not* the case! A for loop from ubyte 10 to ubyte 2, for example, will iterate through
|
||||||
|
all values 10, 11, 12, 13, .... 254, 255, 0 (wrapped), 1, 2. In other languages the entire loop will
|
||||||
|
be skipped in such cases. But prog8 omits the overhead of an extra loop range check and/or branch for every for loop
|
||||||
|
by assuming the normal ranges.
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution
|
Conditional Execution
|
||||||
---------------------
|
---------------------
|
||||||
@ -642,7 +654,7 @@ Subroutines can be defined in a Block, but also nested inside another subroutine
|
|||||||
With ``asmsub`` you can define a low-level subroutine that is implemented in inline assembly and takes any parameters
|
With ``asmsub`` you can define a low-level subroutine that is implemented in inline assembly and takes any parameters
|
||||||
in registers directly.
|
in registers directly.
|
||||||
|
|
||||||
Trivial subroutines can be tagged as inline to tell the compiler to copy their code
|
Trivial subroutines can be tagged as ``inline`` to tell the compiler to copy their code
|
||||||
in-place to the locations where the subroutine is called, rather than inserting an actual call and return to the
|
in-place to the locations where the subroutine is called, rather than inserting an actual call and return to the
|
||||||
subroutine. This may increase code size significantly and can only be used in limited scenarios, so YMMV.
|
subroutine. This may increase code size significantly and can only be used in limited scenarios, so YMMV.
|
||||||
|
|
||||||
@ -739,9 +751,11 @@ sin16(x)
|
|||||||
|
|
||||||
sqrt16(w)
|
sqrt16(w)
|
||||||
16 bit unsigned integer Square root. Result is unsigned byte.
|
16 bit unsigned integer Square root. Result is unsigned byte.
|
||||||
|
To do the reverse, squaring an integer, just write ``x*x``.
|
||||||
|
|
||||||
sqrt(x)
|
sqrt(x)
|
||||||
Floating point Square root.
|
Floating point Square root.
|
||||||
|
To do the reverse, squaring a floating point number, just write ``x*x`` or ``x**2``.
|
||||||
|
|
||||||
tan(x)
|
tan(x)
|
||||||
Tangent.
|
Tangent.
|
||||||
@ -757,11 +771,12 @@ all(x)
|
|||||||
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
|
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
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 0-byte).
|
||||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
|
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(s) may no longer be correct!
|
||||||
(use strlen function if you want to dynamically determine the length)
|
(use the ``string.length`` routine if you want to dynamically determine the length by counting to the
|
||||||
|
first 0-byte)
|
||||||
|
|
||||||
max(x)
|
max(x)
|
||||||
Maximum of the values in the array value x
|
Maximum of the values in the array value x
|
||||||
@ -788,6 +803,12 @@ sort(array)
|
|||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
cmp(x,y)
|
||||||
|
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
|
||||||
|
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
|
||||||
|
Normally you should just use a comparison expression (``x < y``)
|
||||||
|
|
||||||
lsb(x)
|
lsb(x)
|
||||||
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
||||||
|
|
||||||
@ -824,6 +845,9 @@ 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
|
||||||
|
|
||||||
|
fastrnd8()
|
||||||
|
returns a pseudo-random byte from 0..255 (using a fast but not very good rng)
|
||||||
|
|
||||||
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,
|
||||||
|
@ -129,7 +129,7 @@ Directives
|
|||||||
take care of that yourself. The program will just start running from whatever state the machine is in when the
|
take care of that yourself. The program will just start running from whatever state the machine is in when the
|
||||||
program was launched.
|
program was launched.
|
||||||
- ``force_output`` (in a block) will force the block to be outputted in the final program.
|
- ``force_output`` (in a block) will force the block to be outputted 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).
|
Can be useful to make sure some data is generated that would otherwise be discarded because the compiler thinks it's not referenced (such as sprite data)
|
||||||
- ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address).
|
- ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address).
|
||||||
- ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0).
|
- ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0).
|
||||||
|
|
||||||
@ -550,7 +550,7 @@ and can have nothing following it. The close curly brace must be on its own line
|
|||||||
The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters.
|
The parameters is a (possibly empty) comma separated list of "<datatype> <parametername>" pairs specifying the input parameters.
|
||||||
The return type has to be specified if the subroutine returns a value.
|
The return type has to be specified if the subroutine returns a value.
|
||||||
The ``inline`` keyword makes their code copied in-place to the locations where the subroutine is called,
|
The ``inline`` keyword makes their code copied in-place to the locations where the subroutine is called,
|
||||||
rather than having an actual call and return to the subroutine. This is meant for trivial subroutines only
|
rather than having an actual call and return to the subroutine. This is meant for very small subroutines only
|
||||||
as it can increase code size significantly.
|
as it can increase code size significantly.
|
||||||
|
|
||||||
|
|
||||||
@ -592,7 +592,7 @@ flag such as Carry (Pc).
|
|||||||
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
|
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
|
||||||
directly instead of a call to them. Note that it is literal copy-paste of code that is done,
|
directly instead of a call to them. Note that it is literal copy-paste of code that is done,
|
||||||
so make sure the assembly is actually written to behave like such - which probably means you
|
so make sure the assembly is actually written to behave like such - which probably means you
|
||||||
don't want a ``rts`` or ``jmp`` in it!
|
don't want a ``rts`` or ``jmp`` or ``bra`` in it!
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
===============
|
=================
|
||||||
Technical stuff
|
Technical details
|
||||||
===============
|
=================
|
||||||
|
|
||||||
All variables are static in memory
|
All variables are static in memory
|
||||||
----------------------------------
|
----------------------------------
|
||||||
@ -83,3 +83,37 @@ Some builtin functions have a fully custom implementation.
|
|||||||
The compiler will warn about routines that are called and that return a value, if you're not
|
The compiler will warn about routines that are called and that return a value, if you're not
|
||||||
doing something with that returnvalue. This can be on purpuse if you're simply not interested in it.
|
doing something with that returnvalue. This can be on purpuse if you're simply not interested in it.
|
||||||
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
|
Use the ``void`` keyword in front of the subroutine call to get rid of the warning in that case.
|
||||||
|
|
||||||
|
|
||||||
|
The 6502 CPU's X-register: off-limits
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
Prog8 uses the cpu's X-register as a pointer in its internal expression evaluation stack.
|
||||||
|
When only writing code in Prog8, this is taken care of behind the scenes for you by the compiler.
|
||||||
|
However when you are including or linking with assembly routines or kernal/ROM calls that *do*
|
||||||
|
use the X register (either clobbering it internally, or using it as a parameter, or return value register),
|
||||||
|
those calls will destroy Prog8's stack pointer and this will result in invalid calculations.
|
||||||
|
|
||||||
|
You should avoid using the X register in your assembly code, or take preparations.
|
||||||
|
If you make sure that the value of the X register is preserved before calling a routine
|
||||||
|
that uses it, and restored when the routine is done, you'll be ok.
|
||||||
|
|
||||||
|
Routines that return a value in the X register can be called from Prog8 but the return value is
|
||||||
|
inaccessible unless you write a short piece of inline assembly code to deal with it yourself, such as::
|
||||||
|
|
||||||
|
ubyte returnvalue
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG ; use 'phx/plx' if using 65c02 cpu
|
||||||
|
ldx #10
|
||||||
|
jsr routine_using_x
|
||||||
|
stx returnvalue
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
}}
|
||||||
|
; now use 'returnvalue' variable
|
||||||
|
|
||||||
|
Prog8 also provides some help to deal with this:
|
||||||
|
|
||||||
|
- you should use a ``clobbers(X)`` specification for asmsub routines that modify the X register; the compiler will preserve it for you automatically when such a routine is called
|
||||||
|
- the ``sys.rsave()`` and ``sys.rrestore()`` routines can preserve and restore *all* registers (but this is very slow and overkill if you only need to save X)
|
||||||
|
|
||||||
|
@ -2,31 +2,29 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
- optimize assigning array and struct variables (multi-element assings -> memcopy)
|
|
||||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||||
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||||
- optimize several inner loops in gfx2
|
- get rid of all other TODO's in the code ;-)
|
||||||
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
|
|
||||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)
|
|
||||||
|
Low prio
|
||||||
|
^^^^^^^^
|
||||||
|
- optimize several inner loops in gfx2 even further?
|
||||||
|
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
|
||||||
- add a flood fill routine to gfx2?
|
- add a flood fill routine to gfx2?
|
||||||
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
||||||
- refactor the asmgen into their own submodule?
|
- refactor the asmgen into own submodule
|
||||||
- refactor the compiler optimizers into their own submodule?
|
- refactor the compiler optimizers into own submodule
|
||||||
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
- add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||||
- add a compiler option to not remove unused subroutines. this allows for building library programs
|
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
||||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
|
||||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
|
||||||
- some support for 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
|
|
||||||
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
|
|
||||||
- get rid of all other TODO's in the code ;-)
|
|
||||||
|
|
||||||
More optimizations
|
More optimizations
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Add more compiler optimizations to the existing ones.
|
Add more compiler optimizations to the existing ones.
|
||||||
|
|
||||||
|
- find a way to optimize if-statement codegen so that "if var & %10000" doesn't use stack & subroutine call, but also that the simple case "if X {...}" remains fast
|
||||||
|
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
||||||
- further optimize assignment codegeneration, such as the following:
|
- further optimize assignment codegeneration, such as the following:
|
||||||
- rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?)
|
- rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?)
|
||||||
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
||||||
|
@ -42,7 +42,7 @@ main {
|
|||||||
active_height--
|
active_height--
|
||||||
upwards = false
|
upwards = false
|
||||||
} else {
|
} else {
|
||||||
target_height = 8 + rnd() % 16
|
target_height = 8 + fastrnd8() % 16
|
||||||
if upwards
|
if upwards
|
||||||
mountain = 233
|
mountain = 233
|
||||||
else
|
else
|
||||||
@ -57,7 +57,7 @@ main {
|
|||||||
txt.scroll_left(true)
|
txt.scroll_left(true)
|
||||||
|
|
||||||
; float the balloon
|
; float the balloon
|
||||||
if rnd() & %10000
|
if fastrnd8() & %10000
|
||||||
c64.SPXY[1] ++
|
c64.SPXY[1] ++
|
||||||
else
|
else
|
||||||
c64.SPXY[1] --
|
c64.SPXY[1] --
|
||||||
@ -71,10 +71,10 @@ main {
|
|||||||
txt.setcc(39, yy, 160, 8) ; draw mountain
|
txt.setcc(39, yy, 160, 8) ; draw mountain
|
||||||
}
|
}
|
||||||
|
|
||||||
yy = rnd()
|
yy = fastrnd8()
|
||||||
if yy > 100 {
|
if yy > 100 {
|
||||||
; draw a star
|
; draw a star
|
||||||
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
txt.setcc(39, yy % (active_height-1), '.', fastrnd8())
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 200 {
|
if yy > 200 {
|
||||||
@ -85,7 +85,7 @@ main {
|
|||||||
tree = 88
|
tree = 88
|
||||||
else if yy & %00100000 != 0
|
else if yy & %00100000 != 0
|
||||||
tree = 65
|
tree = 65
|
||||||
if rnd() > 130
|
if fastrnd8() > 130
|
||||||
treecolor = 13
|
treecolor = 13
|
||||||
txt.setcc(39, active_height, tree, treecolor)
|
txt.setcc(39, active_height, tree, treecolor)
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ main {
|
|||||||
; Setup Starting Ball Positions
|
; Setup Starting Ball Positions
|
||||||
ubyte lp
|
ubyte lp
|
||||||
for lp in 0 to ballCount-1 {
|
for lp in 0 to ballCount-1 {
|
||||||
BX[lp] = rnd() % txt.DEFAULT_WIDTH
|
BX[lp] = fastrnd8() % txt.DEFAULT_WIDTH
|
||||||
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
|
BY[lp] = fastrnd8() % txt.DEFAULT_HEIGHT
|
||||||
BC[lp] = rnd() & 15
|
BC[lp] = fastrnd8() & 15
|
||||||
DX[lp] = rnd() & 1
|
DX[lp] = fastrnd8() & 1
|
||||||
DY[lp] = rnd() & 1
|
DY[lp] = fastrnd8() & 1
|
||||||
void rnd()
|
void fastrnd8()
|
||||||
}
|
}
|
||||||
|
|
||||||
; start clock
|
; start clock
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
%import textio
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
; Note: this program is compatible with C64 and CX16.
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
byte v1
|
|
||||||
byte v2
|
|
||||||
|
|
||||||
v1 = 100
|
|
||||||
v2 = 127
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 100==127!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 not == 127\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 100 != 127\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100!=127!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("ok: 100 < 127\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100<127!\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: 100 <= 127\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100<=127!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in 100>127!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 is not >127\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("error in 100>=127!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 is not >=127\n")
|
|
||||||
|
|
||||||
|
|
||||||
v1 = 125
|
|
||||||
v2 = 22
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 125==22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 125 not == 22\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 125 != 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 125!=22!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in 125<22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 125 is not < 22\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("error in 125<=22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 125 is not <= 22\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("ok: 125 > 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 125>22!\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: 125 >= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 125>=22!\n")
|
|
||||||
|
|
||||||
v1 = 22
|
|
||||||
v2 = 22
|
|
||||||
if v1==v2
|
|
||||||
txt.print("ok: 22 == 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22==22!\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("error in 22!=22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not != 22\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in 22<22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not < 22\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: 22 <= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22<=22!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in 22>22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not > 22\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: 22 >= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22>=22!\n")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
%import textio
|
|
||||||
%import floats
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
; Note: this program is compatible with C64 and CX16.
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
float v1
|
|
||||||
float v2
|
|
||||||
|
|
||||||
v1 = 1.11
|
|
||||||
v2 = 699.99
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 1.11==699.99!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 1.11 not == 699.99\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 1.11 != 699.99\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 1.11!=699.99!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("ok: 1.11 < 699.99\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 1.11<699.99!\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: 1.11 <= 699.99\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 1.11<=699.99!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in 1.11>699.99!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 1.11 is not >699.99\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("error in 1.11>=699.99!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 1.11 is not >=699.99\n")
|
|
||||||
|
|
||||||
|
|
||||||
v1 = 555.5
|
|
||||||
v2 = -22.2
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 555.5==-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 555.5 not == -22.2\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 555.5 != -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 555.5!=-22.2!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in 555.5<-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 555.5 is not < -22.2\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("error in 555.5<=-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 555.5 is not <= -22.2\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("ok: 555.5 > -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 555.5>-22.2!\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: 555.5 >= -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 555.5>=-22.2!\n")
|
|
||||||
|
|
||||||
v1 = -22.2
|
|
||||||
v2 = -22.2
|
|
||||||
if v1==v2
|
|
||||||
txt.print("ok: -22.2 == -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in -22.2==-22.2!\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("error in -22.2!=-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: -22.2 is not != -22.2\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in -22.2<-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: -22.2 is not < -22.2\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: -22.2 <= -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in -22.2<=-22.2!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in -22.2>-22.2!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: -22.2 is not > -22.2\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: -22.2 >= -22.2\n")
|
|
||||||
else
|
|
||||||
txt.print("error in -22.2>=-22.2!\n")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
%import textio
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
|
|
||||||
; Note: this program is compatible with C64 and CX16.
|
|
||||||
|
|
||||||
main {
|
|
||||||
|
|
||||||
sub start() {
|
|
||||||
|
|
||||||
ubyte v1
|
|
||||||
ubyte v2
|
|
||||||
|
|
||||||
v1 = 100
|
|
||||||
v2 = 200
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 100==200!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 not == 200\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 100 != 200\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100!=200!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("ok: 100 < 200\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100<200!\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: 100 <= 200\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 100<=200!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in 100>200!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 is not >200\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("error in 100>=200!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 100 is not >=200\n")
|
|
||||||
|
|
||||||
|
|
||||||
v1 = 155
|
|
||||||
v2 = 22
|
|
||||||
if v1==v2
|
|
||||||
txt.print("error in 155==22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 155 not == 22\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("ok: 155 != 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 155!=22!\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in 155<22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 155 is not < 22\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("error in 155<=22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 155 is not <= 22\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("ok: 155 > 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 155>22!\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: 155 >= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 155>=22!\n")
|
|
||||||
|
|
||||||
v1 = 22
|
|
||||||
v2 = 22
|
|
||||||
if v1==v2
|
|
||||||
txt.print("ok: 22 == 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22==22!\n")
|
|
||||||
|
|
||||||
if v1!=v2
|
|
||||||
txt.print("error in 22!=22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not != 22\n")
|
|
||||||
|
|
||||||
if v1<v2
|
|
||||||
txt.print("error in 22<22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not < 22\n")
|
|
||||||
|
|
||||||
if v1<=v2
|
|
||||||
txt.print("ok: 22 <= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22<=22!\n")
|
|
||||||
|
|
||||||
if v1>v2
|
|
||||||
txt.print("error in 22>22!\n")
|
|
||||||
else
|
|
||||||
txt.print("ok: 22 is not > 22\n")
|
|
||||||
|
|
||||||
if v1>=v2
|
|
||||||
txt.print("ok: 22 >= 22\n")
|
|
||||||
else
|
|
||||||
txt.print("error in 22>=22!\n")
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user