mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
243 Commits
Author | SHA1 | Date | |
---|---|---|---|
f4d83075be | |||
254592c383 | |||
ee23ac0537 | |||
a48cf0bb24 | |||
dae59238cd | |||
8736da1a21 | |||
09a1de69e7 | |||
63d67bc6cb | |||
7099245204 | |||
4d097d2139 | |||
6485bf9ad9 | |||
b7c5b1bfc7 | |||
2b7546e827 | |||
3549ccf4b3 | |||
e2f5752d9a | |||
1a59019fc8 | |||
7bac7bdc3e | |||
19fe58dbac | |||
0a5b30e21c | |||
664818fd29 | |||
d5214e2505 | |||
d906fcea0e | |||
29c8e8b740 | |||
71fec4c555 | |||
5ee36c897d | |||
4aba0c7405 | |||
ed7479c854 | |||
8d3d5f726a | |||
a9a7068818 | |||
1bde7c7718 | |||
17068130bb | |||
81a91d62cb | |||
2575263438 | |||
7f0e25cb50 | |||
a1e4e9c50f | |||
98eff2701b | |||
8b84f87217 | |||
306a1b7bc2 | |||
481214c46e | |||
a5961cbeab | |||
3bf335e0a0 | |||
68f696d165 | |||
1170aed026 | |||
bf1b2066b6 | |||
4c080afb76 | |||
ee1c43ca91 | |||
1c2e6f9e4c | |||
dd379430d9 | |||
42033ebd35 | |||
a086d6e009 | |||
c70bbdab26 | |||
3d956ef554 | |||
329f491c30 | |||
e93701f50e | |||
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 | |||
e6079dfd71 | |||
2b435fe6a5 | |||
4e640b11fd | |||
8b1e1e68fa | |||
fd11927708 | |||
cd500fee8c | |||
1bd32c0f19 | |||
7aefca3de0 | |||
f275ed96ea | |||
d14dac3872 | |||
b0213b0565 | |||
c677f0a875 | |||
6e65cb2c0a | |||
e65c5402d7 | |||
334f86480a | |||
0e62f5b759 | |||
edf9a500d3 | |||
001d01fdaf | |||
a95677564e | |||
4aca8bb8df | |||
5540482888 | |||
00d735249b | |||
b5289511ba | |||
b6ded8501f | |||
781915d2cf | |||
f4cef3eaf2 | |||
d23c2eed86 | |||
15695a304e | |||
6319269976 | |||
0ed3d951a7 | |||
2aa39757b4 | |||
39d32a3600 | |||
219d17de34 | |||
9bb5b454e4 | |||
2412f8c531 | |||
8701d684e6 | |||
b543cc34cd | |||
791dbbab9b | |||
ac0b1da3fc | |||
2f97aedc3c | |||
ab544ee965 | |||
fa527f8624 | |||
92ee0aefee | |||
99759ae853 | |||
81930312ff | |||
194fbcdd91 | |||
1e3930aae2 | |||
62dda4d891 | |||
2b870fb9f7 |
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>
|
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Kotlin2JvmCompilerArguments">
|
<component name="Kotlin2JvmCompilerArguments">
|
||||||
<option name="jvmTarget" value="1.8" />
|
<option name="jvmTarget" value="11" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
7
LICENSE
7
LICENSE
@ -1,3 +1,10 @@
|
|||||||
|
|
||||||
|
This sofware license is for Prog8 the compiler + associated libraries.
|
||||||
|
The software generated by running the compiler is excluded from this.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
32
README.md
32
README.md
@ -7,9 +7,6 @@ Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
|||||||
|
|
||||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||||
|
|
||||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
|
||||||
|
|
||||||
|
|
||||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||||
as used in many home computers from that era. It is a medium to low level programming language,
|
as used in many home computers from that era. It is a medium to low level programming language,
|
||||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||||
@ -19,27 +16,36 @@ Documentation
|
|||||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||||
https://prog8.readthedocs.io/
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
|
Software license
|
||||||
|
----------------
|
||||||
|
GNU GPL 3.0, see file LICENSE
|
||||||
|
|
||||||
|
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
||||||
|
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
||||||
|
|
||||||
|
|
||||||
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
|
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||||
- subroutines with an input- and output parameter signature
|
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||||
- no stack frame allocations because parameters and local variables are automatically allocated statically
|
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||||
- constant folding in expressions and other high-level program optimizations
|
- subroutines with input parameters and result values
|
||||||
|
- 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)
|
|
||||||
- '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
|
||||||
- structs to group together sets of variables and manipulate them at once
|
|
||||||
- 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:*
|
||||||
|
|
||||||
@ -52,7 +58,7 @@ What does Prog8 provide?
|
|||||||
|
|
||||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
||||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||||
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
- 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)!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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.5.0"
|
||||||
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"
|
||||||
|
|
||||||
@ -34,6 +33,7 @@ dependencies {
|
|||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
// verbose = true
|
// verbose = true
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||||
}
|
}
|
||||||
@ -42,6 +42,7 @@ compileKotlin {
|
|||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ c64 {
|
|||||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
&ubyte STATUS = $90 ; kernel status variable for I/O
|
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ c64 {
|
|||||||
|
|
||||||
; ---- C64 ROM kernal routines ----
|
; ---- C64 ROM kernal routines ----
|
||||||
|
|
||||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
@ -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
|
||||||
@ -294,6 +294,12 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub init_system_phase2() {
|
||||||
|
%asm {{
|
||||||
|
rts ; no phase 2 steps on the C64
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #$80
|
lda #$80
|
||||||
@ -304,27 +310,13 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_irqvec_excl() clobbers(A) {
|
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
lda #<_irq_handler
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>_irq_handler
|
|
||||||
sta c64.CINV+1
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
_irq_handler jsr set_irqvec._irq_handler_init
|
|
||||||
jsr irq.irq
|
|
||||||
jsr set_irqvec._irq_handler_end
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
|
||||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub set_irqvec() clobbers(A) {
|
|
||||||
%asm {{
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
adc #0
|
||||||
|
sta _use_kernal
|
||||||
sei
|
sei
|
||||||
lda #<_irq_handler
|
lda #<_irq_handler
|
||||||
sta c64.CINV
|
sta c64.CINV
|
||||||
@ -333,9 +325,23 @@ asmsub set_irqvec() clobbers(A) {
|
|||||||
cli
|
cli
|
||||||
rts
|
rts
|
||||||
_irq_handler jsr _irq_handler_init
|
_irq_handler jsr _irq_handler_init
|
||||||
jsr irq.irq
|
_modified jsr $ffff ; modified
|
||||||
jsr _irq_handler_end
|
jsr _irq_handler_end
|
||||||
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
lda _use_kernal
|
||||||
|
bne +
|
||||||
|
lda #$ff
|
||||||
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
|
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
||||||
|
|
||||||
|
_use_kernal .byte 0
|
||||||
|
|
||||||
_irq_handler_init
|
_irq_handler_init
|
||||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||||
@ -388,7 +394,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub restore_irqvec() clobbers(A) {
|
asmsub restore_irq() clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
lda #<c64.IRQDFRT
|
lda #<c64.IRQDFRT
|
||||||
@ -404,8 +410,15 @@ asmsub restore_irqvec() clobbers(A) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
adc #0
|
||||||
|
sta set_irq._use_kernal
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
sei
|
sei
|
||||||
jsr _setup_raster_irq
|
jsr _setup_raster_irq
|
||||||
lda #<_raster_irq_handler
|
lda #<_raster_irq_handler
|
||||||
@ -416,12 +429,21 @@ asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
|||||||
rts
|
rts
|
||||||
|
|
||||||
_raster_irq_handler
|
_raster_irq_handler
|
||||||
jsr set_irqvec._irq_handler_init
|
jsr set_irq._irq_handler_init
|
||||||
jsr irq.irq
|
_modified jsr $ffff ; modified
|
||||||
jsr set_irqvec._irq_handler_end
|
jsr set_irq._irq_handler_end
|
||||||
lda #$ff
|
lda #$ff
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
sta c64.VICIRQ ; acknowledge raster irq
|
||||||
jmp c64.IRQDFRT
|
lda set_irq._use_kernal
|
||||||
|
bne +
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
pla
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
tax
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
||||||
|
|
||||||
_setup_raster_irq
|
_setup_raster_irq
|
||||||
pha
|
pha
|
||||||
@ -445,28 +467,6 @@ _setup_raster_irq
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
|
||||||
%asm {{
|
|
||||||
sei
|
|
||||||
jsr set_rasterirq._setup_raster_irq
|
|
||||||
lda #<_raster_irq_handler
|
|
||||||
sta c64.CINV
|
|
||||||
lda #>_raster_irq_handler
|
|
||||||
sta c64.CINV+1
|
|
||||||
cli
|
|
||||||
rts
|
|
||||||
|
|
||||||
_raster_irq_handler
|
|
||||||
jsr set_irqvec._irq_handler_init
|
|
||||||
jsr irq.irq
|
|
||||||
jsr set_irqvec._irq_handler_end
|
|
||||||
lda #$ff
|
|
||||||
sta c64.VICIRQ ; acknowledge raster irq
|
|
||||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
|
||||||
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
; ---- end of C64 specific system utility routines ----
|
; ---- end of C64 specific system utility routines ----
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -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
|
||||||
@ -679,4 +703,37 @@ cx16 {
|
|||||||
&uword r14 = $cf1c
|
&uword r14 = $cf1c
|
||||||
&uword r15 = $cf1e
|
&uword r15 = $cf1e
|
||||||
|
|
||||||
|
&ubyte r0L = $cf00
|
||||||
|
&ubyte r1L = $cf02
|
||||||
|
&ubyte r2L = $cf04
|
||||||
|
&ubyte r3L = $cf06
|
||||||
|
&ubyte r4L = $cf08
|
||||||
|
&ubyte r5L = $cf0a
|
||||||
|
&ubyte r6L = $cf0c
|
||||||
|
&ubyte r7L = $cf0e
|
||||||
|
&ubyte r8L = $cf10
|
||||||
|
&ubyte r9L = $cf12
|
||||||
|
&ubyte r10L = $cf14
|
||||||
|
&ubyte r11L = $cf16
|
||||||
|
&ubyte r12L = $cf18
|
||||||
|
&ubyte r13L = $cf1a
|
||||||
|
&ubyte r14L = $cf1c
|
||||||
|
&ubyte r15L = $cf1e
|
||||||
|
|
||||||
|
&ubyte r0H = $cf01
|
||||||
|
&ubyte r1H = $cf03
|
||||||
|
&ubyte r2H = $cf05
|
||||||
|
&ubyte r3H = $cf07
|
||||||
|
&ubyte r4H = $cf09
|
||||||
|
&ubyte r5H = $cf0b
|
||||||
|
&ubyte r6H = $cf0d
|
||||||
|
&ubyte r7H = $cf0f
|
||||||
|
&ubyte r8H = $cf11
|
||||||
|
&ubyte r9H = $cf13
|
||||||
|
&ubyte r10H = $cf15
|
||||||
|
&ubyte r11H = $cf17
|
||||||
|
&ubyte r12H = $cf19
|
||||||
|
&ubyte r13H = $cf1b
|
||||||
|
&ubyte r14H = $cf1d
|
||||||
|
&ubyte r15H = $cf1f
|
||||||
}
|
}
|
||||||
|
@ -586,7 +586,7 @@ _colormod sta $ffff ; modified
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||||
%asm {{
|
%asm {{
|
||||||
stx P8ZP_SCRATCH_REG
|
stx P8ZP_SCRATCH_REG
|
||||||
tax
|
tax
|
||||||
|
@ -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
|
||||||
rts
|
sty string_out
|
||||||
|
sta string_out+1
|
||||||
|
stx string_out+2
|
||||||
|
lda #0
|
||||||
|
sta string_out+3
|
||||||
|
plx
|
||||||
|
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 +
|
||||||
|
|
||||||
;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
|
pha
|
||||||
and #$0f
|
tya
|
||||||
tax
|
ldy P8ZP_SCRATCH_B1
|
||||||
ldy _hex_digits,x
|
sta string_out,y
|
||||||
pla
|
pla
|
||||||
lsr a
|
inc P8ZP_SCRATCH_B1
|
||||||
lsr a
|
; tens?
|
||||||
lsr a
|
+ ldy P8ZP_SCRATCH_B1
|
||||||
lsr a
|
cmp #'0'
|
||||||
tax
|
beq +
|
||||||
lda _hex_digits,x
|
sta string_out,y
|
||||||
ldx P8ZP_SCRATCH_REG
|
iny
|
||||||
rts
|
+ ; ones.
|
||||||
|
txa
|
||||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
sta string_out,y
|
||||||
|
iny
|
||||||
|
lda #0
|
||||||
|
sta string_out,y
|
||||||
|
plx
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
|
asmsub str_b (byte value @ A) clobbers(A,Y) {
|
||||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
; ---- convert the byte in A in decimal string form, without left padding 0s
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_REG
|
phx
|
||||||
tya
|
ldy #0
|
||||||
jsr ubyte2hex
|
sty P8ZP_SCRATCH_B1
|
||||||
sta output
|
cmp #0
|
||||||
sty output+1
|
bpl +
|
||||||
lda P8ZP_SCRATCH_REG
|
pha
|
||||||
jsr ubyte2hex
|
lda #'-'
|
||||||
sta output+2
|
sta string_out
|
||||||
sty output+3
|
inc P8ZP_SCRATCH_B1
|
||||||
rts
|
pla
|
||||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
+ jsr conv.byte2decimal
|
||||||
|
bra str_ub._output_byte_digits
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
|
||||||
|
; ---- 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 {{
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
tya
|
||||||
|
jsr str_ubbin
|
||||||
|
ldy #0
|
||||||
|
sty string_out+16
|
||||||
|
ldy #7
|
||||||
|
- lsr P8ZP_SCRATCH_REG
|
||||||
|
bcc +
|
||||||
|
lda #'1'
|
||||||
|
bne _digit
|
||||||
|
+ lda #'0'
|
||||||
|
_digit sta string_out+8,y
|
||||||
|
dey
|
||||||
|
bpl -
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +231,7 @@ asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
|||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (P8ZP_SCRATCH_W1)
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
cmp #'$'
|
cmp #'$'
|
||||||
beq _hex
|
beq _hex
|
||||||
@ -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 {{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
; Bitmap pixel graphics routines for the CommanderX16
|
; Bitmap pixel graphics routines for the CommanderX16
|
||||||
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
||||||
; (These modes are not supported by the documented GRAPH_xxxx kernel routines)
|
; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
|
||||||
;
|
;
|
||||||
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
||||||
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
|
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
|
||||||
@ -15,13 +15,13 @@
|
|||||||
; SCREEN MODE LIST:
|
; SCREEN MODE LIST:
|
||||||
; mode 0 = reset back to default text mode
|
; mode 0 = reset back to default text mode
|
||||||
; mode 1 = bitmap 320 x 240 monochrome
|
; mode 1 = bitmap 320 x 240 monochrome
|
||||||
; mode 2 = bitmap 320 x 240 x 4c (unsupported TODO not yet implemented)
|
; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
|
||||||
; mode 3 = bitmap 320 x 240 x 16c (unsupported TODO not yet implemented)
|
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
|
||||||
; mode 4 = bitmap 320 x 240 x 256c
|
; mode 4 = bitmap 320 x 240 x 256c
|
||||||
; mode 5 = bitmap 640 x 480 monochrome
|
; mode 5 = bitmap 640 x 480 monochrome
|
||||||
; mode 6 = bitmap 640 x 480 x 4c (unsupported TODO being implemented)
|
; mode 6 = bitmap 640 x 480 x 4c
|
||||||
; mode 7 = bitmap 640 x 480 x 16c (unsupported due to lack of VRAM)
|
; higher color dephts in highres are not supported due to lack of VRAM
|
||||||
; mode 8 = bitmap 640 x 480 x 256c (unsupported due to lack of VRAM)
|
|
||||||
|
|
||||||
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
|
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
|
||||||
|
|
||||||
@ -37,7 +37,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
|
||||||
@ -85,7 +85,6 @@ gfx2 {
|
|||||||
height = 480
|
height = 480
|
||||||
bpp = 2
|
bpp = 2
|
||||||
}
|
}
|
||||||
; modes 7 and 8 not supported due to lack of VRAM
|
|
||||||
else -> {
|
else -> {
|
||||||
; back to default text mode and colors
|
; back to default text mode and colors
|
||||||
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
||||||
@ -168,7 +167,7 @@ gfx2 {
|
|||||||
if separate_pixels as uword > length
|
if separate_pixels as uword > length
|
||||||
separate_pixels = lsb(length)
|
separate_pixels = lsb(length)
|
||||||
repeat separate_pixels {
|
repeat separate_pixels {
|
||||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
; TODO optimize this by writing a masked byte in 1 go
|
||||||
plot(x, y, color)
|
plot(x, y, color)
|
||||||
x++
|
x++
|
||||||
}
|
}
|
||||||
@ -210,7 +209,7 @@ _loop lda length
|
|||||||
_done
|
_done
|
||||||
}}
|
}}
|
||||||
repeat separate_pixels {
|
repeat separate_pixels {
|
||||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
; TODO optimize this by writing a masked byte in 1 go
|
||||||
plot(x, y, color)
|
plot(x, y, color)
|
||||||
x++
|
x++
|
||||||
}
|
}
|
||||||
@ -276,9 +275,9 @@ _done
|
|||||||
ora colorbits,y
|
ora colorbits,y
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
cpy #%00000011 ; next vera byte?
|
cpy #%00000011 ; next vera byte?
|
||||||
bne +
|
bne ++
|
||||||
inc cx16.VERA_ADDR_L
|
inc cx16.VERA_ADDR_L
|
||||||
bne +
|
bne ++
|
||||||
inc cx16.VERA_ADDR_M
|
inc cx16.VERA_ADDR_M
|
||||||
+ bne +
|
+ bne +
|
||||||
inc cx16.VERA_ADDR_H
|
inc cx16.VERA_ADDR_H
|
||||||
@ -294,90 +293,56 @@ _done
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub vertical_line(uword x, uword y, uword height, ubyte color) {
|
sub vertical_line(uword x, uword y, uword height, ubyte color) {
|
||||||
position(x,y)
|
|
||||||
when active_mode {
|
when active_mode {
|
||||||
1, 5 -> {
|
1, 5 -> {
|
||||||
; monochrome, either resolution
|
; monochrome, lo-res
|
||||||
; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
|
||||||
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
|
||||||
if active_mode>=5
|
|
||||||
cx16.r14 = 640/8
|
|
||||||
else
|
|
||||||
cx16.r14 = 320/8
|
|
||||||
if color {
|
if color {
|
||||||
if monochrome_dont_stipple_flag {
|
if monochrome_dont_stipple_flag {
|
||||||
|
; draw continuous line.
|
||||||
|
position2(x,y,true)
|
||||||
|
if active_mode==1
|
||||||
|
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||||
|
else
|
||||||
|
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||||
repeat height {
|
repeat height {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
ora cx16.r15
|
ora cx16.r15L
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA1
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
clc
|
|
||||||
adc cx16.r14 ; advance vera ptr to go to the next line
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
adc #0
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
|
||||||
; adc #0
|
|
||||||
; sta cx16.VERA_ADDR_H
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
; stippling.
|
; draw stippled line.
|
||||||
height = (height+1)/2
|
if x&1 {
|
||||||
%asm {{
|
y++
|
||||||
lda x
|
height--
|
||||||
eor y
|
}
|
||||||
and #1
|
position2(x,y,true)
|
||||||
bne +
|
if active_mode==1
|
||||||
lda cx16.VERA_ADDR_L
|
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||||
clc
|
else
|
||||||
adc cx16.r14 ; advance vera ptr to go to the next line for correct stipple pattern
|
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||||
sta cx16.VERA_ADDR_L
|
repeat height/2 {
|
||||||
lda cx16.VERA_ADDR_M
|
%asm {{
|
||||||
adc #0
|
lda cx16.VERA_DATA0
|
||||||
sta cx16.VERA_ADDR_M
|
ora cx16.r15L
|
||||||
+
|
sta cx16.VERA_DATA1
|
||||||
asl cx16.r14
|
}}
|
||||||
ldy height
|
}
|
||||||
beq +
|
|
||||||
- lda cx16.VERA_DATA0
|
|
||||||
ora cx16.r15
|
|
||||||
sta cx16.VERA_DATA0
|
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
clc
|
|
||||||
adc cx16.r14 ; advance vera data ptr to go to the next-next line
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
adc #0
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
|
||||||
; adc #0
|
|
||||||
; sta cx16.VERA_ADDR_H
|
|
||||||
dey
|
|
||||||
bne -
|
|
||||||
+
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cx16.r15 = ~cx16.r15
|
position2(x,y,true)
|
||||||
|
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||||
|
if active_mode==1
|
||||||
|
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||||
|
else
|
||||||
|
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||||
repeat height {
|
repeat height {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
and cx16.r15
|
and cx16.r15L
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA1
|
||||||
lda cx16.VERA_ADDR_L
|
|
||||||
clc
|
|
||||||
adc cx16.r14 ; advance vera data ptr to go to the next line
|
|
||||||
sta cx16.VERA_ADDR_L
|
|
||||||
lda cx16.VERA_ADDR_M
|
|
||||||
adc #0
|
|
||||||
sta cx16.VERA_ADDR_M
|
|
||||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
|
||||||
; adc #0
|
|
||||||
; sta cx16.VERA_ADDR_H
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -385,6 +350,7 @@ _done
|
|||||||
4 -> {
|
4 -> {
|
||||||
; lores 256c
|
; lores 256c
|
||||||
; set vera auto-increment to 320 pixel increment (=next line)
|
; set vera auto-increment to 320 pixel increment (=next line)
|
||||||
|
position(x,y)
|
||||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||||
%asm {{
|
%asm {{
|
||||||
ldy height
|
ldy height
|
||||||
@ -398,82 +364,78 @@ _done
|
|||||||
}
|
}
|
||||||
6 -> {
|
6 -> {
|
||||||
; highres 4c
|
; highres 4c
|
||||||
; note for this mode we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
if height==0
|
||||||
; TODO also mostly usable for lores 4c?
|
return
|
||||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
position2(x,y,true)
|
||||||
|
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||||
; TODO optimize the 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]
|
||||||
repeat height {
|
repeat height {
|
||||||
ubyte value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask | color
|
|
||||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
|
|
||||||
%asm {{
|
%asm {{
|
||||||
; 24 bits add 160 (640/4)
|
lda cx16.VERA_DATA0
|
||||||
clc
|
and mask
|
||||||
lda cx16.r0
|
ora color
|
||||||
adc #640/4
|
sta cx16.VERA_DATA1
|
||||||
sta cx16.r0
|
|
||||||
lda cx16.r0+1
|
|
||||||
adc #0
|
|
||||||
sta cx16.r0+1
|
|
||||||
bcc +
|
|
||||||
inc cx16.r1
|
|
||||||
+
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_both_strides(ubyte stride) {
|
||||||
|
stride <<= 4
|
||||||
|
cx16.VERA_CTRL = 0
|
||||||
|
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||||
|
cx16.VERA_CTRL = 1
|
||||||
|
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -482,25 +444,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 {
|
||||||
@ -509,10 +471,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,8 +550,6 @@ _done
|
|||||||
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
|
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||||
ubyte[4] shift4c = [6,4,2,0]
|
ubyte[4] shift4c = [6,4,2,0]
|
||||||
uword addr
|
|
||||||
ubyte value
|
|
||||||
|
|
||||||
when active_mode {
|
when active_mode {
|
||||||
1 -> {
|
1 -> {
|
||||||
@ -601,23 +561,43 @@ _done
|
|||||||
and #1
|
and #1
|
||||||
}}
|
}}
|
||||||
if_nz {
|
if_nz {
|
||||||
addr = x/8 + y*(320/8)
|
cx16.r0L = lsb(x) & 7 ; xbits
|
||||||
value = bits[lsb(x)&7]
|
x /= 8
|
||||||
if color
|
x += y*(320/8)
|
||||||
cx16.vpoke_or(0, addr, value)
|
%asm {{
|
||||||
else {
|
stz cx16.VERA_CTRL
|
||||||
value = ~value
|
stz cx16.VERA_ADDR_H
|
||||||
cx16.vpoke_and(0, addr, value)
|
lda x+1
|
||||||
}
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda x
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
ldy cx16.r0L ; xbits
|
||||||
|
lda bits,y
|
||||||
|
ldy color
|
||||||
|
beq +
|
||||||
|
tsb cx16.VERA_DATA0
|
||||||
|
bra ++
|
||||||
|
+ trb cx16.VERA_DATA0
|
||||||
|
+
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
; TODO mode 2,3
|
||||||
4 -> {
|
4 -> {
|
||||||
; lores 256c
|
; lores 256c
|
||||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, color)
|
%asm {{
|
||||||
; activate vera auto-increment mode so next_pixel() can be used after this
|
stz cx16.VERA_CTRL
|
||||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | %00010000
|
lda cx16.r1
|
||||||
color = cx16.VERA_DATA0
|
ora #%00010000 ; enable auto-increment so next_pixel() can be used after this
|
||||||
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.r0+1
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.r0
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
lda color
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
5 -> {
|
5 -> {
|
||||||
; highres monochrome
|
; highres monochrome
|
||||||
@ -628,26 +608,48 @@ _done
|
|||||||
and #1
|
and #1
|
||||||
}}
|
}}
|
||||||
if_nz {
|
if_nz {
|
||||||
addr = x/8 + y*(640/8)
|
cx16.r0L = lsb(x) & 7 ; xbits
|
||||||
value = bits[lsb(x)&7]
|
x /= 8
|
||||||
if color
|
x += y*(640/8)
|
||||||
cx16.vpoke_or(0, addr, value)
|
%asm {{
|
||||||
else {
|
stz cx16.VERA_CTRL
|
||||||
value = ~value
|
stz cx16.VERA_ADDR_H
|
||||||
cx16.vpoke_and(0, addr, value)
|
lda x+1
|
||||||
}
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda x
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
ldy cx16.r0L ; xbits
|
||||||
|
lda bits,y
|
||||||
|
ldy color
|
||||||
|
beq +
|
||||||
|
tsb cx16.VERA_DATA0
|
||||||
|
bra ++
|
||||||
|
+ trb cx16.VERA_DATA0
|
||||||
|
+
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6 -> {
|
6 -> {
|
||||||
; highres 4c
|
; highres 4c
|
||||||
; 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)
|
||||||
|
cx16.r2L = lsb(x) & 3 ; xbits
|
||||||
color &= 3
|
color &= 3
|
||||||
color <<= shift4c[lsb(x) & 3]
|
color <<= shift4c[cx16.r2L]
|
||||||
; TODO optimize the vera memory manipulation in pure assembly
|
%asm {{
|
||||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
stz cx16.VERA_CTRL
|
||||||
value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask4c[lsb(x) & 3] | color
|
lda cx16.r1L
|
||||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
|
sta cx16.VERA_ADDR_H
|
||||||
|
lda cx16.r0H
|
||||||
|
sta cx16.VERA_ADDR_M
|
||||||
|
lda cx16.r0L
|
||||||
|
sta cx16.VERA_ADDR_L
|
||||||
|
ldy cx16.r2L ; xbits
|
||||||
|
lda mask4c,y
|
||||||
|
and cx16.VERA_DATA0
|
||||||
|
ora color
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,6 +683,20 @@ _done
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub position2(uword @zp x, uword y, ubyte also_port_1) {
|
||||||
|
position(x, y)
|
||||||
|
if also_port_1 {
|
||||||
|
when active_mode {
|
||||||
|
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
|
||||||
|
; TODO modes 2, 3
|
||||||
|
4, 6 -> {
|
||||||
|
ubyte bank = lsb(cx16.r1)
|
||||||
|
cx16.vaddr(bank, cx16.r0, 1, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline asmsub next_pixel(ubyte color @A) {
|
inline asmsub next_pixel(ubyte color @A) {
|
||||||
; -- sets the next pixel byte to the graphics chip.
|
; -- sets the next pixel byte to the graphics chip.
|
||||||
; for 8 bpp screens this will plot 1 pixel.
|
; for 8 bpp screens this will plot 1 pixel.
|
||||||
@ -760,13 +776,13 @@ _done
|
|||||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
||||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||||
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 ! TODO allow per-pixel horizontal positioning
|
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
|
||||||
uword chardataptr
|
uword chardataptr
|
||||||
when active_mode {
|
when active_mode {
|
||||||
1, 5 -> {
|
1, 5 -> {
|
||||||
; monochrome mode, either resolution
|
; monochrome mode, either resolution
|
||||||
cx16.r2 = 40
|
cx16.r2 = 40
|
||||||
if active_mode>=5
|
if active_mode==5
|
||||||
cx16.r2 = 80
|
cx16.r2 = 80
|
||||||
while @(sctextptr) {
|
while @(sctextptr) {
|
||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
@ -812,6 +828,7 @@ _done
|
|||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
|
; TODO rewrite this inner loop fully in assembly
|
||||||
position(x,y)
|
position(x,y)
|
||||||
y++
|
y++
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -840,7 +857,7 @@ _done
|
|||||||
while @(sctextptr) {
|
while @(sctextptr) {
|
||||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
; TODO rewrite this inner loop in assembly
|
; TODO rewrite this inner loop fully in assembly
|
||||||
ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
|
ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
|
||||||
repeat 8 {
|
repeat 8 {
|
||||||
charbits <<= 1
|
charbits <<= 1
|
||||||
@ -877,15 +894,31 @@ _done
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub addr_mul_24_for_highres_4c(uword yy, uword xx) {
|
asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
|
||||||
; TODO turn into asmsub
|
; yy * 160 + xx/4 (24 bits calculation)
|
||||||
; 24 bits result is in r0 and r1L (highest byte)
|
; 24 bits result is in r0 and r1L (highest byte)
|
||||||
cx16.r0 = yy*128
|
|
||||||
cx16.r2 = yy*32
|
|
||||||
xx >>= 2
|
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
; add r2 and xx to r0 (24-bits)
|
ldy #5
|
||||||
|
- asl cx16.r2
|
||||||
|
rol cx16.r2+1
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
lda cx16.r2
|
||||||
|
sta cx16.r0
|
||||||
|
lda cx16.r2+1
|
||||||
|
sta cx16.r0+1
|
||||||
|
asl cx16.r0
|
||||||
|
rol cx16.r0+1
|
||||||
|
asl cx16.r0
|
||||||
|
rol cx16.r0+1
|
||||||
|
|
||||||
|
; xx >>= 2 (xx=R3)
|
||||||
|
lsr cx16.r3+1
|
||||||
|
ror cx16.r3
|
||||||
|
lsr cx16.r3+1
|
||||||
|
ror cx16.r3
|
||||||
|
|
||||||
|
; add r2 and xx (r3) to r0 (24-bits)
|
||||||
stz cx16.r1
|
stz cx16.r1
|
||||||
clc
|
clc
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
@ -898,60 +931,61 @@ _done
|
|||||||
inc cx16.r1
|
inc cx16.r1
|
||||||
+ clc
|
+ clc
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
adc xx
|
adc cx16.r3
|
||||||
sta cx16.r0
|
sta cx16.r0
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
adc xx+1
|
adc cx16.r3+1
|
||||||
sta cx16.r0+1
|
sta cx16.r0+1
|
||||||
bcc +
|
bcc +
|
||||||
inc cx16.r1
|
inc cx16.r1
|
||||||
+
|
+
|
||||||
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 {
|
asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 {
|
||||||
; yy * 320 + xx (24 bits calculation)
|
; yy * 320 + xx (24 bits calculation)
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.r1
|
sta cx16.r1
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
rol P8ZP_SCRATCH_REG
|
rol P8ZP_SCRATCH_REG
|
||||||
sta cx16.r0
|
sta cx16.r0
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_REG
|
adc P8ZP_SCRATCH_REG
|
||||||
sta cx16.r0+1
|
sta cx16.r0+1
|
||||||
bcc +
|
bcc +
|
||||||
inc cx16.r1
|
inc cx16.r1
|
||||||
+ ; now add the value to this 24-bits number
|
+ ; now add the value to this 24-bits number
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
clc
|
clc
|
||||||
adc P8ZP_SCRATCH_W1
|
adc P8ZP_SCRATCH_W1
|
||||||
sta cx16.r0
|
sta cx16.r0
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
adc P8ZP_SCRATCH_W1+1
|
adc P8ZP_SCRATCH_W1+1
|
||||||
sta cx16.r0+1
|
sta cx16.r0+1
|
||||||
bcc +
|
bcc +
|
||||||
inc cx16.r1
|
inc cx16.r1
|
||||||
+ lda cx16.r1
|
+ lda cx16.r1
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -9,8 +9,10 @@ palette {
|
|||||||
ubyte c
|
ubyte c
|
||||||
|
|
||||||
sub set_color(ubyte index, uword color) {
|
sub set_color(ubyte index, uword color) {
|
||||||
cx16.vpoke(1, $fa00+index*2, lsb(color))
|
vera_palette_ptr = $fa00+index*2
|
||||||
cx16.vpoke(1, $fa01+index*2, msb(color))
|
cx16.vpoke(1, vera_palette_ptr, lsb(color))
|
||||||
|
vera_palette_ptr++
|
||||||
|
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
||||||
@ -98,7 +100,7 @@ palette {
|
|||||||
$666, ; 12 = medium grey
|
$666, ; 12 = medium grey
|
||||||
$9D8, ; 13 = light green
|
$9D8, ; 13 = light green
|
||||||
$65B, ; 14 = light blue
|
$65B, ; 14 = light blue
|
||||||
$999 ; 15 = light grey
|
$999 ; 15 = light grey
|
||||||
]
|
]
|
||||||
|
|
||||||
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||||
@ -117,7 +119,7 @@ palette {
|
|||||||
$777, ; 12 = medium grey
|
$777, ; 12 = medium grey
|
||||||
$af9, ; 13 = light green
|
$af9, ; 13 = light green
|
||||||
$76e, ; 14 = light blue
|
$76e, ; 14 = light blue
|
||||||
$bbb ; 15 = light grey
|
$bbb ; 15 = light grey
|
||||||
]
|
]
|
||||||
|
|
||||||
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
||||||
|
@ -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,25 +87,15 @@ 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 {
|
||||||
|
|
||||||
; 65c02 hardware vectors:
|
; irq and hardware vectors:
|
||||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||||
|
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||||
|
|
||||||
|
|
||||||
; the sixteen virtual 16-bit registers
|
; the sixteen virtual 16-bit registers
|
||||||
@ -126,6 +116,41 @@ cx16 {
|
|||||||
&uword r14 = $001e
|
&uword r14 = $001e
|
||||||
&uword r15 = $0020
|
&uword r15 = $0020
|
||||||
|
|
||||||
|
&ubyte r0L = $0002
|
||||||
|
&ubyte r1L = $0004
|
||||||
|
&ubyte r2L = $0006
|
||||||
|
&ubyte r3L = $0008
|
||||||
|
&ubyte r4L = $000a
|
||||||
|
&ubyte r5L = $000c
|
||||||
|
&ubyte r6L = $000e
|
||||||
|
&ubyte r7L = $0010
|
||||||
|
&ubyte r8L = $0012
|
||||||
|
&ubyte r9L = $0014
|
||||||
|
&ubyte r10L = $0016
|
||||||
|
&ubyte r11L = $0018
|
||||||
|
&ubyte r12L = $001a
|
||||||
|
&ubyte r13L = $001c
|
||||||
|
&ubyte r14L = $001e
|
||||||
|
&ubyte r15L = $0020
|
||||||
|
|
||||||
|
&ubyte r0H = $0003
|
||||||
|
&ubyte r1H = $0005
|
||||||
|
&ubyte r2H = $0007
|
||||||
|
&ubyte r3H = $0009
|
||||||
|
&ubyte r4H = $000b
|
||||||
|
&ubyte r5H = $000d
|
||||||
|
&ubyte r6H = $000f
|
||||||
|
&ubyte r7H = $0011
|
||||||
|
&ubyte r8H = $0013
|
||||||
|
&ubyte r9H = $0015
|
||||||
|
&ubyte r10H = $0017
|
||||||
|
&ubyte r11H = $0019
|
||||||
|
&ubyte r12H = $001b
|
||||||
|
&ubyte r13H = $001d
|
||||||
|
&ubyte r14H = $001f
|
||||||
|
&ubyte r15H = $0021
|
||||||
|
|
||||||
|
|
||||||
; VERA registers
|
; VERA registers
|
||||||
|
|
||||||
const uword VERA_BASE = $9F20
|
const uword VERA_BASE = $9F20
|
||||||
@ -171,7 +196,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
|
||||||
@ -189,7 +214,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
|
||||||
@ -207,6 +232,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
|
||||||
@ -293,16 +323,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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,75 +388,126 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
|
|||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||||
; -- write a single byte to VERA's video memory
|
; -- write a single byte to VERA's video memory
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
sty cx16.VERA_DATA0
|
sty cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
||||||
; -- or a single byte to the value already in the VERA's video memory at that location
|
; -- or a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
ora cx16.VERA_DATA0
|
ora cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
|
||||||
; -- and a single byte to the value already in the VERA's video memory at that location
|
; -- and a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
and cx16.VERA_DATA0
|
and cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
|
||||||
; -- xor a single byte to the value already in the VERA's video memory at that location
|
; -- xor a single byte to the value already in the VERA's video memory at that location
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
stz cx16.VERA_CTRL
|
stz cx16.VERA_CTRL
|
||||||
and #1
|
and #1
|
||||||
sta cx16.VERA_ADDR_H
|
sta cx16.VERA_ADDR_H
|
||||||
lda cx16.r0
|
lda cx16.r0
|
||||||
sta cx16.VERA_ADDR_L
|
sta cx16.VERA_ADDR_L
|
||||||
lda cx16.r0+1
|
lda cx16.r0+1
|
||||||
sta cx16.VERA_ADDR_M
|
sta cx16.VERA_ADDR_M
|
||||||
tya
|
tya
|
||||||
eor cx16.VERA_DATA0
|
eor cx16.VERA_DATA0
|
||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
@ -453,17 +543,15 @@ _loop ldy #0
|
|||||||
}
|
}
|
||||||
|
|
||||||
; ---- system stuff -----
|
; ---- system stuff -----
|
||||||
asmsub init_system() {
|
asmsub init_system() {
|
||||||
; Initializes the machine to a sane starting state.
|
; Initializes the machine to a sane starting state.
|
||||||
; Called automatically by the loader program logic.
|
; Called automatically by the loader program logic.
|
||||||
%asm {{
|
%asm {{
|
||||||
sei
|
sei
|
||||||
cld
|
cld
|
||||||
;stz $00
|
|
||||||
;stz $01
|
|
||||||
;stz d1prb ; select rom bank 0
|
|
||||||
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
|
||||||
@ -485,8 +573,177 @@ asmsub init_system() {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub init_system_phase2() {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda cx16.CINV
|
||||||
|
sta restore_irq._orig_irqvec
|
||||||
|
lda cx16.CINV+1
|
||||||
|
sta restore_irq._orig_irqvec+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda #0
|
||||||
|
adc #0
|
||||||
|
sta _use_kernal
|
||||||
|
sei
|
||||||
|
lda #<_irq_handler
|
||||||
|
sta cx16.CINV
|
||||||
|
lda #>_irq_handler
|
||||||
|
sta cx16.CINV+1
|
||||||
|
lda cx16.VERA_IEN
|
||||||
|
ora #%00000001 ; enable the vsync irq
|
||||||
|
sta cx16.VERA_IEN
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
|
||||||
|
_irq_handler jsr _irq_handler_init
|
||||||
|
_modified jsr $ffff ; modified
|
||||||
|
jsr _irq_handler_end
|
||||||
|
lda _use_kernal
|
||||||
|
bne +
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
lda cx16.VERA_ISR
|
||||||
|
ora #1
|
||||||
|
sta cx16.VERA_ISR ; clear Vera Vsync irq status
|
||||||
|
ply
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
+ jmp (restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||||
|
|
||||||
|
_use_kernal .byte 0
|
||||||
|
|
||||||
|
_irq_handler_init
|
||||||
|
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||||
|
stx IRQ_X_REG
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
sta IRQ_SCRATCH_ZPB1
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
sta IRQ_SCRATCH_ZPREG
|
||||||
|
lda P8ZP_SCRATCH_W1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1
|
||||||
|
lda P8ZP_SCRATCH_W1+1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
sta IRQ_SCRATCH_ZPWORD2
|
||||||
|
lda P8ZP_SCRATCH_W2+1
|
||||||
|
sta IRQ_SCRATCH_ZPWORD2+1
|
||||||
|
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
dex
|
||||||
|
cld
|
||||||
|
rts
|
||||||
|
|
||||||
|
_irq_handler_end
|
||||||
|
; restore all zp scratch registers and the X register
|
||||||
|
lda IRQ_SCRATCH_ZPB1
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
lda IRQ_SCRATCH_ZPREG
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD1+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda IRQ_SCRATCH_ZPWORD2+1
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
ldx IRQ_X_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
IRQ_X_REG .byte 0
|
||||||
|
IRQ_SCRATCH_ZPB1 .byte 0
|
||||||
|
IRQ_SCRATCH_ZPREG .byte 0
|
||||||
|
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||||
|
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
asmsub restore_irq() clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sei
|
||||||
|
lda _orig_irqvec
|
||||||
|
sta cx16.CINV
|
||||||
|
lda _orig_irqvec+1
|
||||||
|
sta cx16.CINV+1
|
||||||
|
lda cx16.VERA_IEN
|
||||||
|
and #%11110000 ; disable all Vera IRQs
|
||||||
|
ora #%00000001 ; enable only the vsync Irq
|
||||||
|
sta cx16.VERA_IEN
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
_orig_irqvec .word 0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||||
|
%asm {{
|
||||||
|
sta _modified+1
|
||||||
|
sty _modified+2
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
sei
|
||||||
|
lda cx16.VERA_IEN
|
||||||
|
and #%11110000 ; clear other IRQs
|
||||||
|
ora #%00000010 ; enable the line (raster) irq
|
||||||
|
sta cx16.VERA_IEN
|
||||||
|
lda cx16.r0
|
||||||
|
ldy cx16.r0+1
|
||||||
|
jsr set_rasterline
|
||||||
|
lda #<_raster_irq_handler
|
||||||
|
sta cx16.CINV
|
||||||
|
lda #>_raster_irq_handler
|
||||||
|
sta cx16.CINV+1
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
|
||||||
|
_raster_irq_handler
|
||||||
|
jsr set_irq._irq_handler_init
|
||||||
|
_modified jsr $ffff ; modified
|
||||||
|
jsr set_irq._irq_handler_end
|
||||||
|
; end irq processing - don't use kernal's irq handling
|
||||||
|
lda cx16.VERA_ISR
|
||||||
|
ora #%00000010
|
||||||
|
sta cx16.VERA_ISR ; clear Vera line irq status
|
||||||
|
ply
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
rti
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub set_rasterline(uword line @AY) {
|
||||||
|
%asm {{
|
||||||
|
sta cx16.VERA_IRQ_LINE_L
|
||||||
|
lda cx16.VERA_IEN
|
||||||
|
and #%01111111
|
||||||
|
sta cx16.VERA_IEN
|
||||||
|
tya
|
||||||
|
lsr a
|
||||||
|
ror a
|
||||||
|
and #%10000000
|
||||||
|
ora cx16.VERA_IEN
|
||||||
|
sta cx16.VERA_IEN
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sys {
|
sys {
|
||||||
; ------- lowlevel system routines --------
|
; ------- lowlevel system routines --------
|
||||||
|
|
||||||
@ -494,11 +751,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)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -513,6 +769,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
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,6 +626,8 @@ asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
|||||||
|
|
||||||
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
||||||
; ---- set the color in A on the screen matrix at the given position
|
; ---- set the color in A on the screen matrix at the given position
|
||||||
|
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||||
|
; use the high nybble in A to set the Bg color!
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
txa
|
txa
|
||||||
@ -657,6 +659,8 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
|||||||
|
|
||||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||||
; ---- set char+color at the given position on the screen
|
; ---- set char+color at the given position on the screen
|
||||||
|
; note: color handling is the same as on the C64: it only sets the foreground color.
|
||||||
|
; use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors.
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
lda column
|
lda column
|
||||||
@ -685,8 +689,35 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||||
|
; ---- set char+color at the given position on the screen
|
||||||
|
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||||
|
; use the high nybble in A to set the Bg color!
|
||||||
|
%asm {{
|
||||||
|
phx
|
||||||
|
lda column
|
||||||
|
asl a
|
||||||
|
tax
|
||||||
|
ldy row
|
||||||
|
stz cx16.VERA_CTRL
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda char
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
inx
|
||||||
|
stz cx16.VERA_ADDR_H
|
||||||
|
stx cx16.VERA_ADDR_L
|
||||||
|
sty cx16.VERA_ADDR_M
|
||||||
|
lda colors
|
||||||
|
sta cx16.VERA_DATA0
|
||||||
|
plx
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
tax
|
tax
|
||||||
|
@ -336,6 +336,45 @@ _end rts
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ----- iterative file saver functions (uses io channel 14) -----
|
||||||
|
|
||||||
|
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte {
|
||||||
|
; -- open a file for iterative writing with f_write
|
||||||
|
f_close_w()
|
||||||
|
|
||||||
|
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||||
|
c64.SETLFS(14, drivenumber, 1)
|
||||||
|
void c64.OPEN() ; open 14,8,1,"filename"
|
||||||
|
if_cc {
|
||||||
|
void c64.CHKOUT(14) ; use #14 as input channel
|
||||||
|
return not c64.READST()
|
||||||
|
}
|
||||||
|
f_close_w()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte {
|
||||||
|
; -- write the given umber of bytes to the currently open file
|
||||||
|
if num_bytes!=0 {
|
||||||
|
void c64.CHKOUT(14) ; use #14 as input channel again
|
||||||
|
repeat num_bytes {
|
||||||
|
c64.CHROUT(@(bufferpointer))
|
||||||
|
bufferpointer++
|
||||||
|
}
|
||||||
|
return not c64.READST()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sub f_close_w() {
|
||||||
|
; -- end an iterative file writing session (close channels).
|
||||||
|
c64.CLRCHN()
|
||||||
|
c64.CLOSE(14)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; ---- other functions ----
|
||||||
|
|
||||||
sub status(ubyte drivenumber) -> uword {
|
sub status(ubyte drivenumber) -> uword {
|
||||||
; -- retrieve the disk drive's current status message
|
; -- retrieve the disk drive's current status message
|
||||||
uword messageptr = &filename
|
uword messageptr = &filename
|
||||||
@ -353,18 +392,22 @@ _end rts
|
|||||||
messageptr++
|
messageptr++
|
||||||
}
|
}
|
||||||
|
|
||||||
io_error:
|
|
||||||
@(messageptr) = 0
|
@(messageptr) = 0
|
||||||
|
done:
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
c64.CLOSE(15)
|
||||||
return filename
|
return filename
|
||||||
}
|
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
filename = "?disk error"
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
|
||||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
||||||
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 +424,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,68 +244,45 @@ randseed .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randbyte .proc
|
randbyte .proc
|
||||||
; -- 8-bit pseudo random number generator into A
|
; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||||
lda _seed
|
jmp randword
|
||||||
beq _eor
|
|
||||||
asl a
|
|
||||||
beq _done ; if the input was $80, skip the EOR
|
|
||||||
bcc _done
|
|
||||||
_eor eor #$1d ; xor with magic value see below for possible values
|
|
||||||
_done sta _seed
|
|
||||||
rts
|
|
||||||
|
|
||||||
_seed .byte $3a
|
|
||||||
|
|
||||||
; possible 'magic' eor bytes are:
|
|
||||||
; $1d, $2b, $2d, $4d, $5f, $63, $65, $69
|
|
||||||
; $71, $87, $8d, $a9, $c3, $cf, $e7, $f5
|
|
||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randword .proc
|
randword .proc
|
||||||
; -- 16 bit pseudo random number generator into AY
|
; -- 16 bit pseudo random number generator into AY
|
||||||
|
|
||||||
magic_eor = $3f1d
|
; rand64k ;Factors of 65535: 3 5 17 257
|
||||||
; possible magic eor words are:
|
lda sr1+1
|
||||||
; $3f1d, $3f81, $3fa5, $3fc5, $4075, $409d, $40cd, $4109
|
asl a
|
||||||
; $413f, $414b, $4153, $4159, $4193, $4199, $41af, $41bb
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
eor sr1+1
|
||||||
|
asl a
|
||||||
|
rol sr1 ;shift this left, "random" bit comes from low
|
||||||
|
rol sr1+1
|
||||||
|
; rand32k ;Factors of 32767: 7 31 151 are independent and can be combined
|
||||||
|
lda sr2+1
|
||||||
|
asl a
|
||||||
|
eor sr2+1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
ror sr2 ;shift this right, random bit comes from high - nicer when eor with sr1
|
||||||
|
rol sr2+1
|
||||||
|
lda sr1+1 ;can be left out
|
||||||
|
eor sr2+1 ;if you dont use
|
||||||
|
tay ;y as suggested
|
||||||
|
lda sr1 ;mix up lowbytes of SR1
|
||||||
|
eor sr2 ;and SR2 to combine both
|
||||||
|
rts
|
||||||
|
|
||||||
lda _seed
|
sr1 .word $a55a
|
||||||
beq _lowZero ; $0000 and $8000 are special values to test for
|
sr2 .word $7653
|
||||||
|
|
||||||
; Do a normal shift
|
|
||||||
asl _seed
|
|
||||||
lda _seed+1
|
|
||||||
rol a
|
|
||||||
bcc _noEor
|
|
||||||
|
|
||||||
_doEor ; high byte is in A
|
|
||||||
eor #>magic_eor
|
|
||||||
sta _seed+1
|
|
||||||
lda _seed
|
|
||||||
eor #<magic_eor
|
|
||||||
sta _seed
|
|
||||||
ldy _seed+1
|
|
||||||
rts
|
|
||||||
|
|
||||||
_lowZero lda _seed+1
|
|
||||||
beq _doEor ; High byte is also zero, so apply the EOR
|
|
||||||
; For speed, you could store 'magic' into 'seed' directly
|
|
||||||
; instead of running the EORs
|
|
||||||
|
|
||||||
; wasn't zero, check for $8000
|
|
||||||
asl a
|
|
||||||
beq _noEor ; if $00 is left after the shift, then it was $80
|
|
||||||
bcs _doEor ; else, do the EOR based on the carry bit as usual
|
|
||||||
|
|
||||||
_noEor sta _seed+1
|
|
||||||
tay
|
|
||||||
lda _seed
|
|
||||||
rts
|
|
||||||
|
|
||||||
_seed .word $2c9e
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
@ -797,6 +774,13 @@ stack_mul_word_320 .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
stack_mul_word_640 .proc
|
||||||
|
; stackW = (stackLo * 2 * 320) (stackHi doesn't matter)
|
||||||
|
asl P8ESTACK_LO+1,x
|
||||||
|
jmp stack_mul_word_320
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||||
mul_byte_3 .proc
|
mul_byte_3 .proc
|
||||||
; A = A + A*2
|
; A = A + A*2
|
||||||
@ -1287,6 +1271,13 @@ mul_word_320 .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
mul_word_640 .proc
|
||||||
|
; AY = (A * 2 * 320) (msb in Y doesn't matter)
|
||||||
|
asl a
|
||||||
|
jmp mul_word_320
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
; ----------- end optimized multiplications -----------
|
; ----------- end optimized multiplications -----------
|
||||||
|
|
||||||
|
|
||||||
@ -1537,3 +1528,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
|
||||||
|
@ -432,6 +432,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 +549,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
|
||||||
|
@ -178,7 +178,7 @@ _found sty P8ZP_SCRATCH_B1
|
|||||||
|
|
||||||
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
||||||
; Compares two strings for sorting.
|
; Compares two strings for sorting.
|
||||||
; Returns -1 (255), 0 or 1 depeding on wether string1 sorts before, equal or after string2.
|
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
|
||||||
; Note that you can also directly compare strings and string values with eachother using
|
; Note that you can also directly compare strings and string values with eachother using
|
||||||
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -190,8 +190,8 @@ _found sty P8ZP_SCRATCH_B1
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub lower(uword st @AY) {
|
asmsub lower(uword st @AY) -> ubyte @Y {
|
||||||
; Lowercases the petscii string in-place.
|
; Lowercases the petscii string in-place. Returns length of the string.
|
||||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||||
; but regular text doesn't usually contain those characters anyway.)
|
; but regular text doesn't usually contain those characters anyway.)
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -213,8 +213,8 @@ _done rts
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub upper(uword st @AY) {
|
asmsub upper(uword st @AY) -> ubyte @Y {
|
||||||
; Uppercases the petscii string in-place.
|
; Uppercases the petscii string in-place. Returns length of the string.
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
@ -1 +1 @@
|
|||||||
6.1
|
7.0-BETA
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
package prog8
|
package prog8
|
||||||
|
|
||||||
import kotlinx.cli.ArgParser
|
import kotlinx.cli.*
|
||||||
import kotlinx.cli.ArgType
|
|
||||||
import kotlinx.cli.default
|
|
||||||
import kotlinx.cli.multiple
|
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.CompilationResult
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.ICompilationTarget
|
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
|
import java.io.File
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardWatchEventKinds
|
import java.nio.file.StandardWatchEventKinds
|
||||||
@ -44,6 +41,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
||||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||||
|
val libDirs by cli.option(ArgType.String, fullName="libdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cli.parse(args)
|
cli.parse(args)
|
||||||
@ -58,6 +56,10 @@ private fun compileMain(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val libdirs = libDirs.toMutableList()
|
||||||
|
if(libdirs.firstOrNull()!=".")
|
||||||
|
libdirs.add(0, ".")
|
||||||
|
|
||||||
if(watchMode==true) {
|
if(watchMode==true) {
|
||||||
val watchservice = FileSystems.getDefault().newWatchService()
|
val watchservice = FileSystems.getDefault().newWatchService()
|
||||||
val allImportedFiles = mutableSetOf<Path>()
|
val allImportedFiles = mutableSetOf<Path>()
|
||||||
@ -67,7 +69,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val results = mutableListOf<CompilationResult>()
|
val results = mutableListOf<CompilationResult>()
|
||||||
for(filepathRaw in moduleFiles) {
|
for(filepathRaw in moduleFiles) {
|
||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||||
results.add(compilationResult)
|
results.add(compilationResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +106,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
} catch (x: ParsingFailedError) {
|
} catch (x: ParsingFailedError) {
|
||||||
@ -117,7 +119,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else {
|
else {
|
||||||
ICompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
|
compilationResult.compTarget.machine.launchEmulator(compilationResult.programName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,26 @@ 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: ErrorReporter, 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,30 +68,37 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
val decls = scope.statements.filterIsInstance<VarDecl>().filter { it.type == VarDeclType.VAR }
|
||||||
subroutineVariables.addAll(decls.map { it.name to it })
|
subroutineVariables.addAll(decls.map { it.name to it })
|
||||||
|
|
||||||
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 any remaining 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))
|
||||||
|
}
|
||||||
|
movements.add(IAstModification.InsertFirst(decl, sub))
|
||||||
}
|
}
|
||||||
val moveVardeclsUp = decls.map { IAstModification.InsertFirst(it, sub) }
|
return replacements + movements
|
||||||
return replaceVardecls + moveVardeclsUp
|
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -107,7 +115,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||||
val mods = mutableListOf<IAstModification>()
|
val mods = mutableListOf<IAstModification>()
|
||||||
val returnStmt = Return(null, subroutine.position)
|
val returnStmt = Return(null, subroutine.position)
|
||||||
@ -138,7 +146,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
// see if we can remove superfluous typecasts (outside of expressions)
|
// see if we can remove superfluous typecasts (outside of expressions)
|
||||||
// such as casting byte<->ubyte, word<->uword
|
// such as casting byte<->ubyte, word<->uword
|
||||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
||||||
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)
|
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||||
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
||||||
if(typecast.parent !is Expression) {
|
if(typecast.parent !is Expression) {
|
||||||
@ -154,16 +162,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
// 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 +192,55 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
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.operator=="==" || binExpr.operator=="!=") &&
|
||||||
|
(binExpr.left as? NumericLiteralValue)?.number==0 &&
|
||||||
|
(binExpr.right 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.UNDEFINED)
|
||||||
|
// 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 +260,95 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
}
|
}
|
||||||
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.UNDEFINED)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
|
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()
|
||||||
|
val dt = expr.indexer.indexExpr.inferType(program)
|
||||||
|
val register = if(dt.istype(DataType.UBYTE) || dt.istype(DataType.BYTE)) "r9L" else "r9"
|
||||||
|
// 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", register), 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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package prog8.compiler
|
|||||||
|
|
||||||
import prog8.ast.AstToSourceCode
|
import prog8.ast.AstToSourceCode
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
|
import prog8.ast.IMemSizer
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
@ -48,7 +49,8 @@ data class CompilationOptions(val output: OutputType,
|
|||||||
val zeropage: ZeropageType,
|
val zeropage: ZeropageType,
|
||||||
val zpReserved: List<IntRange>,
|
val zpReserved: List<IntRange>,
|
||||||
val floats: Boolean,
|
val floats: Boolean,
|
||||||
val noSysInit: Boolean) {
|
val noSysInit: Boolean,
|
||||||
|
val compTarget: ICompilationTarget) {
|
||||||
var slowCodegenWarnings = false
|
var slowCodegenWarnings = false
|
||||||
var optimize = false
|
var optimize = false
|
||||||
}
|
}
|
||||||
@ -59,6 +61,7 @@ class CompilerException(message: String?) : Exception(message)
|
|||||||
class CompilationResult(val success: Boolean,
|
class CompilationResult(val success: Boolean,
|
||||||
val programAst: Program,
|
val programAst: Program,
|
||||||
val programName: String,
|
val programName: String,
|
||||||
|
val compTarget: ICompilationTarget,
|
||||||
val importedFiles: List<Path>)
|
val importedFiles: List<Path>)
|
||||||
|
|
||||||
|
|
||||||
@ -67,33 +70,35 @@ fun compileProgram(filepath: Path,
|
|||||||
writeAssembly: Boolean,
|
writeAssembly: Boolean,
|
||||||
slowCodegenWarnings: Boolean,
|
slowCodegenWarnings: Boolean,
|
||||||
compilationTarget: String,
|
compilationTarget: String,
|
||||||
|
libdirs: List<String>,
|
||||||
outputDir: Path): CompilationResult {
|
outputDir: Path): CompilationResult {
|
||||||
var programName = ""
|
var programName = ""
|
||||||
lateinit var programAst: Program
|
lateinit var programAst: Program
|
||||||
lateinit var importedFiles: List<Path>
|
lateinit var importedFiles: List<Path>
|
||||||
val errors = ErrorReporter()
|
val errors = ErrorReporter()
|
||||||
|
|
||||||
when(compilationTarget) {
|
val compTarget =
|
||||||
C64Target.name -> ICompilationTarget.instance = C64Target
|
when(compilationTarget) {
|
||||||
Cx16Target.name -> ICompilationTarget.instance = Cx16Target
|
C64Target.name -> C64Target
|
||||||
else -> {
|
Cx16Target.name -> Cx16Target
|
||||||
System.err.println("invalid compilation target")
|
else -> {
|
||||||
exitProcess(1)
|
System.err.println("invalid compilation target")
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
|
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, libdirs)
|
||||||
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
||||||
compilationOptions.optimize = optimize
|
compilationOptions.optimize = optimize
|
||||||
programAst = ast
|
programAst = ast
|
||||||
importedFiles = imported
|
importedFiles = imported
|
||||||
processAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
|
processAst(programAst, errors, compilationOptions)
|
||||||
if (compilationOptions.optimize)
|
if (compilationOptions.optimize)
|
||||||
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions))
|
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compilationOptions)
|
||||||
postprocessAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
@ -103,7 +108,7 @@ fun compileProgram(filepath: Path,
|
|||||||
System.out.flush()
|
System.out.flush()
|
||||||
System.err.flush()
|
System.err.flush()
|
||||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||||
return CompilationResult(true, programAst, programName, importedFiles)
|
return CompilationResult(true, programAst, programName, compTarget, importedFiles)
|
||||||
|
|
||||||
} catch (px: ParsingFailedError) {
|
} catch (px: ParsingFailedError) {
|
||||||
System.err.print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
@ -127,8 +132,8 @@ fun compileProgram(filepath: Path,
|
|||||||
throw x
|
throw x
|
||||||
}
|
}
|
||||||
|
|
||||||
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions))
|
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
|
||||||
return CompilationResult(false, failedProgram, programName, emptyList())
|
return CompilationResult(false, failedProgram, programName, compTarget, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
|
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
|
||||||
@ -137,13 +142,13 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
override val names = functions.keys
|
override val names = functions.keys
|
||||||
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
|
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
|
||||||
|
|
||||||
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? {
|
override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? {
|
||||||
val func = BuiltinFunctions[name]
|
val func = BuiltinFunctions[name]
|
||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = func.constExpressionFunc
|
||||||
if(exprfunc!=null) {
|
if(exprfunc!=null) {
|
||||||
return try {
|
return try {
|
||||||
exprfunc(args, position, program)
|
exprfunc(args, position, program, memsizer)
|
||||||
} catch(x: NotConstArgumentException) {
|
} catch(x: NotConstArgumentException) {
|
||||||
// const-evaluating the builtin function call failed.
|
// const-evaluating the builtin function call failed.
|
||||||
null
|
null
|
||||||
@ -153,7 +158,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
|
||||||
}
|
}
|
||||||
@ -161,150 +166,169 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
builtinFunctionReturnType(name, args, program)
|
builtinFunctionReturnType(name, args, program)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICompilationTarget, libdirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
val compilationTargetName = ICompilationTarget.instance.name
|
val compilationTargetName = compTarget.name
|
||||||
println("Compiler target: $compilationTargetName. Parsing...")
|
println("Compiler target: $compilationTargetName. Parsing...")
|
||||||
val importer = ModuleImporter()
|
|
||||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf)
|
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf, compTarget)
|
||||||
bf.program = programAst
|
bf.program = programAst
|
||||||
importer.importModule(programAst, filepath, ICompilationTarget.instance, compilationTargetName)
|
|
||||||
errors.handle()
|
val importer = ModuleImporter(programAst, compTarget, compilationTargetName, libdirs)
|
||||||
|
importer.importModule(filepath)
|
||||||
|
errors.report()
|
||||||
|
|
||||||
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
|
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
|
||||||
|
val compilerOptions = determineCompilationOptions(programAst, compTarget)
|
||||||
val compilerOptions = determineCompilationOptions(programAst)
|
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||||
|
|
||||||
// depending on the machine and compiler options we may have to include some libraries
|
// depending on the machine and compiler options we may have to include some libraries
|
||||||
ICompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst, ICompilationTarget.instance, compilationTargetName)
|
for(lib in compTarget.machine.importLibs(compilerOptions, compilationTargetName))
|
||||||
|
importer.importLibraryModule(lib)
|
||||||
|
|
||||||
// always import prog8_lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule(programAst, "math", ICompilationTarget.instance, compilationTargetName)
|
importer.importLibraryModule("math")
|
||||||
importer.importLibraryModule(programAst, "prog8_lib", ICompilationTarget.instance, compilationTargetName)
|
importer.importLibraryModule("prog8_lib")
|
||||||
errors.handle()
|
errors.report()
|
||||||
return Triple(programAst, compilerOptions, importedFiles)
|
return Triple(programAst, compilerOptions, importedFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||||
val mainModule = program.modules.first()
|
val mainModule = program.mainModule
|
||||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.uppercase()
|
||||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.uppercase()
|
||||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.uppercase()
|
||||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
||||||
|
.flatMap { (it as Directive).args }.toSet()
|
||||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||||
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
||||||
var zpType: ZeropageType =
|
var zpType: ZeropageType =
|
||||||
if (zpoption == null)
|
if (zpoption == null)
|
||||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||||
else
|
else
|
||||||
try {
|
try {
|
||||||
ZeropageType.valueOf(zpoption)
|
ZeropageType.valueOf(zpoption)
|
||||||
} catch (x: IllegalArgumentException) {
|
} catch (x: IllegalArgumentException) {
|
||||||
ZeropageType.KERNALSAFE
|
ZeropageType.KERNALSAFE
|
||||||
// error will be printed by the astchecker
|
// error will be printed by the astchecker
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zpType==ZeropageType.FLOATSAFE && ICompilationTarget.instance.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
|
||||||
}
|
}
|
||||||
|
|
||||||
val zpReserved = mainModule.statements
|
val zpReserved = mainModule.statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||||
.map { (it as Directive).args }
|
.map { (it as Directive).args }
|
||||||
.map { it[0].int!!..it[1].int!! }
|
.map { it[0].int!!..it[1].int!! }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
|
if (outputType != null && !OutputType.values().any { it.name == outputType }) {
|
||||||
System.err.println("invalid output type $outputType")
|
System.err.println("invalid output type $outputType")
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
|
if (launcherType != null && !LauncherType.values().any { it.name == launcherType }) {
|
||||||
System.err.println("invalid launcher type $launcherType")
|
System.err.println("invalid launcher type $launcherType")
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationOptions(
|
return CompilationOptions(
|
||||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||||
zpType, zpReserved, floatsEnabled, noSysInit
|
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||||
|
compTarget
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
|
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
// perform initial syntax checks and processings
|
// perform initial syntax checks and processings
|
||||||
println("Processing for target ${compTarget.name}...")
|
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||||
programAst.checkIdentifiers(errors, compTarget)
|
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors, compilerOptions.compTarget)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.reorderStatements(errors)
|
programAst.reorderStatements(errors)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.variousCleanups()
|
programAst.variousCleanups(programAst, errors)
|
||||||
programAst.checkValid(compilerOptions, errors, compTarget)
|
errors.report()
|
||||||
errors.handle()
|
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||||
programAst.checkIdentifiers(errors, compTarget)
|
errors.report()
|
||||||
errors.handle()
|
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||||
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAst(programAst: Program, errors: ErrorReporter, functions: IBuiltinFunctions) {
|
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()
|
val optsDone2 = programAst.splitBinaryExpressions(compTarget)
|
||||||
val optsDone3 = programAst.optimizeStatements(errors, functions)
|
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget)
|
||||||
programAst.constantFold(errors) // 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.handle()
|
errors.report()
|
||||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
val remover = UnusedCodeRemover(programAst, errors)
|
val inliner = SubroutineInliner(programAst, errors, options)
|
||||||
remover.visit(programAst)
|
inliner.visit(programAst)
|
||||||
remover.applyModifications()
|
errors.report()
|
||||||
errors.handle()
|
if(errors.noErrors()) {
|
||||||
|
inliner.applyModifications()
|
||||||
|
inliner.fixCallsToInlinedSubroutines()
|
||||||
|
val remover2 = UnusedCodeRemover(programAst, errors, compTarget)
|
||||||
|
remover2.visit(programAst)
|
||||||
|
remover2.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
|
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.variousCleanups()
|
programAst.variousCleanups(programAst, errors)
|
||||||
programAst.checkValid(compilerOptions, errors, compTarget) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.report()
|
||||||
val callGraph = CallGraph(programAst)
|
val callGraph = CallGraph(programAst)
|
||||||
callGraph.checkRecursiveCalls(errors)
|
callGraph.checkRecursiveCalls(errors)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.verifyFunctionArgTypes()
|
programAst.verifyFunctionArgTypes()
|
||||||
programAst.moveMainAndStartToFirst()
|
programAst.moveMainAndStartToFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program,
|
||||||
|
errors: IErrorReporter,
|
||||||
|
outputDir: Path,
|
||||||
compilerOptions: CompilationOptions): String {
|
compilerOptions: CompilationOptions): String {
|
||||||
// asm generation directly from the Ast,
|
// asm generation directly from the Ast
|
||||||
programAst.processAstBeforeAsmGeneration(errors, ICompilationTarget.instance)
|
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||||
errors.handle()
|
errors.report()
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
ICompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
||||||
val assembly = asmGeneratorFor(ICompilationTarget.instance,
|
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
||||||
programAst,
|
programAst,
|
||||||
errors,
|
errors,
|
||||||
ICompilationTarget.instance.machine.zeropage,
|
compilerOptions.compTarget.machine.zeropage,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
outputDir).compileToAssembly()
|
outputDir).compileToAssembly()
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
errors.handle()
|
errors.report()
|
||||||
return assembly.name
|
return assembly.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,16 @@ package prog8.compiler
|
|||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
|
|
||||||
class ErrorReporter {
|
|
||||||
|
interface IErrorReporter {
|
||||||
|
fun err(msg: String, position: Position)
|
||||||
|
fun warn(msg: String, position: Position)
|
||||||
|
fun noErrors(): Boolean
|
||||||
|
fun report()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ErrorReporter: IErrorReporter {
|
||||||
private enum class MessageSeverity {
|
private enum class MessageSeverity {
|
||||||
WARNING,
|
WARNING,
|
||||||
ERROR
|
ERROR
|
||||||
@ -13,10 +22,14 @@ class ErrorReporter {
|
|||||||
private val messages = mutableListOf<CompilerMessage>()
|
private val messages = mutableListOf<CompilerMessage>()
|
||||||
private val alreadyReportedMessages = mutableSetOf<String>()
|
private val alreadyReportedMessages = mutableSetOf<String>()
|
||||||
|
|
||||||
fun err(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
override fun err(msg: String, position: Position) {
|
||||||
fun warn(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
||||||
|
}
|
||||||
|
override fun warn(msg: String, position: Position) {
|
||||||
|
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
||||||
|
}
|
||||||
|
|
||||||
fun handle() {
|
override fun report() {
|
||||||
var numErrors = 0
|
var numErrors = 0
|
||||||
var numWarnings = 0
|
var numWarnings = 0
|
||||||
messages.forEach {
|
messages.forEach {
|
||||||
@ -40,5 +53,5 @@ class ErrorReporter {
|
|||||||
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isEmpty() = messages.isEmpty()
|
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
|
|
||||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
|
||||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int {
|
||||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
||||||
|
|
||||||
if(options.zeropage==ZeropageType.DONTUSE)
|
if(options.zeropage==ZeropageType.DONTUSE)
|
||||||
|
@ -8,17 +8,19 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
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.ErrorReporter
|
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
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
internal class AstChecker(private val program: Program,
|
internal class AstChecker(private val program: Program,
|
||||||
private val compilerOptions: CompilationOptions,
|
private val compilerOptions: CompilationOptions,
|
||||||
private val errors: ErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val compTarget: ICompilationTarget
|
private val compTarget: ICompilationTarget
|
||||||
) : IAstVisitor {
|
) : IAstVisitor {
|
||||||
|
|
||||||
@ -41,17 +43,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// there can be an optional single 'irq' block with a 'irq' subroutine in it,
|
if(compilerOptions.floats) {
|
||||||
// which will be used as the 60hz irq routine in the vm if it's present
|
if (compilerOptions.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
val irqBlocks = program.modules.flatMap { it.statements }.filter { it is Block && it.name=="irq" }.map { it as Block }
|
errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.mainModule.position)
|
||||||
if(irqBlocks.size>1)
|
|
||||||
errors.err("more than one 'irq' block", irqBlocks[0].position)
|
|
||||||
for(irqBlock in irqBlocks) {
|
|
||||||
val irqSub = irqBlock.subScope("irq") as? Subroutine
|
|
||||||
if (irqSub != null) {
|
|
||||||
if (irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
|
|
||||||
errors.err("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visit(program)
|
super.visit(program)
|
||||||
@ -83,9 +77,9 @@ 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.UNDEFINED))
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,12 +87,24 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(ifStatement: IfStatement) {
|
override fun visit(ifStatement: IfStatement) {
|
||||||
if(ifStatement.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
if(!ifStatement.condition.inferType(program).isInteger())
|
||||||
errors.err("condition value should be an integer type", ifStatement.condition.position)
|
errors.err("condition value should be an integer type", ifStatement.condition.position)
|
||||||
super.visit(ifStatement)
|
super.visit(ifStatement)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(forLoop: ForLoop) {
|
override fun visit(forLoop: ForLoop) {
|
||||||
|
|
||||||
|
fun checkUnsignedLoopDownto0(range: RangeExpr?) {
|
||||||
|
if(range==null)
|
||||||
|
return
|
||||||
|
val step = range.step.constValue(program)?.number?.toDouble() ?: 1.0
|
||||||
|
if(step < -1.0) {
|
||||||
|
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||||
|
if(limit==0.0 && range.from.constValue(program)==null)
|
||||||
|
errors.err("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping", forLoop.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
||||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||||
errors.err("can only loop over an iterable type", forLoop.position)
|
errors.err("can only loop over an iterable type", forLoop.position)
|
||||||
@ -111,11 +117,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||||
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
||||||
|
|
||||||
|
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr)
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
||||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||||
|
|
||||||
|
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr)
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
||||||
@ -131,7 +141,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) {
|
||||||
@ -153,6 +163,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(forLoop)
|
super.visit(forLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visit(jump: Jump) {
|
override fun visit(jump: Jump) {
|
||||||
val ident = jump.identifier
|
val ident = jump.identifier
|
||||||
if(ident!=null) {
|
if(ident!=null) {
|
||||||
@ -187,7 +198,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
errors.err("statement occurs in a block, where it will never be executed. Use it in a subroutine instead.", statement.position)
|
errors.err("non-declarative statement occurs in block scope, where it will never be executed. Move it to a subroutine instead.", statement.position)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,6 +214,28 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(label)
|
super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hasReturnOrJump(scope: INameScope): Boolean {
|
||||||
|
class Searcher: IAstVisitor
|
||||||
|
{
|
||||||
|
var count=0
|
||||||
|
|
||||||
|
override fun visit(returnStmt: Return) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
override fun visit(jump: Jump) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val s=Searcher()
|
||||||
|
for(stmt in scope.statements) {
|
||||||
|
stmt.accept(s)
|
||||||
|
if(s.count>0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return s.count > 0
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
fun err(msg: String) = errors.err(msg, subroutine.position)
|
fun err(msg: String) = errors.err(msg, subroutine.position)
|
||||||
|
|
||||||
@ -216,13 +249,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
|
||||||
@ -231,13 +257,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(!hasReturnOrJump(subroutine)) {
|
||||||
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)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,10 +377,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(statusFlagsNoCarry.isNotEmpty())
|
if(statusFlagsNoCarry.isNotEmpty())
|
||||||
err("can only use Carry as status flag parameter")
|
err("can only use Carry as status flag parameter")
|
||||||
|
|
||||||
val carryParameter = subroutine.asmParameterRegisters.singleOrNull { it.statusflag==Statusflag.Pc }
|
|
||||||
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
|
|
||||||
err("carry parameter has to come last")
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
// Instead, their reference (address) should be passed (as an UWORD).
|
// Instead, their reference (address) should be passed (as an UWORD).
|
||||||
@ -365,13 +387,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(untilLoop: UntilLoop) {
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
if(untilLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
if(!untilLoop.condition.inferType(program).isInteger())
|
||||||
errors.err("condition value should be an integer type", untilLoop.condition.position)
|
errors.err("condition value should be an integer type", untilLoop.condition.position)
|
||||||
super.visit(untilLoop)
|
super.visit(untilLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whileLoop: WhileLoop) {
|
override fun visit(whileLoop: WhileLoop) {
|
||||||
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
if(!whileLoop.condition.inferType(program).isInteger())
|
||||||
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
||||||
super.visit(whileLoop)
|
super.visit(whileLoop)
|
||||||
}
|
}
|
||||||
@ -391,43 +413,19 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetIdent = assignment.target.identifier
|
|
||||||
if(targetIdent!=null) {
|
|
||||||
val targetVar = targetIdent.targetVarDecl(program)
|
|
||||||
if(targetVar?.struct != null) {
|
|
||||||
val sourceStructLv = assignment.value as? ArrayLiteralValue
|
|
||||||
if (sourceStructLv != null) {
|
|
||||||
if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
|
|
||||||
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
|
||||||
} else {
|
|
||||||
val sourceIdent = assignment.value as? IdentifierReference
|
|
||||||
if (sourceIdent != null) {
|
|
||||||
val sourceVar = sourceIdent.targetVarDecl(program)
|
|
||||||
if (sourceVar?.struct != null) {
|
|
||||||
if (sourceVar.struct !== targetVar.struct)
|
|
||||||
errors.err("assignment of different struct types", assignment.position)
|
|
||||||
} else if(sourceVar?.isArray==true) {
|
|
||||||
if((sourceVar.value as ArrayLiteralValue).value.size != targetVar.struct?.numberOfElements)
|
|
||||||
errors.err("number of elements doesn't match struct definition", sourceVar.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val targetDt = assignment.target.inferType(program)
|
val targetDt = assignment.target.inferType(program)
|
||||||
val valueDt = assignment.value.inferType(program)
|
val valueDt = assignment.value.inferType(program)
|
||||||
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
||||||
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
if(targetDt.isIterable())
|
||||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||||
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
|
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
|
||||||
errors.err("value's type doesn't match target", assignment.value.position)
|
errors.err("type of value doesn't match target", assignment.value.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(assignment.value is TypecastExpression) {
|
if(assignment.value is TypecastExpression) {
|
||||||
@ -497,12 +495,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
override fun visit(addressOf: AddressOf) {
|
override fun visit(addressOf: AddressOf) {
|
||||||
val variable=addressOf.identifier.targetVarDecl(program)
|
val variable=addressOf.identifier.targetVarDecl(program)
|
||||||
if(variable!=null
|
if(variable!=null && variable.type==VarDeclType.CONST)
|
||||||
&& variable.datatype !in ArrayDatatypes
|
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||||
&& variable.type!=VarDeclType.MEMORY
|
|
||||||
&& variable.struct == null
|
|
||||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
|
||||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
|
||||||
super.visit(addressOf)
|
super.visit(addressOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,7 +504,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)
|
||||||
@ -544,17 +538,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
when(decl.type) {
|
when(decl.type) {
|
||||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
|
||||||
if(decl.struct==null)
|
|
||||||
throw FatalAstException("struct vardecl should be linked to its struct $decl")
|
|
||||||
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
|
|
||||||
err("struct can not be in zeropage")
|
|
||||||
}
|
|
||||||
if(decl.struct!=null) {
|
|
||||||
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
|
|
||||||
err("struct can not be in zeropage")
|
|
||||||
}
|
|
||||||
|
|
||||||
when(decl.value) {
|
when(decl.value) {
|
||||||
null -> {
|
null -> {
|
||||||
// a vardecl without an initial value, don't bother with it
|
// a vardecl without an initial value, don't bother with it
|
||||||
@ -564,30 +547,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||||
val struct = decl.struct!!
|
checkValueTypeAndRangeArray(decl.datatype, arraySpec, decl.value as ArrayLiteralValue)
|
||||||
val structLv = decl.value as ArrayLiteralValue
|
|
||||||
if(struct.numberOfElements != structLv.value.size) {
|
|
||||||
errors.err("struct value has incorrect number of elements", structLv.position)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for(value in structLv.value.zip(struct.statements)) {
|
|
||||||
val memberdecl = value.second as VarDecl
|
|
||||||
val constValue = value.first.constValue(program)
|
|
||||||
if(constValue==null) {
|
|
||||||
errors.err("struct literal value for field '${memberdecl.name}' should consist of compile-time constants", value.first.position)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val memberDt = memberdecl.datatype
|
|
||||||
if(!checkValueTypeAndRange(memberDt, constValue)) {
|
|
||||||
errors.err("struct member value's type is not compatible with member field '${memberdecl.name}'", value.first.position)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
|
||||||
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||||
@ -602,8 +563,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
if(decl.arraysize!=null) {
|
val arraysize = decl.arraysize
|
||||||
val arraySize = decl.arraysize!!.constIndex() ?: 1
|
if(arraysize!=null) {
|
||||||
|
val arraySize = arraysize.constIndex() ?: 1
|
||||||
when(decl.datatype) {
|
when(decl.datatype) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||||
if(arraySize > 256)
|
if(arraySize > 256)
|
||||||
@ -617,10 +579,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val numvalue = decl.value as? NumericLiteralValue
|
||||||
if(decl.value is NumericLiteralValue) {
|
if(numvalue!=null) {
|
||||||
val value = decl.value as NumericLiteralValue
|
if (numvalue.type !in IntegerDatatypes || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
|
||||||
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
|
||||||
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -631,23 +592,16 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
if (!declValue.inferType(program).istype(decl.datatype)) {
|
||||||
val valueIdt = declValue.inferType(program)
|
|
||||||
if(!valueIdt.isKnown)
|
|
||||||
throw AstException("unknown dt")
|
|
||||||
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
|
|
||||||
if(valueDt !in ArrayDatatypes)
|
|
||||||
err("initialisation of struct should be with array value", declValue.position)
|
|
||||||
} else if (!declValue.inferType(program).istype(decl.datatype)) {
|
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// array length limits and constant lenghts
|
// array length limits and constant lenghts
|
||||||
if(decl.isArray) {
|
if(decl.isArray) {
|
||||||
val length = decl.arraysize!!.constIndex()
|
val length = decl.arraysize?.constIndex()
|
||||||
if(length==null)
|
if(length==null)
|
||||||
err("array length must be a constant")
|
err("array length must be known at compile-time")
|
||||||
else {
|
else {
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
@ -756,7 +710,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("this directive may only occur in a block or at module level")
|
err("this directive may only occur in a block or at module level")
|
||||||
if(directive.args.isEmpty())
|
if(directive.args.isEmpty())
|
||||||
err("missing option directive argument(s)")
|
err("missing option directive argument(s)")
|
||||||
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it })
|
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
|
||||||
err("invalid option directive argument(s)")
|
err("invalid option directive argument(s)")
|
||||||
}
|
}
|
||||||
"%target" -> {
|
"%target" -> {
|
||||||
@ -782,11 +736,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
override fun visit(array: ArrayLiteralValue) {
|
override fun visit(array: ArrayLiteralValue) {
|
||||||
if(array.type.isKnown) {
|
if(array.type.isKnown) {
|
||||||
if (!compilerOptions.floats && array.type.typeOrElse(DataType.STRUCT) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
if (!compilerOptions.floats && array.type.typeOrElse(DataType.UNDEFINED) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||||
errors.err("floating point used, but that is not enabled via options", array.position)
|
errors.err("floating point used, but that is not enabled via options", array.position)
|
||||||
}
|
}
|
||||||
val arrayspec = ArrayIndex.forArray(array)
|
val arrayspec = ArrayIndex.forArray(array)
|
||||||
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.UNDEFINED), arrayspec, array)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||||
@ -818,7 +772,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
return // any error should be reported elsewhere
|
return // any error should be reported elsewhere
|
||||||
|
|
||||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
val dt = idt.typeOrElse(DataType.UNDEFINED)
|
||||||
if(expr.operator=="-") {
|
if(expr.operator=="-") {
|
||||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||||
errors.err("can only take negative of a signed number type", expr.position)
|
errors.err("can only take negative of a signed number type", expr.position)
|
||||||
@ -843,8 +797,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
return // hopefully this error will be detected elsewhere
|
return // hopefully this error will be detected elsewhere
|
||||||
|
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
val rightDt = rightIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
|
|
||||||
when(expr.operator){
|
when(expr.operator){
|
||||||
"/", "%" -> {
|
"/", "%" -> {
|
||||||
@ -970,6 +924,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,7 +999,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||||
else if(args[0] isSameAs args[1])
|
else if(args[0] isSameAs args[1])
|
||||||
errors.err("swap should have 2 different args", position)
|
errors.err("swap should have 2 different args", position)
|
||||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
else if(!dt1.isNumeric())
|
||||||
errors.err("swap requires args of numerical type", position)
|
errors.err("swap requires args of numerical type", position)
|
||||||
}
|
}
|
||||||
else if(target.name=="all" || target.name=="any") {
|
else if(target.name=="all" || target.name=="any") {
|
||||||
@ -1062,7 +1030,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
ident = fcall.args[0] as? IdentifierReference
|
ident = fcall.args[0] as? IdentifierReference
|
||||||
}
|
}
|
||||||
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
||||||
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].toUpperCase())
|
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].uppercase())
|
||||||
val same = params.filter { it.value.registerOrPair==reg }
|
val same = params.filter { it.value.registerOrPair==reg }
|
||||||
for(s in same) {
|
for(s in same) {
|
||||||
if(s.index!=arg.index) {
|
if(s.index!=arg.index) {
|
||||||
@ -1128,29 +1096,28 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(whenStatement: WhenStatement) {
|
override fun visit(whenStatement: WhenStatement) {
|
||||||
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
if(!whenStatement.condition.inferType(program).isInteger())
|
||||||
if(conditionType !in IntegerDatatypes)
|
|
||||||
errors.err("when condition must be an integer value", whenStatement.position)
|
errors.err("when condition must be an integer value", whenStatement.position)
|
||||||
val choiceValues = whenStatement.choiceValues(program)
|
val tally = mutableSetOf<Int>()
|
||||||
val occurringValues = choiceValues.map {it.first}
|
for((choices, choiceNode) in whenStatement.choiceValues(program)) {
|
||||||
val tally = choiceValues.associate { it.second to occurringValues.count { ov->it.first==ov} }
|
if(choices!=null) {
|
||||||
tally.filter { it.value>1 }.forEach {
|
for (c in choices) {
|
||||||
errors.err("choice value occurs multiple times", it.key.position)
|
if(c in tally)
|
||||||
|
errors.err("choice value already occurs earlier", choiceNode.position)
|
||||||
|
else
|
||||||
|
tally.add(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(whenStatement.choices.isEmpty())
|
if(whenStatement.choices.isEmpty())
|
||||||
errors.err("empty when statement", whenStatement.position)
|
errors.err("empty when statement", whenStatement.position)
|
||||||
|
|
||||||
@ -1168,7 +1135,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
when {
|
when {
|
||||||
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
|
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
|
||||||
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
|
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
|
||||||
constvalue.type != conditionType.typeOrElse(DataType.STRUCT) -> errors.err("choice value datatype differs from condition value", whenChoice.position)
|
constvalue.type != conditionType.typeOrElse(DataType.UNDEFINED) -> errors.err("choice value datatype differs from condition value", whenChoice.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1178,24 +1145,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(whenChoice)
|
super.visit(whenChoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl) {
|
|
||||||
// a struct can only contain 1 or more vardecls and can not be nested
|
|
||||||
if(structDecl.statements.isEmpty())
|
|
||||||
errors.err("struct must contain at least one member", structDecl.position)
|
|
||||||
|
|
||||||
for(member in structDecl.statements){
|
|
||||||
val decl = member as? VarDecl
|
|
||||||
if(decl==null)
|
|
||||||
errors.err("struct can only contain variable declarations", structDecl.position)
|
|
||||||
else {
|
|
||||||
if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
|
||||||
errors.err("struct can not contain zeropage members", decl.position)
|
|
||||||
if(decl.datatype !in NumericDatatypes)
|
|
||||||
errors.err("structs can only contain numerical types", decl.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||||
val targetStatement = target.targetStatement(program)
|
val targetStatement = target.targetStatement(program)
|
||||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||||
@ -1219,8 +1168,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
else false
|
else false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
|
private fun checkValueTypeAndRangeArray(targetDt: DataType, arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
|
||||||
arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
|
|
||||||
fun err(msg: String) : Boolean {
|
fun err(msg: String) : Boolean {
|
||||||
errors.err(msg, value.position)
|
errors.err(msg, value.position)
|
||||||
return false
|
return false
|
||||||
@ -1293,22 +1241,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
return err("invalid float array initialization value ${value.type}, expected $targetDt")
|
return err("invalid float array initialization value ${value.type}, expected $targetDt")
|
||||||
}
|
}
|
||||||
DataType.STRUCT -> {
|
|
||||||
if(value.type.typeOrElse(DataType.STRUCT) in ArrayDatatypes) {
|
|
||||||
if(value.value.size != struct!!.numberOfElements)
|
|
||||||
return err("number of values is not the same as the number of members in the struct")
|
|
||||||
for(elt in value.value.zip(struct.statements)) {
|
|
||||||
val vardecl = elt.second as VarDecl
|
|
||||||
val valuetype = elt.first.inferType(program)
|
|
||||||
if (!valuetype.isKnown || valuetype isNotAssignableTo vardecl.datatype) {
|
|
||||||
errors.err("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1361,7 +1293,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val array = value.value.map {
|
val array = value.value.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is NumericLiteralValue -> it.number.toInt()
|
is NumericLiteralValue -> it.number.toInt()
|
||||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
is AddressOf -> it.identifier.hashCode() and 0xffff
|
||||||
is TypecastExpression -> {
|
is TypecastExpression -> {
|
||||||
val constVal = it.expression.constValue(program)
|
val constVal = it.expression.constValue(program)
|
||||||
val cast = constVal?.cast(it.type)
|
val cast = constVal?.cast(it.type)
|
||||||
@ -1411,16 +1343,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
||||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype== DataType.STR
|
||||||
DataType.STRUCT -> {
|
else -> {
|
||||||
if(sourceDatatype==DataType.STRUCT) {
|
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||||
val structLv = sourceValue as ArrayLiteralValue
|
|
||||||
val numValues = structLv.value.size
|
|
||||||
val targetstruct = target.identifier!!.targetVarDecl(program)!!.struct!!
|
|
||||||
return targetstruct.numberOfElements == numValues
|
|
||||||
}
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
else -> errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
@ -1430,10 +1356,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||||
}
|
}
|
||||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||||
errors.err("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||||
else {
|
else {
|
||||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
||||||
errors.err("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position)
|
errors.err(
|
||||||
|
"cannot assign ${sourceDatatype.name.lowercase()} to ${
|
||||||
|
targetDatatype.name.lowercase(
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,32 +1,39 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter, compTarget: ICompilationTarget) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
val checker = AstChecker(this, compilerOptions, errors, compTarget)
|
val checker = AstChecker(this, compilerOptions, errors, compTarget)
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter, 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: ErrorReporter) {
|
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||||
val reorder = StatementReorderer(this, errors)
|
val reorder = StatementReorderer(this, errors)
|
||||||
reorder.visit(this)
|
reorder.visit(this)
|
||||||
reorder.applyModifications()
|
if(errors.noErrors()) {
|
||||||
|
reorder.applyModifications()
|
||||||
|
reorder.visit(this)
|
||||||
|
if(errors.noErrors())
|
||||||
|
reorder.applyModifications()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
internal fun Program.addTypecasts(errors: IErrorReporter) {
|
||||||
val caster = TypecastsAdder(this, errors)
|
val caster = TypecastsAdder(this, errors)
|
||||||
caster.visit(this)
|
caster.visit(this)
|
||||||
caster.applyModifications()
|
caster.applyModifications()
|
||||||
@ -37,12 +44,12 @@ internal fun Program.verifyFunctionArgTypes() {
|
|||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: ErrorReporter, compTarget: ICompilationTarget) {
|
internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
|
|
||||||
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: ErrorReporter, compTarget: ICompil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
process.applyModifications()
|
if(errors.noErrors())
|
||||||
|
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,29 +78,27 @@ 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))
|
throw FatalAstException("module wrong")
|
||||||
throw FatalAstException("module wrong")
|
modules.add(0, mod)
|
||||||
modules.add(0, mod)
|
mod.remove(block)
|
||||||
mod.remove(block)
|
var afterDirective = mod.statements.indexOfFirst { it !is Directive }
|
||||||
var afterDirective = mod.statements.indexOfFirst { it !is Directive }
|
if(afterDirective<0)
|
||||||
if(afterDirective<0)
|
mod.statements.add(block)
|
||||||
mod.statements.add(block)
|
else
|
||||||
else
|
mod.statements.add(afterDirective, block)
|
||||||
mod.statements.add(afterDirective, block)
|
block.remove(start)
|
||||||
block.remove(start)
|
afterDirective = block.statements.indexOfFirst { it !is Directive }
|
||||||
afterDirective = block.statements.indexOfFirst { it !is Directive }
|
if(afterDirective<0)
|
||||||
if(afterDirective<0)
|
block.statements.add(start)
|
||||||
block.statements.add(start)
|
else
|
||||||
else
|
block.statements.add(afterDirective, start)
|
||||||
block.statements.add(afterDirective, start)
|
|
||||||
|
|
||||||
// overwrite the directives in the module containing the entrypoint
|
// overwrite the directives in the module containing the entrypoint
|
||||||
for(directive in directives) {
|
for(directive in directives) {
|
||||||
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)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,15 @@ package prog8.compiler.astprocessing
|
|||||||
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
import prog8.ast.base.NumericDatatypes
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
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.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
internal class AstIdentifiersChecker(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
@ -66,29 +62,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
if(decl.name in compTarget.machine.opcodeNames)
|
if(decl.name in compTarget.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
if(decl.datatype==DataType.STRUCT) {
|
|
||||||
if (decl.structHasBeenFlattened)
|
|
||||||
return super.visit(decl) // don't do this multiple times
|
|
||||||
|
|
||||||
if (decl.struct == null) {
|
|
||||||
errors.err("undefined struct type", decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes })
|
|
||||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
|
||||||
|
|
||||||
if (decl.value is NumericLiteralValue) {
|
|
||||||
errors.err("you cannot initialize a struct using a single value", decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decl.value != null && decl.value !is ArrayLiteralValue) {
|
|
||||||
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
@ -169,14 +142,4 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
|
|
||||||
super.visit(string)
|
super.visit(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl) {
|
|
||||||
for(member in structDecl.statements){
|
|
||||||
val decl = member as? VarDecl
|
|
||||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
|
||||||
errors.err("structs can only contain numerical types", decl.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.visit(structDecl)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,36 +3,19 @@ package prog8.compiler.astprocessing
|
|||||||
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.expressions.ArrayIndexedExpression
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
|
import prog8.ast.expressions.DirectMemoryRead
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.ast.statements.AnonymousScope
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.ParameterVarDecl
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
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> {
|
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
|
||||||
// and include the original decl as well.
|
|
||||||
if(decl.datatype==DataType.STRUCT && !decl.structHasBeenFlattened) {
|
|
||||||
val decls = decl.flattenStructMembers()
|
|
||||||
decls.add(decl)
|
|
||||||
val result = AnonymousScope(decls, decl.position)
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
|
||||||
decl, result, parent
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
// For non-kernel subroutines and non-asm parameters:
|
// For non-kernal subroutines and non-asm parameters:
|
||||||
// inject subroutine params as local variables (if they're not there yet).
|
// inject subroutine params as local variables (if they're not there yet).
|
||||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
@ -83,6 +66,10 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
||||||
|
}
|
||||||
|
|
||||||
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
||||||
val rightStrval = expr.right as? StringLiteralValue
|
val rightStrval = expr.right as? StringLiteralValue
|
||||||
val leftStrval = expr.left as? StringLiteralValue
|
val leftStrval = expr.left as? StringLiteralValue
|
||||||
@ -109,3 +96,25 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal fun replacePointerVarIndexWithMemread(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||||
|
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||||
|
// rewrite pointervar[index] into @(pointervar+index)
|
||||||
|
val indexer = arrayIndexedExpression.indexer
|
||||||
|
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||||
|
return if(parent is AssignTarget) {
|
||||||
|
// 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 newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||||
|
} else {
|
||||||
|
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
||||||
|
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
@ -13,17 +13,13 @@ 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) {
|
||||||
// replace the literal string by a identifier reference to a new local vardecl
|
// replace the literal string by a identifier reference to the interned string
|
||||||
val vardecl = VarDecl.createAuto(string)
|
val scopedName = program.internString(string)
|
||||||
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
val identifier = IdentifierReference(scopedName, string.position)
|
||||||
return listOf(
|
return listOf(IAstModification.ReplaceNode(string, identifier, parent))
|
||||||
IAstModification.ReplaceNode(string, identifier, parent),
|
|
||||||
IAstModification.InsertFirst(vardecl, string.definingScope())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -42,7 +38,7 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
|||||||
val arrayDt = array.guessDatatype(program)
|
val arrayDt = array.guessDatatype(program)
|
||||||
if(arrayDt.isKnown) {
|
if(arrayDt.isKnown) {
|
||||||
// this array literal is part of an expression, turn it into an identifier reference
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.UNDEFINED))
|
||||||
if(litval2!=null) {
|
if(litval2!=null) {
|
||||||
val vardecl2 = VarDecl.createAuto(litval2)
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.base.*
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.expressions.*
|
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.compiler.ErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
internal class StatementReorderer(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||||
// Reorders the statements in a way the compiler needs.
|
// Reorders the statements in a way the compiler needs.
|
||||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||||
// - library blocks are put last.
|
// - library blocks are put last.
|
||||||
@ -18,12 +22,10 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
// - in every block and module, most directives and vardecls are moved to the top. (not in subroutines!)
|
// - in every block and module, most directives and vardecls are moved to the top. (not in subroutines!)
|
||||||
// - the 'start' subroutine is moved to the top.
|
// - the 'start' subroutine is moved to the top.
|
||||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
|
||||||
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
||||||
// - sorts the choices in when statement.
|
// - sorts the choices in when statement.
|
||||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
override fun after(module: Module, parent: Node): Iterable<IAstModification> {
|
||||||
@ -84,44 +86,17 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
||||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
|
||||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
|
||||||
// rewrite pointervar[index] into @(pointervar+index)
|
|
||||||
val indexer = arrayIndexedExpression.indexer
|
|
||||||
val index = (indexer.indexNum ?: indexer.indexVar)!!
|
|
||||||
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
|
|
||||||
return if(parent is AssignTarget) {
|
|
||||||
// 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 newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
|
||||||
} else {
|
|
||||||
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
|
||||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
arrayIndexedExpression.indexer.indexNum = expr2
|
|
||||||
arrayIndexedExpression.indexer.origExpression = null
|
|
||||||
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> {
|
||||||
|
|
||||||
|
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||||
|
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||||
|
// but the current assembly code generator for IF statements now also depends on it so we do it here regardless of optimization.)
|
||||||
|
if (expr.left.constValue(program) != null && expr.operator in associativeOperators && expr.right.constValue(program) == null)
|
||||||
|
return listOf(IAstModification.SwapOperands(expr))
|
||||||
|
|
||||||
// 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,
|
||||||
// try to make the bit shifting 'wide enough' to fall into the variable's type.
|
// try to make the bit shifting 'wide enough' to fall into the variable's type.
|
||||||
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
|
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
|
||||||
@ -131,7 +106,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
is Assignment -> {
|
is Assignment -> {
|
||||||
val targetDt = parent.target.inferType(program)
|
val targetDt = parent.target.inferType(program)
|
||||||
if(leftDt != targetDt) {
|
if(leftDt != targetDt) {
|
||||||
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
|
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.UNDEFINED), true, parent.position)
|
||||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,41 +142,41 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
else -> return noModifications
|
else -> return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(expr.operator in logicalOperators) {
|
||||||
|
// make sure that logical expressions like "var and other-logical-expression
|
||||||
|
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
|
||||||
|
// generating the wrong results later
|
||||||
|
|
||||||
|
fun wrapped(expr: Expression): Expression =
|
||||||
|
BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0, expr.position), expr.position)
|
||||||
|
|
||||||
|
fun isLogicalExpr(expr: Expression?): Boolean {
|
||||||
|
if(expr is BinaryExpression && expr.operator in (logicalOperators + comparisonOperators))
|
||||||
|
return true
|
||||||
|
if(expr is PrefixExpression && expr.operator in logicalOperators)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(isLogicalExpr(expr.left)) {
|
||||||
|
if(isLogicalExpr(expr.right))
|
||||||
|
noModifications
|
||||||
|
else
|
||||||
|
listOf(IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr))
|
||||||
|
} else {
|
||||||
|
if(isLogicalExpr(expr.right))
|
||||||
|
listOf(IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr))
|
||||||
|
else {
|
||||||
|
listOf(
|
||||||
|
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
|
||||||
|
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
@ -211,57 +186,18 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
val declValue = decl.value
|
|
||||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
val declConstValue = declValue.constValue(program)
|
|
||||||
if(declConstValue==null) {
|
|
||||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
|
||||||
// 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.allowInitializeWithZero = false
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
val assign = Assignment(target, declValue, decl.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
|
||||||
IAstModification.InsertFirst(decl, decl.definingScope())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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.isArray() && valueType.isArray() ) {
|
||||||
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 element", assignment.position)
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
return copyArrayValue(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
|
||||||
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
|
||||||
} else {
|
|
||||||
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(assignments.isNotEmpty()) {
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,124 +250,40 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
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())
|
||||||
|
errors.err("element count mismatch", assign.position)
|
||||||
|
if (sourceVar.datatype != targetVar.datatype)
|
||||||
|
errors.err("element type mismatch", assign.position)
|
||||||
}
|
}
|
||||||
val alv = sourceVar.value as? ArrayLiteralValue
|
|
||||||
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
if(!errors.noErrors())
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> {
|
|
||||||
if(targetVar.arraysize==null) {
|
|
||||||
errors.err("array has no defined size", identifier.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
|
||||||
errors.err("element count mismatch", position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO use a pointer loop instead of individual assignments
|
|
||||||
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 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 struct = targetVar.struct!!
|
|
||||||
when (structAssignment.value) {
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
|
|
||||||
when {
|
|
||||||
sourceVar.struct!=null -> {
|
|
||||||
// struct memberwise copy
|
|
||||||
val sourceStruct = sourceVar.struct!!
|
|
||||||
if(sourceStruct!==targetVar.struct) {
|
|
||||||
// structs are not the same in assignment
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
}
|
|
||||||
if(struct.statements.size!=sourceStruct.statements.size)
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
return struct.statements.zip(sourceStruct.statements).map { member ->
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sourceVar.isArray -> {
|
|
||||||
val array = (sourceVar.value as ArrayLiteralValue).value
|
|
||||||
if(struct.statements.size!=array.size)
|
|
||||||
return listOf() // error will be printed elsewhere
|
|
||||||
return struct.statements.zip(array).map {
|
|
||||||
val decl = it.first as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, decl.name)
|
|
||||||
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val target = AssignTarget(targetName, null, null, structAssignment.position)
|
|
||||||
val assign = Assignment(target, it.second, structAssignment.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ArrayLiteralValue -> {
|
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("strange struct value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,26 +8,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.compiler.ErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||||
/*
|
/*
|
||||||
* Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
* Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
||||||
* (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) {
|
||||||
val valueDt = declValue.inferType(program)
|
val valueDt = declValue.inferType(program)
|
||||||
if(!valueDt.istype(decl.datatype)) {
|
if(!valueDt.istype(decl.datatype)) {
|
||||||
|
|
||||||
// don't add a typecast on an array initializer value
|
// don't add a typecast on an array initializer value
|
||||||
if(valueDt.typeOrElse(DataType.STRUCT) in IntegerDatatypes && decl.datatype in ArrayDatatypes)
|
if(valueDt.isInteger() && decl.datatype in ArrayDatatypes)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
// don't add a typecast if the initializer value is inherently not assignable
|
||||||
|
if(valueDt isNotAssignableTo decl.datatype)
|
||||||
return noModifications
|
return noModifications
|
||||||
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
@ -45,7 +47,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val rightDt = expr.right.inferType(program)
|
val rightDt = expr.right.inferType(program)
|
||||||
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
||||||
// determine common datatype and add typecast as required to make left and right equal types
|
// determine common datatype and add typecast as required to make left and right equal types
|
||||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.STRUCT), rightDt.typeOrElse(DataType.STRUCT), expr.left, expr.right)
|
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.UNDEFINED), rightDt.typeOrElse(DataType.UNDEFINED), expr.left, expr.right)
|
||||||
if(toFix!=null) {
|
if(toFix!=null) {
|
||||||
return when {
|
return when {
|
||||||
toFix===expr.left -> listOf(IAstModification.ReplaceNode(
|
toFix===expr.left -> listOf(IAstModification.ReplaceNode(
|
||||||
@ -64,8 +66,8 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
val valueItype = assignment.value.inferType(program)
|
val valueItype = assignment.value.inferType(program)
|
||||||
val targetItype = assignment.target.inferType(program)
|
val targetItype = assignment.target.inferType(program)
|
||||||
if(targetItype.isKnown && valueItype.isKnown) {
|
if(targetItype.isKnown && valueItype.isKnown) {
|
||||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
val targettype = targetItype.typeOrElse(DataType.UNDEFINED)
|
||||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
val valuetype = valueItype.typeOrElse(DataType.UNDEFINED)
|
||||||
if (valuetype != targettype) {
|
if (valuetype != targettype) {
|
||||||
if (valuetype isAssignableTo targettype) {
|
if (valuetype isAssignableTo targettype) {
|
||||||
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
|
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
|
||||||
@ -124,7 +126,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
sub.parameters.zip(call.args).forEachIndexed { index, pair ->
|
sub.parameters.zip(call.args).forEachIndexed { index, pair ->
|
||||||
val argItype = pair.second.inferType(program)
|
val argItype = pair.second.inferType(program)
|
||||||
if(argItype.isKnown) {
|
if(argItype.isKnown) {
|
||||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
val argtype = argItype.typeOrElse(DataType.UNDEFINED)
|
||||||
val requiredType = pair.first.type
|
val requiredType = pair.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
@ -157,7 +159,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
func.parameters.zip(call.args).forEachIndexed { index, pair ->
|
func.parameters.zip(call.args).forEachIndexed { index, pair ->
|
||||||
val argItype = pair.second.inferType(program)
|
val argItype = pair.second.inferType(program)
|
||||||
if (argItype.isKnown) {
|
if (argItype.isKnown) {
|
||||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
val argtype = argItype.typeOrElse(DataType.UNDEFINED)
|
||||||
if (pair.first.possibleDatatypes.all { argtype != it }) {
|
if (pair.first.possibleDatatypes.all { argtype != it }) {
|
||||||
for (possibleType in pair.first.possibleDatatypes) {
|
for (possibleType in pair.first.possibleDatatypes) {
|
||||||
if (argtype isAssignableTo possibleType) {
|
if (argtype isAssignableTo possibleType) {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
|||||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||||
if(firstUnknownDt>=0)
|
if(firstUnknownDt>=0)
|
||||||
return "argument ${firstUnknownDt+1} invalid argument type"
|
return "argument ${firstUnknownDt+1} invalid argument type"
|
||||||
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
|
val argtypes = argITypes.map { it.typeOrElse(DataType.UNDEFINED) }
|
||||||
val target = call.target.targetStatement(program)
|
val target = call.target.targetStatement(program)
|
||||||
if (target is Subroutine) {
|
if (target is Subroutine) {
|
||||||
if(call.args.size != target.parameters.size)
|
if(call.args.size != target.parameters.size)
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
package prog8.compiler.functions
|
package prog8.compiler.functions
|
||||||
|
|
||||||
|
import prog8.ast.IMemSizer
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.StructDecl
|
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import prog8.compiler.target.ICompilationTarget
|
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
class FParam(val name: String, val possibleDatatypes: Set<DataType>)
|
class FParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||||
|
|
||||||
|
|
||||||
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer) -> NumericLiteralValue
|
||||||
|
|
||||||
|
|
||||||
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||||
@ -88,6 +87,7 @@ class FSignature(val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
||||||
private val functionSignatures: List<FSignature> = listOf(
|
private val functionSignatures: List<FSignature> = listOf(
|
||||||
// this set of function have no return value and operate in-place:
|
// this set of function have no return value and operate in-place:
|
||||||
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
@ -96,41 +96,41 @@ 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 -> 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 -> 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
|
||||||
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||||
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||||
FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof),
|
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||||
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||||
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||||
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
||||||
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
||||||
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
||||||
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||||
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
||||||
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
||||||
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
||||||
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
||||||
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||||
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||||
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::log) },
|
||||||
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, ::log2) },
|
||||||
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
||||||
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||||
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||||
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||||
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||||
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||||
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAny) },
|
||||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAll) },
|
||||||
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
|
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
|
||||||
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
|
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
|
||||||
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||||
FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE),
|
FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE),
|
||||||
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
|
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
|
||||||
@ -141,6 +141,8 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
||||||
FSignature("memory" , true, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD),
|
FSignature("memory" , true, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD),
|
||||||
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
||||||
|
FSignature("callfar" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
|
||||||
|
FSignature("callrom" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble()
|
|||||||
|
|
||||||
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
||||||
|
|
||||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
fun builtinSum(array: List<Number>): Number = array.sumOf { it.toDouble() }
|
||||||
|
|
||||||
fun builtinAny(array: List<Number>): Number = if(array.any { it.toDouble()!=0.0 }) 1 else 0
|
fun builtinAny(array: List<Number>): Number = if(array.any { it.toDouble()!=0.0 }) 1 else 0
|
||||||
|
|
||||||
@ -162,7 +164,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
|
|
||||||
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||||
if(arglist is ArrayLiteralValue) {
|
if(arglist is ArrayLiteralValue) {
|
||||||
val dt = arglist.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet()
|
val dt = arglist.value.map {it.inferType(program).typeOrElse(DataType.UNDEFINED)}.toSet()
|
||||||
if(dt.any { it !in NumericDatatypes }) {
|
if(dt.any { it !in NumericDatatypes }) {
|
||||||
throw FatalAstException("fuction $function only accepts array of numeric values")
|
throw FatalAstException("fuction $function only accepts array of numeric values")
|
||||||
}
|
}
|
||||||
@ -176,9 +178,9 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
val idt = arglist.inferType(program)
|
val idt = arglist.inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("couldn't determine type of iterable $arglist")
|
throw FatalAstException("couldn't determine type of iterable $arglist")
|
||||||
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
|
return when(val dt = idt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.STR, in NumericDatatypes -> dt
|
DataType.STR, in NumericDatatypes -> dt
|
||||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
in ArrayDatatypes -> ArrayToElementTypes.getValue(dt)
|
||||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +195,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
return when (function) {
|
return when (function) {
|
||||||
"abs" -> {
|
"abs" -> {
|
||||||
val dt = args.single().inferType(program)
|
val dt = args.single().inferType(program)
|
||||||
return if(dt.typeOrElse(DataType.STRUCT) in NumericDatatypes)
|
return if(dt.isNumeric())
|
||||||
dt
|
dt
|
||||||
else
|
else
|
||||||
InferredTypes.InferredType.unknown()
|
InferredTypes.InferredType.unknown()
|
||||||
@ -202,7 +204,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
|||||||
when(val dt = datatypeFromIterableArg(args.single())) {
|
when(val dt = datatypeFromIterableArg(args.single())) {
|
||||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
||||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(dt))
|
||||||
else -> InferredTypes.unknown()
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,7 +273,8 @@ private fun collectionArg(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinAbs(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
// 1 arg, type = float or int, result type= isSameAs as argument type
|
// 1 arg, type = float or int, result type= isSameAs as argument type
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("abs requires one numeric argument", position)
|
throw SyntaxError("abs requires one numeric argument", position)
|
||||||
@ -284,29 +287,7 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
// 1 arg, type = anything, result type = ubyte
|
|
||||||
if(args.size!=1)
|
|
||||||
throw SyntaxError("offsetof requires one argument", position)
|
|
||||||
val idref = args[0] as? IdentifierReference
|
|
||||||
?: throw SyntaxError("offsetof argument should be an identifier", position)
|
|
||||||
|
|
||||||
val vardecl = idref.targetVarDecl(program)!!
|
|
||||||
val struct = vardecl.struct
|
|
||||||
if (struct == null || vardecl.datatype == DataType.STRUCT)
|
|
||||||
throw SyntaxError("offsetof can only be used on struct members", position)
|
|
||||||
|
|
||||||
val membername = idref.nameInSource.last()
|
|
||||||
var offset = 0
|
|
||||||
for(member in struct.statements) {
|
|
||||||
if((member as VarDecl).name == membername)
|
|
||||||
return NumericLiteralValue(DataType.UBYTE, offset, position)
|
|
||||||
offset += ICompilationTarget.instance.memorySize(member.datatype)
|
|
||||||
}
|
|
||||||
throw SyntaxError("undefined struct member", position)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
|
||||||
// 1 arg, type = anything, result type = ubyte
|
// 1 arg, type = anything, result type = ubyte
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("sizeof requires one argument", position)
|
throw SyntaxError("sizeof requires one argument", position)
|
||||||
@ -318,31 +299,22 @@ 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) =
|
|
||||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { ICompilationTarget.instance.memorySize((it as VarDecl).datatype) }.sum(), position)
|
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
dt.isArray() -> {
|
||||||
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
val elementDt = ArrayToElementTypes.getValue(dt.typeOrElse(DataType.UNDEFINED))
|
||||||
numericLiteral(ICompilationTarget.instance.memorySize(elementDt) * length, position)
|
numericLiteral(memsizer.memorySize(elementDt) * length, position)
|
||||||
}
|
|
||||||
dt.istype(DataType.STRUCT) -> {
|
|
||||||
when (target) {
|
|
||||||
is VarDecl -> structSize(target.struct!!)
|
|
||||||
is StructDecl -> structSize(target)
|
|
||||||
else -> throw CompilerException("weird struct type $target")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||||
else -> NumericLiteralValue(DataType.UBYTE, ICompilationTarget.instance.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
|
else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.typeOrElse(DataType.UNDEFINED)), position)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw SyntaxError("sizeof invalid argument type", position)
|
throw SyntaxError("sizeof invalid argument type", position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinLen(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("len requires one argument", position)
|
throw SyntaxError("len requires one argument", position)
|
||||||
@ -366,17 +338,17 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
val refLv = target.value as StringLiteralValue
|
val refLv = target.value as? StringLiteralValue ?: throw CannotEvaluateException("len", "stringsize unknown")
|
||||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||||
}
|
}
|
||||||
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
|
||||||
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinMkword(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||||
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -385,7 +357,8 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinSin8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin8 requires one argument", position)
|
throw SyntaxError("sin8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -393,7 +366,8 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
|
|||||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin8u requires one argument", position)
|
throw SyntaxError("sin8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -401,7 +375,8 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
|
|||||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinCos8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos8 requires one argument", position)
|
throw SyntaxError("cos8 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -409,7 +384,8 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
|
|||||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
|
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos8u requires one argument", position)
|
throw SyntaxError("cos8u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -417,7 +393,8 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
|
|||||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
|
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinSin16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin16 requires one argument", position)
|
throw SyntaxError("sin16 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -425,7 +402,8 @@ private fun builtinSin16(args: List<Expression>, position: Position, program: Pr
|
|||||||
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
|
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sin16u requires one argument", position)
|
throw SyntaxError("sin16u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -433,7 +411,8 @@ private fun builtinSin16u(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
|
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinCos16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos16 requires one argument", position)
|
throw SyntaxError("cos16 requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -441,7 +420,8 @@ private fun builtinCos16(args: List<Expression>, position: Position, program: Pr
|
|||||||
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
|
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("cos16u requires one argument", position)
|
throw SyntaxError("cos16u requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
@ -449,7 +429,8 @@ private fun builtinCos16u(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
private fun builtinSgn(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("sgn requires one argument", position)
|
throw SyntaxError("sgn requires one argument", position)
|
||||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
|
@ -1,31 +1,29 @@
|
|||||||
package prog8.compiler.target
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import prog8.ast.IMemSizer
|
||||||
import prog8.ast.IStringEncoding
|
import prog8.ast.IStringEncoding
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.ErrorReporter
|
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
|
||||||
|
|
||||||
|
|
||||||
internal interface ICompilationTarget: IStringEncoding {
|
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||||
val name: String
|
val name: String
|
||||||
val machine: IMachineDefinition
|
val machine: IMachineDefinition
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||||
fun memorySize(dt: DataType): Int
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
lateinit var instance: ICompilationTarget // TODO reduce dependency on this by just passing the instance as a parameter
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isInRegularRAM(target: AssignTarget, program: Program): Boolean {
|
fun isInRegularRAM(target: AssignTarget, program: Program): Boolean {
|
||||||
val memAddr = target.memoryAddress
|
val memAddr = target.memoryAddress
|
||||||
@ -67,7 +65,6 @@ internal interface ICompilationTarget: IStringEncoding {
|
|||||||
else -> return true
|
else -> return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,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) {
|
||||||
@ -94,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) {
|
||||||
@ -113,11 +126,12 @@ internal object Cx16Target: ICompilationTarget {
|
|||||||
internal fun asmGeneratorFor(
|
internal fun asmGeneratorFor(
|
||||||
compTarget: ICompilationTarget,
|
compTarget: ICompilationTarget,
|
||||||
program: Program,
|
program: Program,
|
||||||
errors: ErrorReporter,
|
errors: IErrorReporter,
|
||||||
zp: Zeropage,
|
zp: Zeropage,
|
||||||
options: CompilationOptions,
|
options: CompilationOptions,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
package prog8.compiler.target
|
package prog8.compiler.target
|
||||||
|
|
||||||
import prog8.ast.IStringEncoding
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import prog8.parser.ModuleImporter
|
|
||||||
|
|
||||||
|
|
||||||
internal interface IMachineFloat {
|
interface IMachineFloat {
|
||||||
fun toDouble(): Double
|
fun toDouble(): Double
|
||||||
fun makeFloatFillAsm(): String
|
fun makeFloatFillAsm(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class CpuType {
|
enum class CpuType {
|
||||||
CPU6502,
|
CPU6502,
|
||||||
CPU65c02
|
CPU65c02
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface IMachineDefinition {
|
interface IMachineDefinition {
|
||||||
val FLOAT_MAX_NEGATIVE: Double
|
val FLOAT_MAX_NEGATIVE: Double
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
val FLOAT_MEM_SIZE: Int
|
val FLOAT_MEM_SIZE: Int
|
||||||
@ -34,10 +31,7 @@ internal interface IMachineDefinition {
|
|||||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||||
fun getFloat(num: Number): IMachineFloat
|
fun getFloat(num: Number): IMachineFloat
|
||||||
|
|
||||||
// TODO don't do the importing here, just return a list of modules to import...:
|
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||||
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program,
|
|
||||||
encoder: IStringEncoding, compilationTargetName: String)
|
|
||||||
|
|
||||||
fun launchEmulator(programName: String)
|
fun launchEmulator(programName: String)
|
||||||
fun isRegularRAMaddress(address: Int): Boolean
|
fun isRegularRAMaddress(address: Int): Boolean
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.ast.IStringEncoding
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.IMachineDefinition
|
import prog8.compiler.target.IMachineDefinition
|
||||||
import prog8.compiler.target.IMachineFloat
|
import prog8.compiler.target.IMachineFloat
|
||||||
import prog8.parser.ModuleImporter
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@ -31,15 +28,11 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
override fun importLibs(
|
override fun importLibs(compilerOptions: CompilationOptions,compilationTargetName: String): List<String> {
|
||||||
compilerOptions: CompilationOptions,
|
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
importer: ModuleImporter,
|
listOf("syslib")
|
||||||
program: Program,
|
else
|
||||||
encoder: IStringEncoding,
|
emptyList()
|
||||||
compilationTargetName: String)
|
|
||||||
{
|
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
|
||||||
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(programName: String) {
|
||||||
@ -109,13 +102,14 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
// 0x90-0xfa is 'kernel work storage area'
|
// 0x90-0xfa is 'kernal work storage area'
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -1049,51 +1049,90 @@ object Petscii {
|
|||||||
private val encodingScreencodeLowercase = decodingScreencodeLowercase.withIndex().associate{it.value to it.index}
|
private val encodingScreencodeLowercase = decodingScreencodeLowercase.withIndex().associate{it.value to it.index}
|
||||||
private val encodingScreencodeUppercase = decodingScreencodeUppercase.withIndex().associate{it.value to it.index}
|
private val encodingScreencodeUppercase = decodingScreencodeUppercase.withIndex().associate{it.value to it.index}
|
||||||
|
|
||||||
|
private fun replaceSpecial(chr: Char): Char =
|
||||||
|
// characters often used in C like source code can be translated with a little bit of fantasy:
|
||||||
|
when(chr) {
|
||||||
|
'^' -> '↑'
|
||||||
|
'_' -> '▁'
|
||||||
|
'{' -> '┤'
|
||||||
|
'}' -> '├'
|
||||||
|
'|' -> '│'
|
||||||
|
'\\' -> '╲'
|
||||||
|
else -> chr
|
||||||
|
}
|
||||||
|
|
||||||
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(chr3: Char, lowercase: Boolean): Short {
|
||||||
return text.map {
|
val chr = replaceSpecial(chr3)
|
||||||
val petscii = lookup[it]
|
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
||||||
petscii?.toShort() ?: when (it) {
|
return screencode?.toShort() ?: when (chr) {
|
||||||
'\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.code - 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.code})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(chr3: Char, lowercase: Boolean): Short {
|
||||||
return text.map{
|
val chr = replaceSpecial(chr3)
|
||||||
val screencode = lookup[it]
|
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||||
screencode?.toShort() ?: when (it) {
|
return screencode?.toShort() ?: when (chr) {
|
||||||
'\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.code - 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.code})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,12 +9,12 @@ 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.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -22,15 +22,16 @@ import kotlin.math.absoluteValue
|
|||||||
|
|
||||||
|
|
||||||
internal class AsmGen(private val program: Program,
|
internal class AsmGen(private val program: Program,
|
||||||
val errors: ErrorReporter,
|
val errors: IErrorReporter,
|
||||||
val zeropage: Zeropage,
|
val zeropage: Zeropage,
|
||||||
val options: CompilationOptions,
|
val options: CompilationOptions,
|
||||||
val compTarget: ICompilationTarget,
|
private val compTarget: ICompilationTarget,
|
||||||
private val outputDir: Path): IAssemblyGenerator {
|
private val outputDir: Path): IAssemblyGenerator {
|
||||||
|
|
||||||
// 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,640)
|
||||||
|
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)
|
||||||
@ -88,12 +89,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
return AssemblyProgram(program.name, outputDir, compTarget.name)
|
return AssemblyProgram(program.name, outputDir, compTarget.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun isTargetCpu(cpu: CpuType) = compTarget.machine.cpu == cpu
|
||||||
|
internal fun haveFPWR() = compTarget is Cx16Target
|
||||||
|
|
||||||
private fun header() {
|
private fun header() {
|
||||||
val ourName = this.javaClass.name
|
val ourName = this.javaClass.name
|
||||||
val cpu = when(compTarget.machine.cpu) {
|
val cpu = when(compTarget.machine.cpu) {
|
||||||
CpuType.CPU6502 -> "6502"
|
CpuType.CPU6502 -> "6502"
|
||||||
CpuType.CPU65c02 -> "65c02"
|
CpuType.CPU65c02 -> "w65c02"
|
||||||
else -> "unsupported"
|
else -> "unsupported"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,17 +128,19 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("* = ${program.actualLoadAddress.toHex()}")
|
out("* = ${program.actualLoadAddress.toHex()}")
|
||||||
val year = LocalDate.now().year
|
val year = LocalDate.now().year
|
||||||
out(" .word (+), $year")
|
out(" .word (+), $year")
|
||||||
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
|
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||||
out("+\t.word 0")
|
out("+\t.word 0")
|
||||||
out("_prog8_entrypoint\t; assembly code starts here\n")
|
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
out(" jsr ${compTarget.name}.init_system")
|
out(" jsr ${compTarget.name}.init_system")
|
||||||
|
out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
}
|
}
|
||||||
options.output == OutputType.PRG -> {
|
options.output == OutputType.PRG -> {
|
||||||
out("; ---- program without basic sys call ----")
|
out("; ---- program without basic sys call ----")
|
||||||
out("* = ${program.actualLoadAddress.toHex()}\n")
|
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||||
if(!options.noSysInit)
|
if(!options.noSysInit)
|
||||||
out(" jsr ${compTarget.name}.init_system")
|
out(" jsr ${compTarget.name}.init_system")
|
||||||
|
out(" jsr ${compTarget.name}.init_system_phase2")
|
||||||
}
|
}
|
||||||
options.output == OutputType.RAW -> {
|
options.output == OutputType.RAW -> {
|
||||||
out("; ---- raw assembler program ----")
|
out("; ---- raw assembler program ----")
|
||||||
@ -152,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() {
|
||||||
@ -176,14 +190,17 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun block2asm(block: Block) {
|
private fun block2asm(block: Block) {
|
||||||
out("\n\n; ---- block: '${block.name}' ----")
|
out("\n\n; ---- block: '${block.name}' ----")
|
||||||
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
if(block.address!=null)
|
||||||
|
out("* = ${block.address!!.toHex()}")
|
||||||
val addr = block.address
|
else {
|
||||||
if(addr!=null) {
|
if("align_word" in block.options())
|
||||||
out(".cerror * > ${addr.toHex()}, 'block address overlaps by ', *-${addr.toHex()},' bytes'")
|
out("\t.align 2")
|
||||||
out("* = ${addr.toHex()}")
|
else if("align_page" in block.options())
|
||||||
|
out("\t.align $100")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||||
|
|
||||||
outputSourceLine(block)
|
outputSourceLine(block)
|
||||||
zeropagevars2asm(block.statements)
|
zeropagevars2asm(block.statements)
|
||||||
memdefs2asm(block.statements)
|
memdefs2asm(block.statements)
|
||||||
@ -238,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 }
|
||||||
@ -262,7 +270,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
try {
|
try {
|
||||||
val errors = ErrorReporter()
|
val errors = ErrorReporter()
|
||||||
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
||||||
errors.handle()
|
errors.report()
|
||||||
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
||||||
// make sure we add the var to the set of zpvars for this block
|
// make sure we add the var to the set of zpvars for this block
|
||||||
allocatedZeropageVariables[fullName] = address to variable.datatype
|
allocatedZeropageVariables[fullName] = address to variable.datatype
|
||||||
@ -282,10 +290,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.UWORD -> out("$name\t.word 0")
|
DataType.UWORD -> out("$name\t.word 0")
|
||||||
DataType.WORD -> out("$name\t.sint 0")
|
DataType.WORD -> out("$name\t.sint 0")
|
||||||
DataType.FLOAT -> out("$name\t.byte 0,0,0,0,0 ; float")
|
DataType.FLOAT -> out("$name\t.byte 0,0,0,0,0 ; float")
|
||||||
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)
|
||||||
@ -344,11 +351,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
out(" .byte ${f.second} ; float ${f.first}")
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun memdefs2asm(statements: List<Statement>) {
|
private fun memdefs2asm(statements: List<Statement>) {
|
||||||
out("\n; memdefs and kernel subroutines")
|
out("\n; memdefs and kernal subroutines")
|
||||||
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||||
for(m in memvars) {
|
for(m in memvars) {
|
||||||
if(m.value is NumericLiteralValue)
|
if(m.value is NumericLiteralValue)
|
||||||
@ -361,7 +371,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val addr = sub.asmAddress
|
val addr = sub.asmAddress
|
||||||
if(addr!=null) {
|
if(addr!=null) {
|
||||||
if(sub.statements.isNotEmpty())
|
if(sub.statements.isNotEmpty())
|
||||||
throw AssemblyError("kernel subroutine cannot have statements")
|
throw AssemblyError("kernal subroutine cannot have statements")
|
||||||
out(" ${sub.name} = ${addr.toHex()}")
|
out(" ${sub.name} = ${addr.toHex()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,18 +381,12 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("\n; non-zeropage variables")
|
out("\n; non-zeropage variables")
|
||||||
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
|
|
||||||
// first output the flattened struct member variables *in order*
|
|
||||||
// after that, the other variables sorted by their datatype
|
|
||||||
|
|
||||||
val (structMembers, normalVars) = vars.partition { it.struct!=null }
|
|
||||||
structMembers.forEach { vardecl2asm(it) }
|
|
||||||
|
|
||||||
// special treatment for string types: merge strings that are identical
|
// special treatment for string types: merge strings that are identical
|
||||||
val encodedstringVars = normalVars
|
val encodedstringVars = vars
|
||||||
.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) {
|
||||||
@ -392,15 +396,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// non-string variables
|
// non-string variables
|
||||||
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
||||||
if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
|
if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
|
||||||
vardecl2asm(it)
|
vardecl2asm(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
|
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
|
||||||
val string = (lastvar.value as StringLiteralValue).value
|
val sv = lastvar.value as StringLiteralValue
|
||||||
out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"")
|
val altEncoding = if(sv.altEncoding) "@" else ""
|
||||||
|
out("${lastvar.name}\t; ${lastvar.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
|
||||||
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
|
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
|
||||||
for (chunk in outputBytes.chunked(16))
|
for (chunk in outputBytes.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
@ -428,10 +433,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
||||||
}
|
}
|
||||||
is AddressOf -> {
|
is AddressOf -> {
|
||||||
it.identifier.firstStructVarName(program) ?: asmSymbolName(it.identifier)
|
asmSymbolName(it.identifier)
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
it.firstStructVarName(program) ?: asmSymbolName(it)
|
asmSymbolName(it)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird array elt dt")
|
else -> throw AssemblyError("weird array elt dt")
|
||||||
}
|
}
|
||||||
@ -492,29 +497,17 @@ internal class AsmGen(private val program: Program,
|
|||||||
return newName
|
return newName
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun asmSymbolName(identifier: IdentifierReference): String {
|
internal fun asmSymbolName(identifier: IdentifierReference) =
|
||||||
return if(identifier.memberOfStruct(program)!=null) {
|
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||||
val name = identifier.targetVarDecl(program)!!.name
|
|
||||||
fixNameSymbols(name)
|
|
||||||
} else {
|
|
||||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun asmSymbolName(regs: RegisterOrPair): String =
|
internal fun asmSymbolName(regs: RegisterOrPair): String =
|
||||||
if(regs in Cx16VirtualRegisters)
|
if (regs in Cx16VirtualRegisters)
|
||||||
"cx16." + regs.toString().toLowerCase()
|
"cx16." + regs.toString().lowercase()
|
||||||
else
|
else
|
||||||
throw AssemblyError("no symbol name for register $regs")
|
throw AssemblyError("no symbol name for register $regs")
|
||||||
|
|
||||||
internal fun asmVariableName(identifier: IdentifierReference): String {
|
internal fun asmVariableName(identifier: IdentifierReference) =
|
||||||
return if(identifier.memberOfStruct(program)!=null) {
|
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||||
val name = identifier.targetVarDecl(program)!!.name
|
|
||||||
fixNameSymbols(name)
|
|
||||||
} else {
|
|
||||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun asmSymbolName(name: String) = fixNameSymbols(name)
|
internal fun asmSymbolName(name: String) = fixNameSymbols(name)
|
||||||
internal fun asmVariableName(name: String) = fixNameSymbols(name)
|
internal fun asmVariableName(name: String) = fixNameSymbols(name)
|
||||||
@ -527,7 +520,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val sourceName = asmVariableName(pointervar)
|
val sourceName = asmVariableName(pointervar)
|
||||||
val vardecl = pointervar.targetVarDecl(program)!!
|
val vardecl = pointervar.targetVarDecl(program)!!
|
||||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||||
return if (isZpVar(scopedName)) {
|
return if (isZpVar(scopedName)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
out(" lda ($sourceName)")
|
out(" lda ($sourceName)")
|
||||||
@ -562,7 +555,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||||
|
|
||||||
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
|
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||||
// just use the cpu's stack for all registers, shorter code
|
// just use the cpu's stack for all registers, shorter code
|
||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> out(" pha")
|
CpuRegister.A -> out(" pha")
|
||||||
@ -591,7 +584,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> out(" pha")
|
CpuRegister.A -> out(" pha")
|
||||||
CpuRegister.X -> {
|
CpuRegister.X -> {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phx")
|
if (isTargetCpu(CpuType.CPU65c02)) out(" phx")
|
||||||
else {
|
else {
|
||||||
if(keepA)
|
if(keepA)
|
||||||
out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
|
out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
|
||||||
@ -600,7 +593,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CpuRegister.Y -> {
|
CpuRegister.Y -> {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phy")
|
if (isTargetCpu(CpuType.CPU65c02)) out(" phy")
|
||||||
else {
|
else {
|
||||||
if(keepA)
|
if(keepA)
|
||||||
out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
|
out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
|
||||||
@ -612,7 +605,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreRegisterLocal(register: CpuRegister) {
|
internal fun restoreRegisterLocal(register: CpuRegister) {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||||
when (register) {
|
when (register) {
|
||||||
// this just used the stack, for all registers. Shorter code.
|
// this just used the stack, for all registers. Shorter code.
|
||||||
CpuRegister.A -> out(" pla")
|
CpuRegister.A -> out(" pla")
|
||||||
@ -637,7 +630,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
out(" pla")
|
out(" pla")
|
||||||
}
|
}
|
||||||
CpuRegister.X -> {
|
CpuRegister.X -> {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" plx")
|
if (isTargetCpu(CpuType.CPU65c02)) out(" plx")
|
||||||
else {
|
else {
|
||||||
if(keepA)
|
if(keepA)
|
||||||
out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
|
out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
|
||||||
@ -646,7 +639,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CpuRegister.Y -> {
|
CpuRegister.Y -> {
|
||||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" ply")
|
if (isTargetCpu(CpuType.CPU65c02)) out(" ply")
|
||||||
else {
|
else {
|
||||||
if(keepA)
|
if(keepA)
|
||||||
out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
|
out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
|
||||||
@ -662,7 +655,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
when(stmt) {
|
when(stmt) {
|
||||||
is ParameterVarDecl -> { /* subroutine parameter vardecls don't get any special treatment here */ }
|
is ParameterVarDecl -> { /* subroutine parameter vardecls don't get any special treatment here */ }
|
||||||
is VarDecl -> translate(stmt)
|
is VarDecl -> translate(stmt)
|
||||||
is StructDecl, is NopStatement -> {}
|
is NopStatement -> {}
|
||||||
is Directive -> translate(stmt)
|
is Directive -> translate(stmt)
|
||||||
is Return -> translate(stmt)
|
is Return -> translate(stmt)
|
||||||
is Subroutine -> translateSubroutine(stmt)
|
is Subroutine -> translateSubroutine(stmt)
|
||||||
@ -686,7 +679,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)
|
||||||
@ -699,48 +692,58 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun loadScaledArrayIndexIntoRegister(expr: ArrayIndexedExpression,
|
internal fun loadScaledArrayIndexIntoRegister(
|
||||||
elementDt: DataType,
|
expr: ArrayIndexedExpression,
|
||||||
register: CpuRegister,
|
elementDt: DataType,
|
||||||
addOneExtra: Boolean=false) {
|
register: CpuRegister,
|
||||||
val reg = register.toString().toLowerCase()
|
addOneExtra: Boolean = false
|
||||||
|
) {
|
||||||
|
val reg = register.toString().lowercase()
|
||||||
val indexnum = expr.indexer.constIndex()
|
val indexnum = expr.indexer.constIndex()
|
||||||
if(indexnum!=null) {
|
if (indexnum != null) {
|
||||||
val indexValue = indexnum * compTarget.memorySize(elementDt) + if(addOneExtra) 1 else 0
|
val indexValue = indexnum * compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
|
||||||
out(" ld$reg #$indexValue")
|
out(" ld$reg #$indexValue")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val indexName = asmVariableName(expr.indexer.indexVar!!)
|
val indexVar = expr.indexer.indexExpr as? IdentifierReference
|
||||||
if(addOneExtra) {
|
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
|
||||||
|
|
||||||
|
val indexName = asmVariableName(indexVar)
|
||||||
|
if (addOneExtra) {
|
||||||
// add 1 to the result
|
// add 1 to the result
|
||||||
when(elementDt) {
|
when (elementDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
out(" ldy $indexName | iny")
|
out(" ldy $indexName | iny")
|
||||||
when(register) {
|
when (register) {
|
||||||
CpuRegister.A -> out(" tya")
|
CpuRegister.A -> out(" tya")
|
||||||
CpuRegister.X -> out(" tyx")
|
CpuRegister.X -> out(" tyx")
|
||||||
CpuRegister.Y -> {}
|
CpuRegister.Y -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
out(" lda $indexName | sec | rol a")
|
out(" lda $indexName | sec | rol a")
|
||||||
when(register) {
|
when (register) {
|
||||||
CpuRegister.A -> {}
|
CpuRegister.A -> {
|
||||||
|
}
|
||||||
CpuRegister.X -> out(" tax")
|
CpuRegister.X -> out(" tax")
|
||||||
CpuRegister.Y -> out(" tay")
|
CpuRegister.Y -> out(" tay")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||||
out("""
|
out(
|
||||||
|
"""
|
||||||
lda $indexName
|
lda $indexName
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
sec
|
sec
|
||||||
adc $indexName""")
|
adc $indexName"""
|
||||||
when(register) {
|
)
|
||||||
CpuRegister.A -> {}
|
when (register) {
|
||||||
|
CpuRegister.A -> {
|
||||||
|
}
|
||||||
CpuRegister.X -> out(" tax")
|
CpuRegister.X -> out(" tax")
|
||||||
CpuRegister.Y -> out(" tay")
|
CpuRegister.Y -> out(" tay")
|
||||||
}
|
}
|
||||||
@ -748,26 +751,30 @@ internal class AsmGen(private val program: Program,
|
|||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when(elementDt) {
|
when (elementDt) {
|
||||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
out(" lda $indexName | asl a")
|
out(" lda $indexName | asl a")
|
||||||
when(register) {
|
when (register) {
|
||||||
CpuRegister.A -> {}
|
CpuRegister.A -> {
|
||||||
|
}
|
||||||
CpuRegister.X -> out(" tax")
|
CpuRegister.X -> out(" tax")
|
||||||
CpuRegister.Y -> out(" tay")
|
CpuRegister.Y -> out(" tay")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||||
out("""
|
out(
|
||||||
|
"""
|
||||||
lda $indexName
|
lda $indexName
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc $indexName""")
|
adc $indexName"""
|
||||||
when(register) {
|
)
|
||||||
CpuRegister.A -> {}
|
when (register) {
|
||||||
|
CpuRegister.A -> {
|
||||||
|
}
|
||||||
CpuRegister.X -> out(" tax")
|
CpuRegister.X -> out(" tax")
|
||||||
CpuRegister.Y -> out(" tay")
|
CpuRegister.Y -> out(" tay")
|
||||||
}
|
}
|
||||||
@ -809,9 +816,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))
|
||||||
@ -828,7 +844,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
|
||||||
@ -852,8 +868,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
clc""")
|
clc""")
|
||||||
}
|
}
|
||||||
|
|
||||||
out("; statements")
|
if(!onlyVariables) {
|
||||||
sub.statements.forEach{ translate(it) }
|
out("; statements")
|
||||||
|
sub.statements.forEach { translate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
for(removal in removals.toList()) {
|
for(removal in removals.toList()) {
|
||||||
if(removal.second==sub) {
|
if(removal.second==sub) {
|
||||||
@ -908,6 +926,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")
|
||||||
@ -921,7 +943,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)
|
||||||
@ -943,7 +965,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 -> {
|
||||||
@ -967,10 +989,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")
|
||||||
}
|
}
|
||||||
@ -979,7 +1003,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val dt = stmt.iterations!!.inferType(program)
|
val dt = stmt.iterations!!.inferType(program)
|
||||||
if(!dt.isKnown)
|
if(!dt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.A)
|
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.A)
|
||||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||||
@ -997,10 +1021,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
|
||||||
@ -1009,80 +1034,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)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun repeatByteCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
|
||||||
// note: cannot use original counter variable because it should retain its original value
|
// allocate count var on ZP TODO can be shared with countervars from other subroutines
|
||||||
val counterVar = makeLabel("repeatcounter")
|
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
|
||||||
out(" lda $repeatCountVar | beq $endLabel | sta $counterVar")
|
out("$counterVar = $zpAddr ; auto zp UBYTE")
|
||||||
out(repeatLabel)
|
} else {
|
||||||
translate(body)
|
out("$counterVar .byte 0")
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1095,7 +1088,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()
|
||||||
}
|
}
|
||||||
@ -1129,7 +1122,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!!) {
|
||||||
@ -1148,11 +1141,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)
|
||||||
}
|
}
|
||||||
@ -1211,7 +1204,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)
|
||||||
@ -1254,7 +1247,9 @@ $counterVar .word 0""")
|
|||||||
"%asmbinary" -> {
|
"%asmbinary" -> {
|
||||||
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
|
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
|
||||||
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
|
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
|
||||||
out(" .binary \"${stmt.args[0].str}\" $offset $length")
|
val includedSourcePath = stmt.definingModule().source.resolveSibling(stmt.args[0].str)
|
||||||
|
val relPath = Paths.get("").relativize(includedSourcePath)
|
||||||
|
out(" .binary \"./$relPath\" $offset $length")
|
||||||
}
|
}
|
||||||
"%breakpoint" -> {
|
"%breakpoint" -> {
|
||||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||||
@ -1266,14 +1261,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)
|
||||||
@ -1289,7 +1282,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()
|
||||||
@ -1308,7 +1301,9 @@ $label nop""")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out(" rts")
|
|
||||||
|
if(withRts)
|
||||||
|
out(" rts")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(asm: InlineAssembly) {
|
private fun translate(asm: InlineAssembly) {
|
||||||
@ -1329,7 +1324,7 @@ $label nop""")
|
|||||||
// sign extend signed byte on stack to signed word on stack
|
// sign extend signed byte on stack to signed word on stack
|
||||||
when(valueDt) {
|
when(valueDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(compTarget.machine.cpu == CpuType.CPU65c02)
|
if(isTargetCpu(CpuType.CPU65c02))
|
||||||
out(" stz P8ESTACK_HI+1,x")
|
out(" stz P8ESTACK_HI+1,x")
|
||||||
else
|
else
|
||||||
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||||
@ -1343,7 +1338,7 @@ $label nop""")
|
|||||||
// sign extend signed byte in a var to a full word in that variable
|
// sign extend signed byte in a var to a full word in that variable
|
||||||
when(valueDt) {
|
when(valueDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(compTarget.machine.cpu == CpuType.CPU65c02)
|
if(isTargetCpu(CpuType.CPU65c02))
|
||||||
out(" stz $asmvar+1")
|
out(" stz $asmvar+1")
|
||||||
else
|
else
|
||||||
out(" lda #0 | sta $asmvar+1")
|
out(" lda #0 | sta $asmvar+1")
|
||||||
@ -1367,6 +1362,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)
|
||||||
@ -1396,39 +1398,44 @@ $label nop""")
|
|||||||
|
|
||||||
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
|
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
|
||||||
// optimize pointer,indexregister if possible
|
// optimize pointer,indexregister if possible
|
||||||
|
|
||||||
|
fun evalBytevalueWillClobberA(expr: Expression): Boolean {
|
||||||
|
val dt = expr.inferType(program)
|
||||||
|
if(!dt.istype(DataType.UBYTE) && !dt.istype(DataType.BYTE))
|
||||||
|
return true
|
||||||
|
return when(expr) {
|
||||||
|
is IdentifierReference -> false
|
||||||
|
is NumericLiteralValue -> false
|
||||||
|
is DirectMemoryRead -> expr.addressExpression !is IdentifierReference && expr.addressExpression !is NumericLiteralValue
|
||||||
|
is TypecastExpression -> evalBytevalueWillClobberA(expr.expression)
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(expr.operator=="+") {
|
if(expr.operator=="+") {
|
||||||
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
|
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
|
||||||
if(ptrAndIndex!=null) {
|
if(ptrAndIndex!=null) {
|
||||||
val pointervar = ptrAndIndex.first as? IdentifierReference
|
val pointervar = ptrAndIndex.first as? IdentifierReference
|
||||||
if(write) {
|
if(write) {
|
||||||
when(ptrAndIndex.second) {
|
if(pointervar!=null && isZpVar(pointervar)) {
|
||||||
is NumericLiteralValue, is IdentifierReference -> {
|
val saveA = evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||||
if(pointervar!=null && isZpVar(pointervar)) {
|
if(saveA)
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
out(" pha")
|
||||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
} else {
|
if(saveA)
|
||||||
// copy the pointer var to zp first
|
out(" pla")
|
||||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
} else {
|
||||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
// copy the pointer var to zp first
|
||||||
}
|
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||||
}
|
if(saveA)
|
||||||
else -> {
|
out(" pha")
|
||||||
// same as above but we need to save the A register
|
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||||
if(pointervar!=null && isZpVar(pointervar)) {
|
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||||
out(" pha")
|
if(saveA)
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
out(" pla")
|
||||||
out(" pla")
|
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
|
||||||
} else {
|
|
||||||
// copy the pointer var to zp first
|
|
||||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
|
||||||
out(" pha")
|
|
||||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
|
||||||
out(" pla")
|
|
||||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(pointervar!=null && isZpVar(pointervar)) {
|
if(pointervar!=null && isZpVar(pointervar)) {
|
@ -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,16 +1,20 @@
|
|||||||
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
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.ArrayIndex
|
||||||
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
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.Cx16Target
|
||||||
|
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) {
|
||||||
@ -61,14 +65,187 @@ 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)
|
||||||
|
"callfar" -> funcCallFar(fcall)
|
||||||
|
"callrom" -> funcCallRom(fcall)
|
||||||
|
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCallFar(fcall: IFunctionCall) {
|
||||||
|
if(asmgen.options.compTarget !is Cx16Target)
|
||||||
|
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||||
|
|
||||||
|
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||||
|
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
||||||
|
if(bank==null || address==null)
|
||||||
|
throw AssemblyError("callfar (jsrfar) requires constant arguments")
|
||||||
|
|
||||||
|
if(address !in 0xa000..0xbfff)
|
||||||
|
throw AssemblyError("callfar done on address outside of cx16 banked ram")
|
||||||
|
if(bank==0)
|
||||||
|
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
||||||
|
|
||||||
|
val argAddrArg = fcall.args[2]
|
||||||
|
if(argAddrArg.constValue(program)?.number == 0) {
|
||||||
|
asmgen.out("""
|
||||||
|
jsr cx16.jsrfar
|
||||||
|
.word ${address.toHex()}
|
||||||
|
.byte ${bank.toHex()}""")
|
||||||
|
} else {
|
||||||
|
when(argAddrArg) {
|
||||||
|
is AddressOf -> {
|
||||||
|
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
||||||
|
throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE")
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
|
jsr cx16.jsrfar
|
||||||
|
.word ${address.toHex()}
|
||||||
|
.byte ${bank.toHex()}
|
||||||
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${argAddrArg.number.toHex()}
|
||||||
|
jsr cx16.jsrfar
|
||||||
|
.word ${address.toHex()}
|
||||||
|
.byte ${bank.toHex()}
|
||||||
|
sta ${argAddrArg.number.toHex()}""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("callfar only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCallRom(fcall: IFunctionCall) {
|
||||||
|
if(asmgen.options.compTarget !is Cx16Target)
|
||||||
|
throw AssemblyError("callrom only works on cx16 target at this time")
|
||||||
|
|
||||||
|
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||||
|
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
||||||
|
if(bank==null || address==null)
|
||||||
|
throw AssemblyError("callrom requires constant arguments")
|
||||||
|
|
||||||
|
if(address !in 0xc000..0xffff)
|
||||||
|
throw AssemblyError("callrom done on address outside of cx16 banked rom")
|
||||||
|
if(bank>=32)
|
||||||
|
throw AssemblyError("callrom bank must be <32")
|
||||||
|
|
||||||
|
val argAddrArg = fcall.args[2]
|
||||||
|
if(argAddrArg.constValue(program)?.number == 0) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $01
|
||||||
|
pha
|
||||||
|
lda #${bank}
|
||||||
|
sta $01
|
||||||
|
jsr ${address.toHex()}
|
||||||
|
pla
|
||||||
|
sta $01""")
|
||||||
|
} else {
|
||||||
|
when(argAddrArg) {
|
||||||
|
is AddressOf -> {
|
||||||
|
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
||||||
|
throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE")
|
||||||
|
asmgen.out("""
|
||||||
|
lda $01
|
||||||
|
pha
|
||||||
|
lda #${bank}
|
||||||
|
sta $01
|
||||||
|
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
|
jsr ${address.toHex()}
|
||||||
|
sta ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||||
|
pla
|
||||||
|
sta $01""")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $01
|
||||||
|
pha
|
||||||
|
lda #${bank}
|
||||||
|
sta $01
|
||||||
|
lda ${argAddrArg.number.toHex()}
|
||||||
|
jsr ${address.toHex()}
|
||||||
|
sta ${argAddrArg.number.toHex()}
|
||||||
|
pla
|
||||||
|
sta $01""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("callrom only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(fcall: IFunctionCall) {
|
||||||
|
val arg1 = fcall.args[0]
|
||||||
|
val arg2 = fcall.args[1]
|
||||||
|
val dt1 = arg1.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
if(discardResult || fcall !is FunctionCall)
|
if(discardResult || fcall !is FunctionCall)
|
||||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||||
val scope = fcall.definingScope()
|
|
||||||
val nameRef = fcall.args[0] as IdentifierReference
|
val nameRef = fcall.args[0] as IdentifierReference
|
||||||
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
|
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
|
||||||
val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
|
val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
|
||||||
@ -85,12 +262,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, target, false, fcall.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
|
|
||||||
// remove the variable for the name, it's not used as a variable only as a tag for the assembler.
|
|
||||||
val nameDecl = scope.statements.single { it is VarDecl && it.name==nameRef.nameInSource.single() }
|
|
||||||
asmgen.removals.add(Pair(nameDecl, scope))
|
|
||||||
asmgen.slabs[name] = size
|
asmgen.slabs[name] = size
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +368,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcRor2(fcall: IFunctionCall) {
|
private fun funcRor2(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
@ -238,7 +411,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcRor(fcall: IFunctionCall) {
|
private fun funcRor(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
@ -296,7 +469,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcRol2(fcall: IFunctionCall) {
|
private fun funcRol2(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
@ -339,7 +512,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
private fun funcRol(fcall: IFunctionCall) {
|
private fun funcRol(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
@ -396,8 +569,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?) {
|
||||||
@ -414,7 +586,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall.args, func, scope)
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
||||||
@ -423,7 +595,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||||
@ -439,14 +611,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
||||||
@ -460,7 +632,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
|
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
|
||||||
@ -469,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
DataType.ARRAY_UB, DataType.STR -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||||
@ -480,11 +652,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")
|
||||||
@ -499,7 +671,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
|
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
|
||||||
@ -508,22 +680,22 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
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,12 +782,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val elementIDt = first.inferType(program)
|
val elementIDt = first.inferType(program)
|
||||||
if(!elementIDt.isKnown)
|
if(!elementIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
val elementDt = elementIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
|
|
||||||
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)
|
||||||
@ -643,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
val datatype = first.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
when(datatype) {
|
when(datatype) {
|
||||||
in ByteDatatypes, in WordDatatypes -> {
|
in ByteDatatypes, in WordDatatypes -> {
|
||||||
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
|
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
|
||||||
@ -651,12 +823,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val assignFirst = AsmAssignment(
|
val assignFirst = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
|
||||||
targetFromExpr(first, datatype),
|
targetFromExpr(first, datatype),
|
||||||
false, first.position
|
false, program.memsizer, first.position
|
||||||
)
|
)
|
||||||
val assignSecond = AsmAssignment(
|
val assignSecond = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
|
||||||
targetFromExpr(second, datatype),
|
targetFromExpr(second, datatype),
|
||||||
false, second.position
|
false, program.memsizer, second.position
|
||||||
)
|
)
|
||||||
asmgen.translateNormalAssignment(assignFirst)
|
asmgen.translateNormalAssignment(assignFirst)
|
||||||
asmgen.translateNormalAssignment(assignSecond)
|
asmgen.translateNormalAssignment(assignSecond)
|
||||||
@ -668,12 +840,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val assignFirst = AsmAssignment(
|
val assignFirst = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
||||||
targetFromExpr(first, datatype),
|
targetFromExpr(first, datatype),
|
||||||
false, first.position
|
false, program.memsizer, first.position
|
||||||
)
|
)
|
||||||
val assignSecond = AsmAssignment(
|
val assignSecond = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
||||||
targetFromExpr(second, datatype),
|
targetFromExpr(second, datatype),
|
||||||
false, second.position
|
false, program.memsizer, second.position
|
||||||
)
|
)
|
||||||
asmgen.translateNormalAssignment(assignFirst)
|
asmgen.translateNormalAssignment(assignFirst)
|
||||||
asmgen.translateNormalAssignment(assignSecond)
|
asmgen.translateNormalAssignment(assignSecond)
|
||||||
@ -683,8 +855,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||||
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -797,7 +969,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||||
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
@ -856,7 +1028,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||||
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -914,7 +1086,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall.args, func, scope)
|
||||||
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
if(resultToStack) {
|
if(resultToStack) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||||
@ -926,15 +1098,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")
|
||||||
}
|
}
|
||||||
@ -977,7 +1149,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta ($varname)
|
sta ($varname)
|
||||||
txa
|
txa
|
||||||
@ -997,18 +1169,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
||||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||||
asmgen.out("""
|
// pointervar is already in the zero page, no need to copy
|
||||||
ldy #$index
|
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
||||||
sta ($varname),y
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||||
txa
|
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||||
iny
|
asmgen.out("""
|
||||||
sta ($varname),y""")
|
ldy #$index
|
||||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
sta ($varname),y
|
||||||
return
|
txa
|
||||||
|
iny
|
||||||
|
sta ($varname),y""")
|
||||||
|
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1028,7 +1203,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val varname = asmgen.asmVariableName(addrExpr)
|
val varname = asmgen.asmVariableName(addrExpr)
|
||||||
if(asmgen.isZpVar(addrExpr)) {
|
if(asmgen.isZpVar(addrExpr)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #1
|
ldy #1
|
||||||
lda ($varname),y
|
lda ($varname),y
|
||||||
@ -1052,15 +1227,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
is BinaryExpression -> {
|
is BinaryExpression -> {
|
||||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
||||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||||
asmgen.out("""
|
// pointervar is already in the zero page, no need to copy
|
||||||
ldy #$index
|
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||||
lda ($varname),y
|
asmgen.out("""
|
||||||
pha
|
ldy #$index
|
||||||
iny
|
lda ($varname),y
|
||||||
lda ($varname),y
|
pha
|
||||||
tay
|
iny
|
||||||
pla""")
|
lda ($varname),y
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||||
@ -1079,7 +1260,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
RegisterOrPair.AY -> {}
|
RegisterOrPair.AY -> {}
|
||||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().toLowerCase()} | sty cx16.${resultRegister.toString().toLowerCase()}+1")
|
in Cx16VirtualRegisters -> asmgen.out(
|
||||||
|
" sta cx16.${
|
||||||
|
resultRegister.toString().lowercase()
|
||||||
|
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||||
else -> throw AssemblyError("invalid reg")
|
else -> throw AssemblyError("invalid reg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1129,9 +1313,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}")
|
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}+1")
|
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid mkword target reg")
|
else -> throw AssemblyError("invalid mkword target reg")
|
||||||
}
|
}
|
||||||
@ -1140,7 +1324,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
if (!arg.inferType(program).isWords())
|
||||||
throw AssemblyError("msb required word argument")
|
throw AssemblyError("msb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("msb(const) should have been const-folded away")
|
throw AssemblyError("msb(const) should have been const-folded away")
|
||||||
@ -1184,7 +1368,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||||
val arg = fcall.args.single()
|
val arg = fcall.args.single()
|
||||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
if (!arg.inferType(program).isWords())
|
||||||
throw AssemblyError("lsb required word argument")
|
throw AssemblyError("lsb required word argument")
|
||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||||
@ -1248,7 +1432,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
||||||
val callConv = signature.callConvention(args.map { it.inferType(program).typeOrElse(DataType.STRUCT) })
|
val callConv = signature.callConvention(args.map { it.inferType(program).typeOrElse(DataType.UNDEFINED) })
|
||||||
|
|
||||||
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
||||||
return when (value) {
|
return when (value) {
|
||||||
@ -1292,7 +1476,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
|
||||||
val assign = AsmAssignment(src, tgt, false, value.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
conv.reg != null -> {
|
conv.reg != null -> {
|
||||||
@ -1308,7 +1492,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, value.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("callconv")
|
else -> throw AssemblyError("callconv")
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
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.ArrayToElementTypes
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.RegisterOrPair
|
import prog8.ast.base.RegisterOrPair
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
@ -21,13 +21,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
is RangeExpr -> {
|
is RangeExpr -> {
|
||||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
if(range==null) {
|
if(range==null) {
|
||||||
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
||||||
} else {
|
} else {
|
||||||
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), stmt.iterable as IdentifierReference)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||||
}
|
}
|
||||||
@ -40,6 +40,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||||
|
|
||||||
|
if(stepsize < -1) {
|
||||||
|
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||||
|
if(limit==0.0)
|
||||||
|
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||||
|
}
|
||||||
|
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
if (stepsize==1 || stepsize==-1) {
|
if (stepsize==1 || stepsize==-1) {
|
||||||
@ -49,17 +56,17 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
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 {
|
||||||
|
|
||||||
@ -67,8 +74,8 @@ $endLabel""")
|
|||||||
|
|
||||||
// loop over byte range via loopvar
|
// loop over byte range via loopvar
|
||||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
if(stepsize>0) {
|
if(stepsize>0) {
|
||||||
@ -117,16 +124,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,23 +392,25 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
-2 -> {
|
-2 -> {
|
||||||
when (range.last) {
|
when (range.last) {
|
||||||
0 -> asmgen.out("""
|
0 -> {
|
||||||
lda $varname
|
asmgen.out("""
|
||||||
beq $endLabel
|
lda $varname
|
||||||
dec $varname
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
jmp $loopLabel""")
|
dec $varname""")
|
||||||
|
asmgen.jmp(loopLabel)
|
||||||
|
}
|
||||||
1 -> asmgen.out("""
|
1 -> asmgen.out("""
|
||||||
dec $varname
|
dec $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
else -> asmgen.out("""
|
else -> asmgen.out("""
|
||||||
dec $varname
|
dec $varname
|
||||||
dec $varname
|
dec $varname
|
||||||
lda $varname
|
lda $varname
|
||||||
cmp #${range.last-2}
|
cmp #${range.last-2}
|
||||||
bne $loopLabel""")
|
bne $loopLabel""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -413,8 +421,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 +458,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 +510,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 +553,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,12 +581,12 @@ $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()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
||||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine())
|
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.UNDEFINED), stmt.definingSubroutine())
|
||||||
}
|
}
|
@ -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) {
|
||||||
@ -91,11 +91,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
when {
|
when {
|
||||||
stmt.args.all {isNoClobberRisk(it)} -> {
|
stmt.args.all {isNoClobberRisk(it)} -> {
|
||||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
||||||
|
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
|
||||||
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||||
val (cx16virtualRegsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||||
for(arg in cx16virtualRegsArgsInfo)
|
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
|
||||||
|
for(arg in cx16virtualRegs)
|
||||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
for(arg in otherRegsArgsInfo)
|
for(arg in cpuRegs)
|
||||||
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
|
for(arg in statusRegs)
|
||||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -107,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
|
||||||
@ -164,21 +175,31 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
when (sub.parameters[argi.index].type) {
|
when (sub.parameters[argi.index].type) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// only load the lsb of the virtual register
|
// only load the lsb of the virtual register
|
||||||
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().lowercase()}
|
||||||
""")
|
""")
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
asmgen.out(
|
||||||
|
" stz cx16.${
|
||||||
|
argi.value.second.registerOrPair.toString().lowercase()
|
||||||
|
}+1")
|
||||||
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().lowercase()
|
||||||
|
}+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().lowercase()}
|
||||||
lda P8ESTACK_HI$plusIdxStr,x
|
lda P8ESTACK_HI$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1
|
sta cx16.${
|
||||||
|
argi.value.second.registerOrPair.toString().lowercase()
|
||||||
|
}+1
|
||||||
""")
|
""")
|
||||||
else -> throw AssemblyError("weird dt")
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
@ -233,7 +254,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
@ -246,7 +267,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
@ -318,7 +339,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
} else {
|
} else {
|
||||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
}
|
}
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.*
|
||||||
@ -19,7 +19,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
when {
|
when {
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmVariableName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
when (stmt.target.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when (stmt.target.inferType(program).typeOrElse(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -65,9 +65,10 @@ 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.UNDEFINED)
|
||||||
if(targetArrayIdx.indexer.indexNum!=null) {
|
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * asmgen.compTarget.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,12 +1,12 @@
|
|||||||
package prog8.compiler.target.c64.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
|
import prog8.ast.IMemSizer
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
|
||||||
|
|
||||||
|
|
||||||
internal enum class TargetStorageKind {
|
internal enum class TargetStorageKind {
|
||||||
@ -59,7 +59,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
val idt = inferType(program)
|
val idt = inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
val dt = idt.typeOrElse(DataType.UNDEFINED)
|
||||||
when {
|
when {
|
||||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
|
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
|
||||||
@ -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)
|
||||||
@ -138,12 +132,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
val varName=asmgen.asmVariableName(value)
|
val varName=asmgen.asmVariableName(value)
|
||||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||||
if(dt==DataType.UWORD && varName.toLowerCase().startsWith("cx16.r")) {
|
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||||
val regStr = varName.toLowerCase().substring(5)
|
val regStr = varName.lowercase().substring(5)
|
||||||
val reg = RegisterOrPair.valueOf(regStr.toUpperCase())
|
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
||||||
} else {
|
} else {
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
||||||
@ -153,7 +147,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||||
}
|
}
|
||||||
is FunctionCall -> {
|
is FunctionCall -> {
|
||||||
@ -168,7 +162,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val returnType = value.inferType(program)
|
val returnType = value.inferType(program)
|
||||||
if(!returnType.isKnown)
|
if(!returnType.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.STRUCT), expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.UNDEFINED), expression = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw AssemblyError("weird call")
|
throw AssemblyError("weird call")
|
||||||
@ -179,7 +173,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
val dt = value.inferType(program)
|
val dt = value.inferType(program)
|
||||||
if(!dt.isKnown)
|
if(!dt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.STRUCT), expression = value)
|
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.UNDEFINED), expression = value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,12 +201,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
internal class AsmAssignment(val source: AsmAssignSource,
|
internal class AsmAssignment(val source: AsmAssignSource,
|
||||||
val target: AsmAssignTarget,
|
val target: AsmAssignTarget,
|
||||||
val isAugmentable: Boolean,
|
val isAugmentable: Boolean,
|
||||||
|
memsizer: IMemSizer,
|
||||||
val position: Position) {
|
val position: Position) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
||||||
require(ICompilationTarget.instance.memorySize(source.datatype) <= ICompilationTarget.instance.memorySize(target.datatype)) {
|
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||||
"source storage size must be less or equal to target datatype storage size"
|
"source storage size must be less or equal to target datatype storage size"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
||||||
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
|
||||||
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
|
val assign = AsmAssignment(source, target, assignment.isAugmentable, program.memsizer, assignment.position)
|
||||||
target.origAssign = assign
|
target.origAssign = assign
|
||||||
|
|
||||||
if(assign.isAugmentable)
|
if(assign.isAugmentable)
|
||||||
@ -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()!! * asmgen.compTarget.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")
|
||||||
@ -114,7 +116,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
SourceStorageKind.MEMORY -> {
|
SourceStorageKind.MEMORY -> {
|
||||||
fun assignViaExprEval(expression: Expression) {
|
fun assignViaExprEval(expression: Expression) {
|
||||||
assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
|
assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
||||||
else
|
else
|
||||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||||
@ -143,7 +145,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
when(val value = assign.source.expression!!) {
|
when(val value = assign.source.expression!!) {
|
||||||
is AddressOf -> {
|
is AddressOf -> {
|
||||||
val sourceName = value.identifier.firstStructVarName(program) ?: asmgen.asmVariableName(value.identifier)
|
val sourceName = asmgen.asmVariableName(value.identifier)
|
||||||
assignAddressOf(assign.target, sourceName)
|
assignAddressOf(assign.target, sourceName)
|
||||||
}
|
}
|
||||||
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
||||||
@ -215,7 +217,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val returntype = builtinFunctionReturnType(sub.name, value.args, program)
|
val returntype = builtinFunctionReturnType(sub.name, value.args, program)
|
||||||
if(!returntype.isKnown)
|
if(!returntype.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
when(returntype.typeOrElse(DataType.STRUCT)) {
|
when(returntype.typeOrElse(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
@ -297,7 +299,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val valueIDt = value.inferType(program)
|
val valueIDt = value.inferType(program)
|
||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
if(valueDt==targetDt)
|
if(valueDt==targetDt)
|
||||||
throw AssemblyError("type cast to identical dt should have been removed")
|
throw AssemblyError("type cast to identical dt should have been removed")
|
||||||
|
|
||||||
@ -319,7 +321,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
fun assignViaExprEval(addressExpression: Expression) {
|
fun assignViaExprEval(addressExpression: Expression) {
|
||||||
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
||||||
else
|
else
|
||||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||||
@ -356,8 +358,8 @@ 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.UNDEFINED)
|
||||||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
||||||
|
|
||||||
when (valueDt) {
|
when (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)
|
||||||
@ -441,7 +443,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
|
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
|
||||||
lsb.linkParents(value.parent)
|
lsb.linkParents(value.parent)
|
||||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||||
val assign = AsmAssignment(src, target, false, value.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +475,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
@ -496,7 +498,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,13 +587,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
asmgen.out(
|
||||||
|
" st${
|
||||||
|
regs.toString().lowercase()
|
||||||
|
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(
|
||||||
|
" st${
|
||||||
|
regs.toString().lowercase()
|
||||||
|
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
@ -617,13 +621,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
asmgen.out(
|
||||||
|
" st${
|
||||||
|
regs.toString().lowercase()
|
||||||
|
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(
|
||||||
|
" st${
|
||||||
|
regs.toString().lowercase()
|
||||||
|
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
@ -655,7 +665,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.WORD, DataType.UWORD -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
@ -683,7 +693,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
when(targetDt) {
|
when(targetDt) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {
|
DataType.BYTE, DataType.UBYTE -> {
|
||||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||||
}
|
}
|
||||||
DataType.WORD, DataType.UWORD -> {
|
DataType.WORD, DataType.UWORD -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
@ -708,11 +718,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -762,7 +768,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if(target.constArrayIndexValue!=null) {
|
if(target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
||||||
@ -828,12 +834,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
||||||
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
inx
|
inx
|
||||||
lda P8ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #0
|
lda #0
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't assign byte from stack to register pair XY")
|
else -> throw AssemblyError("can't assign byte from stack to register pair XY")
|
||||||
@ -845,12 +852,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
||||||
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
inx
|
inx
|
||||||
lda P8ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda P8ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't assign word to single byte register")
|
else -> throw AssemblyError("can't assign word to single byte register")
|
||||||
@ -894,11 +902,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName")
|
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName")
|
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda #<$sourceName
|
lda #<$sourceName
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #>$sourceName
|
lda #>$sourceName
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't load address in a single 8-bit register")
|
else -> throw AssemblyError("can't load address in a single 8-bit register")
|
||||||
@ -968,7 +977,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
target.array!!
|
target.array!!
|
||||||
if(target.constArrayIndexValue!=null) {
|
if(target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||||
when(target.datatype) {
|
when(target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
@ -1035,11 +1044,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda $sourceName+1
|
lda $sourceName+1
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||||
@ -1073,11 +1083,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 +1119,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 +1166,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")
|
||||||
@ -1191,7 +1201,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
if (target.constArrayIndexValue!=null) {
|
if (target.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1209,11 +1219,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #0
|
lda #0
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
@ -1242,11 +1253,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!!) {
|
||||||
@ -1295,7 +1315,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(wordtarget.kind) {
|
when(wordtarget.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
|
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||||
@ -1304,7 +1324,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
if (wordtarget.constArrayIndexValue!=null) {
|
if (wordtarget.constArrayIndexValue!=null) {
|
||||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
||||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
@ -1329,7 +1349,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||||
@ -1339,12 +1359,20 @@ 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
|
||||||
require(target.datatype in ByteDatatypes)
|
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)
|
||||||
|
} else {
|
||||||
|
require(target.datatype in ByteDatatypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
|
asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> {
|
TargetStorageKind.MEMORY -> {
|
||||||
when(register) {
|
when(register) {
|
||||||
@ -1368,7 +1396,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 -> {
|
||||||
@ -1383,7 +1412,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
// only assign a single byte to the virtual register's Lsb
|
// only assign a single byte to the virtual register's Lsb
|
||||||
asmgen.out(" sta cx16.${target.register.toString().toLowerCase()}")
|
asmgen.out(" sta cx16.${target.register.toString().lowercase()}")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1397,7 +1426,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
// only assign a single byte to the virtual register's Lsb
|
// only assign a single byte to the virtual register's Lsb
|
||||||
asmgen.out(" stx cx16.${target.register.toString().toLowerCase()}")
|
asmgen.out(" stx cx16.${target.register.toString().lowercase()}")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1411,7 +1440,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
// only assign a single byte to the virtual register's Lsb
|
// only assign a single byte to the virtual register's Lsb
|
||||||
asmgen.out(" sty cx16.${target.register.toString().toLowerCase()}")
|
asmgen.out(" sty cx16.${target.register.toString().lowercase()}")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1501,9 +1530,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AX -> { }
|
RegisterOrPair.AX -> { }
|
||||||
RegisterOrPair.XY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG | tax") }
|
RegisterOrPair.XY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG | tax") }
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
"""
|
||||||
stx cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
|
stx cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||||
@ -1513,9 +1543,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AX -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
RegisterOrPair.AX -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||||
RegisterOrPair.XY -> { asmgen.out(" tax") }
|
RegisterOrPair.XY -> { asmgen.out(" tax") }
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
"""
|
||||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
|
sty cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||||
@ -1525,9 +1556,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AX -> { asmgen.out(" txa | sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
RegisterOrPair.AX -> { asmgen.out(" txa | sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||||
RegisterOrPair.XY -> { }
|
RegisterOrPair.XY -> { }
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
stx cx16.${target.register.toString().toLowerCase()}
|
"""
|
||||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
stx cx16.${target.register.toString().lowercase()}
|
||||||
|
sty cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||||
@ -1571,7 +1603,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
||||||
if(word==0 && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
if(word==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
// optimize setting zero value for this processor
|
// optimize setting zero value for this processor
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -1594,7 +1626,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AY -> asmgen.out(" lda #0 | tay")
|
RegisterOrPair.AY -> asmgen.out(" lda #0 | tay")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
asmgen.out(
|
||||||
|
" stz cx16.${
|
||||||
|
target.register.toString().lowercase()
|
||||||
|
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid register for word value")
|
else -> throw AssemblyError("invalid register for word value")
|
||||||
}
|
}
|
||||||
@ -1644,11 +1679,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.AY -> asmgen.out(" ldy #>${word.toHex()} | lda #<${word.toHex()}")
|
RegisterOrPair.AY -> asmgen.out(" ldy #>${word.toHex()} | lda #<${word.toHex()}")
|
||||||
RegisterOrPair.XY -> asmgen.out(" ldy #>${word.toHex()} | ldx #<${word.toHex()}")
|
RegisterOrPair.XY -> asmgen.out(" ldy #>${word.toHex()} | ldx #<${word.toHex()}")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda #<${word.toHex()}
|
lda #<${word.toHex()}
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #>${word.toHex()}
|
lda #>${word.toHex()}
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid register for word value")
|
else -> throw AssemblyError("invalid register for word value")
|
||||||
@ -1666,7 +1702,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
|
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
|
||||||
if(byte==0.toShort() && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
if(byte==0.toShort() && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
// optimize setting zero value for this cpu
|
// optimize setting zero value for this cpu
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -1695,7 +1731,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
asmgen.out(
|
||||||
|
" stz cx16.${
|
||||||
|
target.register.toString().lowercase()
|
||||||
|
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1735,11 +1774,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx #${byte.toHex()}")
|
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx #${byte.toHex()}")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
|
asmgen.out(
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
" lda #${byte.toHex()} | sta cx16.${
|
||||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
|
target.register.toString().lowercase()
|
||||||
|
}")
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1\n")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n")
|
asmgen.out(
|
||||||
|
" lda #0 | sta cx16.${
|
||||||
|
target.register.toString().lowercase()
|
||||||
|
}+1\n")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
}
|
}
|
||||||
@ -1757,7 +1802,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
// optimized case for float zero
|
// optimized case for float zero
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
stz ${target.asmVarname}
|
stz ${target.asmVarname}
|
||||||
stz ${target.asmVarname}+1
|
stz ${target.asmVarname}+1
|
||||||
@ -1776,9 +1821,10 @@ 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()!! * asmgen.compTarget.memorySize(DataType.FLOAT)
|
if (constIndex!=null) {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
stz ${target.asmVarname}+$indexValue
|
stz ${target.asmVarname}+$indexValue
|
||||||
stz ${target.asmVarname}+$indexValue+1
|
stz ${target.asmVarname}+$indexValue+1
|
||||||
@ -1796,7 +1842,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 +1887,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()!! * asmgen.compTarget.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 +1903,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
|
||||||
@ -1913,11 +1960,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
|
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
|
"""
|
||||||
lda ${address.toHex()}
|
lda ${address.toHex()}
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #0
|
lda #0
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
@ -1953,10 +2001,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
|
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
|
||||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
asmgen.out("""
|
asmgen.out(
|
||||||
sta cx16.${target.register.toString().toLowerCase()}
|
"""
|
||||||
|
sta cx16.${target.register.toString().lowercase()}
|
||||||
lda #0
|
lda #0
|
||||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
sta cx16.${target.register.toString().lowercase()}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird register")
|
else -> throw AssemblyError("weird register")
|
||||||
@ -1975,7 +2024,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(wordtarget.kind) {
|
when(wordtarget.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
|
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||||
@ -1991,7 +2040,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
|
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||||
@ -2003,7 +2052,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.loadByteFromPointerIntoA(identifier)
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
asmgen.out(" sta ${wordtarget.asmVarname}")
|
asmgen.out(" sta ${wordtarget.asmVarname}")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||||
@ -2023,7 +2072,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
asmgen.loadByteFromPointerIntoA(identifier)
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
asmgen.out(" sta P8ESTACK_LO,x")
|
asmgen.out(" sta P8ESTACK_LO,x")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||||
@ -2041,7 +2090,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(addressExpr) {
|
when(addressExpr) {
|
||||||
is NumericLiteralValue, is IdentifierReference -> {
|
is NumericLiteralValue, is IdentifierReference -> {
|
||||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||||
else
|
else
|
||||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||||
@ -2051,7 +2100,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" pha")
|
asmgen.out(" pha")
|
||||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||||
asmgen.out(" pla")
|
asmgen.out(" pla")
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||||
else
|
else
|
||||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||||
@ -2063,7 +2112,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val sourceName = asmgen.asmVariableName(pointervar)
|
val sourceName = asmgen.asmVariableName(pointervar)
|
||||||
val vardecl = pointervar.targetVarDecl(program)!!
|
val vardecl = pointervar.targetVarDecl(program)!!
|
||||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||||
if (asmgen.isZpVar(scopedName)) {
|
if (asmgen.isZpVar(scopedName)) {
|
||||||
// pointervar is already in the zero page, no need to copy
|
// pointervar is already in the zero page, no need to copy
|
||||||
asmgen.out(" sta ($sourceName)")
|
asmgen.out(" sta ($sourceName)")
|
||||||
@ -2109,21 +2158,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
|
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, expr.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
|
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, false, expr.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
||||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, false, Position.DUMMY)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,14 +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.Cx16Target
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
||||||
import prog8.compiler.target.c64.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)
|
||||||
@ -25,7 +25,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val itype = value.inferType(program)
|
val itype = value.inferType(program)
|
||||||
if(!itype.isKnown)
|
if(!itype.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val type = itype.typeOrElse(DataType.STRUCT)
|
val type = itype.typeOrElse(DataType.UNDEFINED)
|
||||||
when (value.operator) {
|
when (value.operator) {
|
||||||
"+" -> {}
|
"+" -> {}
|
||||||
"-" -> inplaceNegate(assign.target, type)
|
"-" -> inplaceNegate(assign.target, type)
|
||||||
@ -110,7 +110,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val ident = value as? IdentifierReference
|
val ident = value as? IdentifierReference
|
||||||
val memread = value as? DirectMemoryRead
|
val memread = value as? DirectMemoryRead
|
||||||
|
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
when (target.datatype) {
|
when (target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
@ -160,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)
|
||||||
@ -199,10 +199,12 @@ 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()*asmgen.compTarget.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 {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
||||||
@ -242,22 +244,22 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
indexVar!=null -> {
|
indexVar!=null -> {
|
||||||
when(target.datatype) {
|
when (target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignFAC1float(target)
|
assignmentAsmGen.assignFAC1float(target)
|
||||||
}
|
}
|
||||||
@ -268,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +280,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val childIDt = value.expression.inferType(program)
|
val childIDt = value.expression.inferType(program)
|
||||||
if(!childIDt.isKnown)
|
if(!childIDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val childDt = childIDt.typeOrElse(DataType.STRUCT)
|
val childDt = childIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
||||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||||
// (works for integer types, not for float.)
|
// (works for integer types, not for float.)
|
||||||
@ -320,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)
|
||||||
@ -361,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)
|
||||||
@ -464,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,7 +628,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if(value>=8) {
|
if(value>=8) {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name")
|
asmgen.out(" stz $name")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name")
|
asmgen.out(" lda #0 | sta $name")
|
||||||
@ -642,7 +639,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
if(value>0) {
|
if(value>0) {
|
||||||
if (dt == DataType.UBYTE) {
|
if (dt == DataType.UBYTE) {
|
||||||
if(value>=8) {
|
if(value>=8) {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name")
|
asmgen.out(" stz $name")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name")
|
asmgen.out(" lda #0 | sta $name")
|
||||||
@ -670,13 +667,12 @@ 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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||||
when(operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -692,8 +688,20 @@ 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
|
|
||||||
}
|
}
|
||||||
|
"|", "or" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" ora $name | sta $name")
|
||||||
|
}
|
||||||
|
"&", "and" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" and $name | sta $name")
|
||||||
|
}
|
||||||
|
"^", "xor" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" eor $name | sta $name")
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
@ -701,7 +709,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||||
when(operator) {
|
when (operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -723,8 +731,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bcc +
|
bcc +
|
||||||
dec $name+1
|
dec $name+1
|
||||||
+""")
|
+""")
|
||||||
// TODO: tuned code for more operators
|
|
||||||
}
|
}
|
||||||
|
"|", "or" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" ora $name | sta $name")
|
||||||
|
}
|
||||||
|
"&", "and" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" and $name | sta $name")
|
||||||
|
if(dt in WordDatatypes) {
|
||||||
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
|
asmgen.out(" stz $name+1")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"^", "xor" -> {
|
||||||
|
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||||
|
asmgen.out(" eor $name | sta $name")
|
||||||
|
}
|
||||||
|
// 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)
|
||||||
}
|
}
|
||||||
@ -857,14 +883,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"<<" -> {
|
"<<" -> {
|
||||||
when {
|
when {
|
||||||
value>=16 -> {
|
value>=16 -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name | stz $name+1")
|
asmgen.out(" stz $name | stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
}
|
}
|
||||||
value==8 -> {
|
value==8 -> {
|
||||||
asmgen.out(" lda $name | sta $name+1")
|
asmgen.out(" lda $name | sta $name+1")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name")
|
asmgen.out(" stz $name")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name")
|
asmgen.out(" lda #0 | sta $name")
|
||||||
@ -884,14 +910,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
if(dt==DataType.UWORD) {
|
if(dt==DataType.UWORD) {
|
||||||
when {
|
when {
|
||||||
value>=16 -> {
|
value>=16 -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name | stz $name+1")
|
asmgen.out(" stz $name | stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
}
|
}
|
||||||
value==8 -> {
|
value==8 -> {
|
||||||
asmgen.out(" lda $name+1 | sta $name")
|
asmgen.out(" lda $name+1 | sta $name")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name+1")
|
asmgen.out(" stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
@ -940,13 +966,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> {
|
"&", "and" -> {
|
||||||
when {
|
when {
|
||||||
value == 0 -> {
|
value == 0 -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name | stz $name+1")
|
asmgen.out(" stz $name | stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
}
|
}
|
||||||
value and 255 == 0 -> {
|
value and 255 == 0 -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name")
|
asmgen.out(" stz $name")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name")
|
asmgen.out(" lda #0 | sta $name")
|
||||||
@ -954,7 +980,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
value < 0x0100 -> {
|
value < 0x0100 -> {
|
||||||
asmgen.out(" lda $name | and #$value | sta $name")
|
asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name+1")
|
asmgen.out(" stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
@ -978,7 +1004,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1041,7 +1066,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||||
@ -1054,8 +1079,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
|
||||||
@ -1092,7 +1158,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> {
|
"&", "and" -> {
|
||||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name+1")
|
asmgen.out(" stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
@ -1100,7 +1166,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1174,7 +1239,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1191,7 +1255,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
val valueiDt = value.inferType(program)
|
val valueiDt = value.inferType(program)
|
||||||
if(!valueiDt.isKnown)
|
if(!valueiDt.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueiDt.typeOrElse(DataType.UNDEFINED)
|
||||||
|
|
||||||
fun multiplyVarByWordInAY() {
|
fun multiplyVarByWordInAY() {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1240,7 +1304,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
when(valueDt) {
|
when (valueDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// the other variable is a BYTE type so optimize for that
|
// the other variable is a BYTE type so optimize for that
|
||||||
when (operator) {
|
when (operator) {
|
||||||
@ -1351,7 +1415,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz $name+1")
|
asmgen.out(" stz $name+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
@ -1365,7 +1429,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1406,7 +1469,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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1454,7 +1516,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("""
|
||||||
@ -1474,7 +1535,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
if(asmgen.compTarget is Cx16Target) {
|
if(asmgen.haveFPWR()) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$name
|
||||||
|
ldy #>$name
|
||||||
|
jsr floats.CONUPK
|
||||||
|
lda #<$otherName
|
||||||
|
ldy #>$otherName
|
||||||
|
jsr floats.FPWR
|
||||||
|
""")
|
||||||
|
} else
|
||||||
// cx16 doesn't have FPWR() only FPWRT()
|
// cx16 doesn't have FPWR() only FPWRT()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
@ -1485,15 +1555,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.MOVFM
|
jsr floats.MOVFM
|
||||||
jsr floats.FPWRT
|
jsr floats.FPWRT
|
||||||
""")
|
""")
|
||||||
} else
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$otherName
|
|
||||||
ldy #>$otherName
|
|
||||||
jsr floats.FPWR
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
"+" -> {
|
"+" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1535,7 +1596,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
|
||||||
@ -1552,7 +1612,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
if(asmgen.compTarget is Cx16Target) {
|
if(asmgen.haveFPWR()) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$name
|
||||||
|
ldy #>$name
|
||||||
|
jsr floats.CONUPK
|
||||||
|
lda #<$constValueName
|
||||||
|
ldy #>$constValueName
|
||||||
|
jsr floats.FPWR
|
||||||
|
""")
|
||||||
|
} else
|
||||||
// cx16 doesn't have FPWR() only FPWRT()
|
// cx16 doesn't have FPWR() only FPWRT()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$name
|
lda #<$name
|
||||||
@ -1563,15 +1632,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.MOVFM
|
jsr floats.MOVFM
|
||||||
jsr floats.FPWRT
|
jsr floats.FPWRT
|
||||||
""")
|
""")
|
||||||
} else
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$name
|
|
||||||
ldy #>$name
|
|
||||||
jsr floats.CONUPK
|
|
||||||
lda #<$constValueName
|
|
||||||
ldy #>$constValueName
|
|
||||||
jsr floats.FPWR
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (value == 0.0)
|
if (value == 0.0)
|
||||||
@ -1620,7 +1680,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
|
||||||
@ -1642,9 +1701,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
when (outerCastDt) {
|
when (outerCastDt) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz ${target.asmVarname}+1")
|
asmgen.out(" stz ${target.asmVarname}+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
|
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
|
||||||
@ -1654,7 +1713,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta ${target.asmVarname},y")
|
asmgen.out(" lda #0 | sta ${target.asmVarname},y")
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||||
else
|
else
|
||||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||||
@ -1686,7 +1745,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
private fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${target.asmVarname}
|
lda ${target.asmVarname}
|
||||||
@ -1730,13 +1789,13 @@ 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 -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${target.asmVarname}
|
lda ${target.asmVarname}
|
||||||
@ -1749,9 +1808,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")
|
||||||
@ -1761,7 +1820,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
private fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${target.asmVarname}
|
lda ${target.asmVarname}
|
||||||
@ -1796,13 +1855,13 @@ 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 -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda ${target.asmVarname}
|
lda ${target.asmVarname}
|
||||||
@ -1813,9 +1872,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")
|
||||||
@ -1834,13 +1893,13 @@ 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 -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #0
|
lda #0
|
||||||
@ -1851,14 +1910,14 @@ 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 -> {
|
||||||
when(target.kind) {
|
when (target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
// simply flip the sign bit in the float
|
// simply flip the sign bit in the float
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1867,8 +1926,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,12 +1,9 @@
|
|||||||
package prog8.compiler.target.cx16
|
package prog8.compiler.target.cx16
|
||||||
|
|
||||||
import prog8.ast.IStringEncoding
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.IMachineDefinition
|
import prog8.compiler.target.IMachineDefinition
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.parser.ModuleImporter
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
internal object CX16MachineDefinition: IMachineDefinition {
|
internal object CX16MachineDefinition: IMachineDefinition {
|
||||||
@ -28,16 +25,11 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
override lateinit var zeropage: Zeropage
|
override lateinit var zeropage: Zeropage
|
||||||
|
|
||||||
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||||
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
override fun importLibs(
|
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
compilerOptions: CompilationOptions,
|
listOf("syslib")
|
||||||
importer: ModuleImporter,
|
else
|
||||||
program: Program,
|
emptyList()
|
||||||
encoder: IStringEncoding,
|
|
||||||
compilationTargetName: String)
|
|
||||||
{
|
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
|
||||||
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(programName: String) {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
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.BinaryExpression
|
||||||
|
import prog8.ast.expressions.augmentAssignmentOperators
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
@ -10,8 +12,7 @@ import prog8.ast.walk.IAstModification
|
|||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
internal class BinExprSplitter(private val program: Program) : 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 +58,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,12 +77,9 @@ 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)
|
||||||
ICompilationTarget.instance.isInRegularRAM(target, program)
|
compTarget.isInRegularRAM(target, program)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
|
||||||
|
@ -1,139 +1,71 @@
|
|||||||
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.compiler.ErrorReporter
|
|
||||||
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.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.loadAsmIncludeFile
|
import prog8.compiler.IErrorReporter
|
||||||
|
|
||||||
private val alwaysKeepSubroutines = setOf(
|
|
||||||
Pair("main", "start"),
|
|
||||||
Pair("irq", "irq")
|
|
||||||
)
|
|
||||||
|
|
||||||
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) : 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)
|
private val usedBlocks: Set<Block> by lazy {
|
||||||
if (it is INameScope)
|
val blocksFromSubroutines = usedSubroutines.map { it.definingBlock() }
|
||||||
findSubs(it)
|
val blocksFromLibraries = program.allBlocks().filter { it.isInLibrary }
|
||||||
|
val used = mutableSetOf<Block>()
|
||||||
|
|
||||||
|
allIdentifiersAndTargets.forEach {
|
||||||
|
if(it.key.first.definingBlock() in blocksFromSubroutines) {
|
||||||
|
val target = it.value.definingBlock()
|
||||||
|
used.add(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
findSubs(scope)
|
|
||||||
|
used + blocksFromLibraries + program.entrypoint().definingBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
private val usedModules: Set<Module> by lazy {
|
||||||
super.visit(program)
|
usedBlocks.map { it.definingModule() }.toSet()
|
||||||
|
|
||||||
program.modules.forEach {
|
|
||||||
it.importedBy.clear()
|
|
||||||
it.imports.clear()
|
|
||||||
|
|
||||||
it.importedBy.addAll(importedBy.getValue(it))
|
|
||||||
it.imports.addAll(imports.getValue(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
val rootmodule = program.modules.first()
|
|
||||||
rootmodule.importedBy.add(rootmodule) // don't discard root module
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
|
||||||
if (block.definingModule().isLibraryModule) {
|
|
||||||
// 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 = loadAsmIncludeFile(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,77 +75,44 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(addressOf: AddressOf) {
|
||||||
|
val otherSub = addressOf.identifier.targetSubroutine(program)
|
||||||
|
if(otherSub!=null) {
|
||||||
|
addressOf.definingSubroutine()?.let { thisSub ->
|
||||||
|
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||||
|
calledBy[otherSub] = calledBy.getValue(otherSub) + thisSub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visit(addressOf)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(jump: Jump) {
|
override fun visit(jump: Jump) {
|
||||||
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?) {
|
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||||
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: ErrorReporter) {
|
|
||||||
val cycles = recursionCycles()
|
val cycles = recursionCycles()
|
||||||
if(cycles.any()) {
|
if(cycles.any()) {
|
||||||
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY)
|
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY)
|
||||||
@ -263,4 +162,39 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
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.autogeneratedDontRemove)
|
||||||
|
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 // Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them
|
||||||
|
}
|
||||||
|
|
||||||
|
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,28 +9,32 @@ 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 {
|
||||||
return when(operator) {
|
try {
|
||||||
"+" -> plus(left, right)
|
return when(operator) {
|
||||||
"-" -> minus(left, right)
|
"+" -> plus(left, right)
|
||||||
"*" -> multiply(left, right)
|
"-" -> minus(left, right)
|
||||||
"/" -> divide(left, right)
|
"*" -> multiply(left, right)
|
||||||
"%" -> remainder(left, right)
|
"/" -> divide(left, right)
|
||||||
"**" -> power(left, right)
|
"%" -> remainder(left, right)
|
||||||
"&" -> bitwiseand(left, right)
|
"**" -> power(left, right)
|
||||||
"|" -> bitwiseor(left, right)
|
"&" -> bitwiseand(left, right)
|
||||||
"^" -> bitwisexor(left, right)
|
"|" -> bitwiseor(left, right)
|
||||||
"and" -> logicaland(left, right)
|
"^" -> bitwisexor(left, right)
|
||||||
"or" -> logicalor(left, right)
|
"and" -> logicaland(left, right)
|
||||||
"xor" -> logicalxor(left, right)
|
"or" -> logicalor(left, right)
|
||||||
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
|
"xor" -> logicalxor(left, right)
|
||||||
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
|
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
|
||||||
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
|
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
|
||||||
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
|
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
|
||||||
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
|
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
|
||||||
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
|
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
|
||||||
"<<" -> shiftedleft(left, right)
|
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
|
||||||
">>" -> shiftedright(left, right)
|
"<<" -> shiftedleft(left, right)
|
||||||
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
">>" -> shiftedright(left, right)
|
||||||
|
else -> throw FatalAstException("const evaluation for invalid operator $operator")
|
||||||
|
}
|
||||||
|
} catch (ax: FatalAstException) {
|
||||||
|
throw ExpressionError(ax.message, left.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@ import prog8.compiler.target.ICompilationTarget
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program) : 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
|
||||||
@ -107,7 +106,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
// optimize various simple cases of ** :
|
// optimize various simple cases of ** :
|
||||||
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
||||||
// optimize 2 ** x into (1<<x) if both operands are integer.
|
// optimize 2 ** x into (1<<x) if both operands are integer.
|
||||||
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
|
val leftDt = leftconst.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
when (leftconst.number.toDouble()) {
|
when (leftconst.number.toDouble()) {
|
||||||
0.0 -> {
|
0.0 -> {
|
||||||
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
||||||
@ -122,11 +121,11 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
||||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||||
} else {
|
} else {
|
||||||
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
|
val rightDt = expr.right.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val targetDt =
|
val targetDt =
|
||||||
when (parent) {
|
when (parent) {
|
||||||
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
|
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||||
is VarDecl -> parent.datatype
|
is VarDecl -> parent.datatype
|
||||||
else -> leftDt
|
else -> leftDt
|
||||||
}
|
}
|
||||||
@ -188,7 +187,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
} else {
|
} else {
|
||||||
val arrayDt = array.guessDatatype(program)
|
val arrayDt = array.guessDatatype(program)
|
||||||
if (arrayDt.isKnown) {
|
if (arrayDt.isKnown) {
|
||||||
val newArray = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
val newArray = array.cast(arrayDt.typeOrElse(DataType.UNDEFINED))
|
||||||
if (newArray != null && newArray != array)
|
if (newArray != null && newArray != array)
|
||||||
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||||
}
|
}
|
||||||
@ -224,7 +223,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
range.step
|
range.step
|
||||||
}
|
}
|
||||||
|
|
||||||
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, ICompilationTarget.instance, range.position)
|
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, compTarget, range.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
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.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.ArrayIndex
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.ForLoop
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.ErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
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: ErrorReporter) : 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 {
|
||||||
@ -31,6 +28,28 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
|||||||
errors.err(x.message, x.position)
|
errors.err(x.message, x.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// move vardecl to the containing subroutine and add initialization assignment in its place if needed
|
||||||
|
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
val subroutine = decl.definingSubroutine() as? INameScope
|
||||||
|
if(subroutine!=null && subroutine!==parent) {
|
||||||
|
val declValue = decl.value
|
||||||
|
decl.value = null
|
||||||
|
decl.allowInitializeWithZero = false
|
||||||
|
return if (declValue == null) {
|
||||||
|
listOf(
|
||||||
|
IAstModification.Remove(decl, parent as INameScope),
|
||||||
|
IAstModification.InsertFirst(decl, subroutine)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
val assign = Assignment(target, declValue, decl.position)
|
||||||
|
listOf(
|
||||||
|
IAstModification.ReplaceNode(decl, assign, parent),
|
||||||
|
IAstModification.InsertFirst(decl, subroutine)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,8 +58,7 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
|||||||
// Replace all constant identifiers with their actual value,
|
// Replace all constant identifiers with their actual value,
|
||||||
// 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: ErrorReporter) : 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 +93,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 +111,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +172,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayToElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
}
|
}
|
||||||
@ -192,7 +197,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
if(rangeExpr==null && litval!=null) {
|
if(rangeExpr==null && litval!=null) {
|
||||||
// arraysize initializer is a single int, and we know the size.
|
// arraysize initializer is a single int, and we know the size.
|
||||||
val fillvalue = litval.number.toDouble()
|
val fillvalue = litval.number.toDouble()
|
||||||
if (fillvalue < ICompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > ICompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||||
errors.err("float value overflow", litval.position)
|
errors.err("float value overflow", litval.position)
|
||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
@ -204,7 +209,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// nothing to do for this type
|
// nothing to do for this type
|
||||||
// this includes strings and structs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>()
|
||||||
@ -135,8 +134,8 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
val rightDt = rightIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
|
|
||||||
if (expr.operator == "+" || expr.operator == "-"
|
if (expr.operator == "+" || expr.operator == "-"
|
||||||
&& leftVal == null && rightVal == null
|
&& leftVal == null && rightVal == null
|
||||||
@ -490,10 +489,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val idt = expr.inferType(program)
|
val idt = expr.inferType(program)
|
||||||
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.UNDEFINED), 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -514,7 +513,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
if (!leftIDt.isKnown)
|
if (!leftIDt.isKnown)
|
||||||
return null
|
return null
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
when (cv) {
|
when (cv) {
|
||||||
-1.0 -> {
|
-1.0 -> {
|
||||||
// '/' -> -left
|
// '/' -> -left
|
||||||
@ -591,14 +590,14 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
return expr2.left
|
return expr2.left
|
||||||
}
|
}
|
||||||
in powersOfTwo -> {
|
in powersOfTwo -> {
|
||||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
if (leftValue.inferType(program).isInteger()) {
|
||||||
// times a power of two => shift left
|
// times a power of two => shift left
|
||||||
val numshifts = log2(cv).toInt()
|
val numshifts = log2(cv).toInt()
|
||||||
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in negativePowersOfTwo -> {
|
in negativePowersOfTwo -> {
|
||||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
if (leftValue.inferType(program).isInteger()) {
|
||||||
// times a negative power of two => negate, then shift left
|
// times a negative power of two => negate, then shift left
|
||||||
val numshifts = log2(-cv).toInt()
|
val numshifts = log2(-cv).toInt()
|
||||||
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
@ -622,7 +621,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val targetIDt = expr.left.inferType(program)
|
val targetIDt = expr.left.inferType(program)
|
||||||
if(!targetIDt.isKnown)
|
if(!targetIDt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
when (val targetDt = targetIDt.typeOrElse(DataType.STRUCT)) {
|
when (val targetDt = targetIDt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||||
@ -657,7 +656,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
val idt = expr.left.inferType(program)
|
val idt = expr.left.inferType(program)
|
||||||
if(!idt.isKnown)
|
if(!idt.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
when (idt.typeOrElse(DataType.STRUCT)) {
|
when (idt.typeOrElse(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount >= 8) {
|
if (amount >= 8) {
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
@ -2,31 +2,32 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.compiler.ErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
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)
|
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)
|
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()
|
||||||
}
|
}
|
||||||
@ -34,13 +35,15 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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: ErrorReporter, functions: IBuiltinFunctions): Int {
|
internal fun Program.optimizeStatements(errors: IErrorReporter,
|
||||||
val optimizer = StatementOptimizer(this, errors, functions)
|
functions: IBuiltinFunctions,
|
||||||
|
compTarget: ICompilationTarget): Int {
|
||||||
|
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
@ -55,8 +58,8 @@ internal fun Program.simplifyExpressions() : Int {
|
|||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.splitBinaryExpressions() : Int {
|
internal fun Program.splitBinaryExpressions(compTarget: ICompilationTarget) : Int {
|
||||||
val opti = BinExprSplitter(this)
|
val opti = BinExprSplitter(this, compTarget)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -10,64 +10,27 @@ 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.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.ErrorReporter
|
import prog8.compiler.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
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: ErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val functions: IBuiltinFunctions
|
private val functions: IBuiltinFunctions,
|
||||||
) : AstWalker() {
|
private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val subsThatNeedReturnVariable = mutableSetOf<Triple<INameScope, DataType, Position>>()
|
||||||
private val callgraph = CallGraph(program)
|
|
||||||
|
|
||||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
|
||||||
if("force_output" !in block.options()) {
|
|
||||||
if (block.containsNoCodeNorVars()) {
|
|
||||||
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, 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) {
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,50 +43,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
|
||||||
// this is a C-64 specific optimization
|
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
|
||||||
val arg = functionCallStatement.args.single()
|
|
||||||
val stringVar: IdentifierReference?
|
|
||||||
stringVar = if(arg is AddressOf) {
|
|
||||||
arg.identifier
|
|
||||||
} else {
|
|
||||||
arg as? IdentifierReference
|
|
||||||
}
|
|
||||||
if(stringVar!=null) {
|
|
||||||
val vardecl = stringVar.targetVarDecl(program)!!
|
|
||||||
val string = vardecl.value as? StringLiteralValue
|
|
||||||
if(string!=null) {
|
|
||||||
val pos = functionCallStatement.position
|
|
||||||
if (string.value.length == 1) {
|
|
||||||
val firstCharEncoded = ICompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
|
||||||
val chrout = FunctionCallStatement(
|
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
|
||||||
functionCallStatement.void, pos
|
|
||||||
)
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
|
||||||
} else if (string.value.length == 2) {
|
|
||||||
val firstTwoCharsEncoded = ICompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
|
||||||
val chrout1 = FunctionCallStatement(
|
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
|
||||||
functionCallStatement.void, pos
|
|
||||||
)
|
|
||||||
val chrout2 = FunctionCallStatement(
|
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
|
||||||
functionCallStatement.void, pos
|
|
||||||
)
|
|
||||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
|
||||||
anonscope.statements.add(chrout1)
|
|
||||||
anonscope.statements.add(chrout2)
|
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||||
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
@ -212,7 +131,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
val size = sv.value.length
|
val size = sv.value.length
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
// loop over string of length 1 -> just assign the single character
|
// loop over string of length 1 -> just assign the single character
|
||||||
val character = ICompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0]
|
val character = compTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||||
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||||
@ -293,18 +212,6 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
|
||||||
// remove empty choices
|
|
||||||
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
|
|
||||||
override fun perform() {
|
|
||||||
whenStatement.choices.remove(choice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return whenStatement.choices
|
|
||||||
.filter { !it.statements.containsCodeOrVars() }
|
|
||||||
.map { ChoiceRemover(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
// if the jump is to the next statement, remove the jump
|
// if the jump is to the next statement, remove the jump
|
||||||
val scope = jump.definingScope()
|
val scope = jump.definingScope()
|
||||||
@ -384,7 +291,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
throw FatalAstException("can't infer type of assignment target")
|
throw FatalAstException("can't infer type of assignment target")
|
||||||
|
|
||||||
// optimize binary expressions a bit
|
// optimize binary expressions a bit
|
||||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
val targetDt = targetIDt.typeOrElse(DataType.UNDEFINED)
|
||||||
val bexpr=assignment.value as? BinaryExpression
|
val bexpr=assignment.value as? BinaryExpression
|
||||||
if(bexpr!=null) {
|
if(bexpr!=null) {
|
||||||
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||||
@ -402,7 +309,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))
|
||||||
}
|
}
|
||||||
@ -416,7 +323,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))
|
||||||
}
|
}
|
||||||
@ -444,21 +351,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)
|
||||||
@ -469,12 +372,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,9 +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.compiler.ErrorReporter
|
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
|
||||||
@ -11,40 +12,23 @@ 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
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() {
|
internal class UnusedCodeRemover(private val program: Program,
|
||||||
|
private val errors: IErrorReporter,
|
||||||
|
private val compTarget: ICompilationTarget): AstWalker() {
|
||||||
|
|
||||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
private val callgraph = CallGraph(program)
|
||||||
val callgraph = CallGraph(program)
|
|
||||||
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
|
||||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
noModifications
|
||||||
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)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
@ -68,7 +52,7 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
|||||||
|
|
||||||
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
||||||
when(val next = parent.nextSibling(stmt)) {
|
when(val next = parent.nextSibling(stmt)) {
|
||||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is StructDecl -> {}
|
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
||||||
else -> errors.warn("unreachable code", next.position)
|
else -> errors.warn("unreachable code", next.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,15 +63,60 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
|||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
||||||
|
if(decl.type==VarDeclType.VAR) {
|
||||||
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
|
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.definingBlock().isInLibrary) {
|
||||||
|
if (callgraph.unused(decl)) {
|
||||||
|
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>()
|
||||||
@ -96,7 +125,7 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
|||||||
val assign1 = stmtPairs[0] as? Assignment
|
val assign1 = stmtPairs[0] as? Assignment
|
||||||
val assign2 = stmtPairs[1] as? Assignment
|
val assign2 = stmtPairs[1] as? Assignment
|
||||||
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
||||||
if (assign1.target.isSameAs(assign2.target, program) && ICompilationTarget.instance.isInRegularRAM(assign1.target, program)) {
|
if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target, program)) {
|
||||||
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
||||||
// only remove the second assignment if its value is a simple expression!
|
// only remove the second assignment if its value is a simple expression!
|
||||||
when(assign2.value) {
|
when(assign2.value) {
|
||||||
|
@ -5,22 +5,22 @@ import org.hamcrest.Matchers.closeTo
|
|||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.*
|
||||||
import prog8.ast.Module
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.Program
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.toHex
|
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.cbm.Petscii
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||||
|
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
zp.allocate("", DataType.UBYTE, null, errors)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
@ -147,37 +147,37 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate("", DataType.FLOAT, null, errors)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||||
zp3.allocate("", DataType.FLOAT, null, errors)
|
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpModesWithFloats() {
|
fun testZpModesWithFloats() {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
|
||||||
}
|
}
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpDontuse() {
|
fun testZpDontuse() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
@ -187,19 +187,19 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFreeSpaces() {
|
fun testFreeSpaces() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
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))
|
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))
|
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))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
assertEquals(238, zp4.available())
|
assertEquals(238, zp4.available())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReservedSpace() {
|
fun testReservedSpace() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
assertEquals(238, zp1.available())
|
assertEquals(238, zp1.available())
|
||||||
assertTrue(50 in zp1.free)
|
assertTrue(50 in zp1.free)
|
||||||
assertTrue(100 in zp1.free)
|
assertTrue(100 in zp1.free)
|
||||||
@ -208,7 +208,7 @@ class TestC64Zeropage {
|
|||||||
assertTrue(200 in zp1.free)
|
assertTrue(200 in zp1.free)
|
||||||
assertTrue(255 in zp1.free)
|
assertTrue(255 in zp1.free)
|
||||||
assertTrue(199 in zp1.free)
|
assertTrue(199 in zp1.free)
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
|
||||||
assertEquals(139, zp2.available())
|
assertEquals(139, zp2.available())
|
||||||
assertFalse(50 in zp2.free)
|
assertFalse(50 in zp2.free)
|
||||||
assertFalse(100 in zp2.free)
|
assertFalse(100 in zp2.free)
|
||||||
@ -221,7 +221,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBasicsafeAllocation() {
|
fun testBasicsafeAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||||
assertEquals(18, zp.available())
|
assertEquals(18, zp.available())
|
||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
@ -244,7 +244,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFullAllocation() {
|
fun testFullAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
assertEquals(238, zp.available())
|
assertEquals(238, zp.available())
|
||||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||||
assertTrue(loc > 3)
|
assertTrue(loc > 3)
|
||||||
@ -274,7 +274,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||||
assertEquals(18, zp.available())
|
assertEquals(18, zp.available())
|
||||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
@ -293,7 +293,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReservedLocations() {
|
fun testReservedLocations() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||||
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -303,9 +303,30 @@ class TestC64Zeropage {
|
|||||||
class TestCx16Zeropage {
|
class TestCx16Zeropage {
|
||||||
@Test
|
@Test
|
||||||
fun testReservedLocations() {
|
fun testReservedLocations() {
|
||||||
val zp = CX16MachineDefinition.CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
|
||||||
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFreeSpaces() {
|
||||||
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
|
||||||
|
assertEquals(88, zp1.available())
|
||||||
|
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
|
||||||
|
assertEquals(175, zp3.available())
|
||||||
|
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||||
|
assertEquals(216, zp4.available())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReservedSpace() {
|
||||||
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||||
|
assertEquals(216, zp1.available())
|
||||||
|
assertTrue(0x22 in zp1.free)
|
||||||
|
assertTrue(0x80 in zp1.free)
|
||||||
|
assertTrue(0xff in zp1.free)
|
||||||
|
assertFalse(0x02 in zp1.free)
|
||||||
|
assertFalse(0x21 in zp1.free)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -326,8 +347,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) }
|
||||||
@ -341,7 +362,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)) }
|
||||||
@ -354,8 +375,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) }
|
||||||
@ -368,8 +389,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)) }
|
||||||
@ -408,16 +430,20 @@ class TestMemory {
|
|||||||
private class DummyFunctions: IBuiltinFunctions {
|
private class DummyFunctions: IBuiltinFunctions {
|
||||||
override val names: Set<String> = emptySet()
|
override val names: Set<String> = emptySet()
|
||||||
override val purefunctionNames: Set<String> = emptySet()
|
override val purefunctionNames: Set<String> = emptySet()
|
||||||
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? = null
|
override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? = null
|
||||||
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DummyMemsizer: IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType): Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_memory_addresses() {
|
fun testInValidRamC64_memory_addresses() {
|
||||||
|
|
||||||
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
|
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
|
||||||
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
|
|
||||||
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
|
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
|
||||||
@ -442,7 +468,7 @@ class TestMemory {
|
|||||||
|
|
||||||
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
|
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
|
||||||
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||||
|
|
||||||
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
|
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
|
||||||
@ -461,7 +487,7 @@ class TestMemory {
|
|||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_memory_identifiers() {
|
fun testInValidRamC64_memory_identifiers() {
|
||||||
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
|
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
|
||||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||||
|
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
|
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
|
||||||
@ -476,7 +502,7 @@ class TestMemory {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
|
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
|
||||||
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
@ -490,18 +516,18 @@ class TestMemory {
|
|||||||
fun testInValidRamC64_memory_expression() {
|
fun testInValidRamC64_memory_expression() {
|
||||||
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
|
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_variable() {
|
fun testInValidRamC64_variable() {
|
||||||
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
@ -509,12 +535,12 @@ class TestMemory {
|
|||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_memmap_variable() {
|
fun testInValidRamC64_memmap_variable() {
|
||||||
val address = 0x1000
|
val address = 0x1000
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
@ -522,25 +548,25 @@ class TestMemory {
|
|||||||
@Test
|
@Test
|
||||||
fun testNotInValidRamC64_memmap_variable() {
|
fun testNotInValidRamC64_memmap_variable() {
|
||||||
val address = 0xd020
|
val address = 0xd020
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_array() {
|
fun testInValidRamC64_array() {
|
||||||
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
@ -548,13 +574,13 @@ class TestMemory {
|
|||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_array_memmapped() {
|
fun testInValidRamC64_array_memmapped() {
|
||||||
val address = 0x1000
|
val address = 0x1000
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
@ -562,13 +588,13 @@ class TestMemory {
|
|||||||
@Test
|
@Test
|
||||||
fun testNotValidRamC64_array_memmapped() {
|
fun testNotValidRamC64_array_memmapped() {
|
||||||
val address = 0xe000
|
val address = 0xe000
|
||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||||
module.linkParents(ParentSentinel)
|
module.linkParents(ParentSentinel)
|
||||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||||
}
|
}
|
||||||
|
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")
|
@ -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.5.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCompatibility = 11
|
targetCompatibility = 11
|
||||||
@ -33,6 +33,7 @@ dependencies {
|
|||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
// verbose = true
|
// verbose = true
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||||
}
|
}
|
||||||
@ -41,6 +42,7 @@ compileKotlin {
|
|||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import prog8.ast.base.VarDeclType
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
||||||
@ -78,31 +79,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun datatypeString(dt: DataType): String {
|
private fun datatypeString(dt: DataType): String {
|
||||||
return when(dt) {
|
return when (dt) {
|
||||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
in NumericDatatypes -> dt.toString().lowercase()
|
||||||
DataType.STR -> dt.toString().toLowerCase()
|
DataType.STR -> dt.toString().lowercase()
|
||||||
DataType.ARRAY_UB -> "ubyte["
|
DataType.ARRAY_UB -> "ubyte["
|
||||||
DataType.ARRAY_B -> "byte["
|
DataType.ARRAY_B -> "byte["
|
||||||
DataType.ARRAY_UW -> "uword["
|
DataType.ARRAY_UW -> "uword["
|
||||||
DataType.ARRAY_W -> "word["
|
DataType.ARRAY_W -> "word["
|
||||||
DataType.ARRAY_F -> "float["
|
DataType.ARRAY_F -> "float["
|
||||||
DataType.STRUCT -> "" // the name of the struct is enough
|
|
||||||
else -> "?????"
|
else -> "?????"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structDecl: StructDecl) {
|
|
||||||
outputln("struct ${structDecl.name} {")
|
|
||||||
scopelevel++
|
|
||||||
for(decl in structDecl.statements) {
|
|
||||||
outputi("")
|
|
||||||
decl.accept(this)
|
|
||||||
output("\n")
|
|
||||||
}
|
|
||||||
scopelevel--
|
|
||||||
outputlni("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
|
|
||||||
// if the vardecl is a parameter of a subroutine, don't output it again
|
// if the vardecl is a parameter of a subroutine, don't output it again
|
||||||
@ -116,13 +104,9 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
VarDeclType.MEMORY -> output("&")
|
VarDeclType.MEMORY -> output("&")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.datatype==DataType.STRUCT && decl.struct!=null)
|
|
||||||
output(decl.struct!!.name)
|
|
||||||
|
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.indexNum?.accept(this)
|
decl.arraysize!!.indexExpr.accept(this)
|
||||||
decl.arraysize!!.indexVar?.accept(this)
|
|
||||||
}
|
}
|
||||||
if(decl.isArray)
|
if(decl.isArray)
|
||||||
output("]")
|
output("]")
|
||||||
@ -138,8 +122,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 +140,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())
|
||||||
@ -249,7 +236,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(branchStatement: BranchStatement) {
|
override fun visit(branchStatement: BranchStatement) {
|
||||||
output("if_${branchStatement.condition.toString().toLowerCase()} ")
|
output("if_${branchStatement.condition.toString().lowercase()} ")
|
||||||
branchStatement.truepart.accept(this)
|
branchStatement.truepart.accept(this)
|
||||||
if(branchStatement.elsepart.statements.isNotEmpty()) {
|
if(branchStatement.elsepart.statements.isNotEmpty()) {
|
||||||
output(" else ")
|
output(" else ")
|
||||||
@ -365,8 +352,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("]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package prog8.ast
|
package prog8.ast
|
||||||
|
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.InferredTypes
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
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
|
||||||
@ -127,18 +124,7 @@ interface INameScope {
|
|||||||
|
|
||||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
||||||
if(scopedName.size>1) {
|
if(scopedName.size>1) {
|
||||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
// a scoped name refers to a name in another module.
|
||||||
// try the struct first.
|
|
||||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
|
||||||
val struct = thing?.struct
|
|
||||||
if (struct != null) {
|
|
||||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
|
||||||
// return ref to the mangled name variable
|
|
||||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
|
||||||
return thing.definingScope().getLabelOrVariable(mangled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||||
for(module in localContext.definingModule().program.modules) {
|
for(module in localContext.definingModule().program.modules) {
|
||||||
var scope: INameScope? = module
|
var scope: INameScope? = module
|
||||||
@ -181,7 +167,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()
|
||||||
|
|
||||||
@ -244,10 +229,14 @@ interface IAssignable {
|
|||||||
// just a tag for now
|
// just a tag for now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IMemSizer {
|
||||||
|
fun memorySize(dt: DataType): Int
|
||||||
|
}
|
||||||
|
|
||||||
interface IBuiltinFunctions {
|
interface IBuiltinFunctions {
|
||||||
val names: Set<String>
|
val names: Set<String>
|
||||||
val purefunctionNames: Set<String>
|
val purefunctionNames: Set<String>
|
||||||
fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue?
|
fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue?
|
||||||
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
|
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,23 +244,56 @@ interface IBuiltinFunctions {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Program(val name: String, val modules: MutableList<Module>, val builtinFunctions: IBuiltinFunctions): Node {
|
class Program(val name: String,
|
||||||
|
val modules: MutableList<Module>,
|
||||||
|
val builtinFunctions: IBuiltinFunctions,
|
||||||
|
val memsizer: IMemSizer): Node {
|
||||||
val namespace = GlobalNamespace(modules, builtinFunctions.names)
|
val namespace = GlobalNamespace(modules, builtinFunctions.names)
|
||||||
|
|
||||||
|
val mainModule: Module
|
||||||
|
get() = modules.first { it.name!=internedStringsModuleName }
|
||||||
val definedLoadAddress: Int
|
val definedLoadAddress: Int
|
||||||
get() = modules.first().loadAddress
|
get() = mainModule.loadAddress
|
||||||
|
|
||||||
var actualLoadAddress: Int = 0
|
var actualLoadAddress: Int = 0
|
||||||
|
private val internedStrings = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||||
|
val internedStringsModuleName = "prog8_interned_strings"
|
||||||
|
|
||||||
fun entrypoint(): Subroutine? {
|
init {
|
||||||
|
// insert a container module for all interned strings later
|
||||||
|
if(modules.firstOrNull()?.name != internedStringsModuleName) {
|
||||||
|
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, true, Path.of(""))
|
||||||
|
modules.add(0, internedStringsModule)
|
||||||
|
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
||||||
|
internedStringsModule.statements.add(block)
|
||||||
|
internedStringsModule.linkParents(this)
|
||||||
|
internedStringsModule.program = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
val key = Pair(string.value, string.altEncoding)
|
||||||
|
val existing = internedStrings[key]
|
||||||
|
if(existing!=null)
|
||||||
|
return existing
|
||||||
|
|
||||||
|
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", string,
|
||||||
|
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||||
|
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
|
||||||
|
(internedStringsBlock as Block).statements.add(decl)
|
||||||
|
decl.linkParents(internedStringsBlock)
|
||||||
|
val scopedName = listOf(internedStringsModuleName, decl.name)
|
||||||
|
internedStrings[key] = scopedName
|
||||||
|
return scopedName
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||||
@ -303,8 +325,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
|
||||||
@ -353,20 +373,6 @@ class GlobalNamespace(val modules: List<Module>, private val builtinFunctionName
|
|||||||
return builtinPlaceholder
|
return builtinPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
if(scopedName.size>1) {
|
|
||||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
|
||||||
// try the struct first.
|
|
||||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
|
||||||
val struct = thing?.struct
|
|
||||||
if (struct != null) {
|
|
||||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
|
||||||
// return ref to the mangled name variable
|
|
||||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
|
||||||
return thing.definingScope().getLabelOrVariable(mangled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
// special case: the do....until statement can also look INSIDE the anonymous scope
|
||||||
if(localContext.parent.parent is UntilLoop) {
|
if(localContext.parent.parent is UntilLoop) {
|
||||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
||||||
@ -376,7 +382,7 @@ class GlobalNamespace(val modules: List<Module>, private val builtinFunctionName
|
|||||||
|
|
||||||
// lookup something from the module.
|
// lookup something from the module.
|
||||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||||
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||||
null -> null
|
null -> null
|
||||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
||||||
}
|
}
|
||||||
@ -392,10 +398,6 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// prefix for struct member variables
|
|
||||||
fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"
|
|
||||||
|
|
||||||
|
|
||||||
fun Number.toHex(): String {
|
fun Number.toHex(): String {
|
||||||
// 0..15 -> "0".."15"
|
// 0..15 -> "0".."15"
|
||||||
// 16..255 -> "$10".."$ff"
|
// 16..255 -> "$10".."$ff"
|
||||||
|
@ -13,6 +13,7 @@ import prog8.parser.prog8Parser
|
|||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
/***************** Antlr Extension methods to create AST ****************/
|
/***************** Antlr Extension methods to create AST ****************/
|
||||||
@ -45,7 +46,8 @@ private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean, encoding: IStri
|
|||||||
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst(encoding)
|
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst(encoding)
|
||||||
it.directive()!=null -> it.directive().toAst()
|
it.directive()!=null -> it.directive().toAst()
|
||||||
it.inlineasm()!=null -> it.inlineasm().toAst()
|
it.inlineasm()!=null -> it.inlineasm().toAst()
|
||||||
else -> throw FatalAstException("weird block statement $it")
|
it.labeldef()!=null -> it.labeldef().toAst()
|
||||||
|
else -> throw FatalAstException("weird block node $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
|
return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
|
||||||
@ -61,11 +63,10 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
|||||||
val vd = it.vardecl()
|
val vd = it.vardecl()
|
||||||
return VarDecl(
|
return VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(encoding),
|
vd.arrayindex()?.toAst(encoding),
|
||||||
vd.varname.text,
|
vd.varname.text,
|
||||||
null,
|
|
||||||
it.expression().toAst(encoding),
|
it.expression().toAst(encoding),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
@ -73,47 +74,15 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
structvarinitializer()?.let {
|
|
||||||
val vd = it.structvardecl()
|
|
||||||
return VarDecl(
|
|
||||||
VarDeclType.VAR,
|
|
||||||
DataType.STRUCT,
|
|
||||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
|
||||||
null,
|
|
||||||
vd.varname.text,
|
|
||||||
vd.structname.text,
|
|
||||||
it.expression().toAst(encoding),
|
|
||||||
isArray = false,
|
|
||||||
autogeneratedDontRemove = false,
|
|
||||||
position = it.toPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
structvardecl()?.let {
|
|
||||||
return VarDecl(
|
|
||||||
VarDeclType.VAR,
|
|
||||||
DataType.STRUCT,
|
|
||||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
|
||||||
null,
|
|
||||||
it.varname.text,
|
|
||||||
it.structname.text,
|
|
||||||
null,
|
|
||||||
isArray = false,
|
|
||||||
autogeneratedDontRemove = false,
|
|
||||||
position = it.toPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
constdecl()?.let {
|
constdecl()?.let {
|
||||||
val cvarinit = it.varinitializer()
|
val cvarinit = it.varinitializer()
|
||||||
val vd = cvarinit.vardecl()
|
val vd = cvarinit.vardecl()
|
||||||
return VarDecl(
|
return VarDecl(
|
||||||
VarDeclType.CONST,
|
VarDeclType.CONST,
|
||||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(encoding),
|
vd.arrayindex()?.toAst(encoding),
|
||||||
vd.varname.text,
|
vd.varname.text,
|
||||||
null,
|
|
||||||
cvarinit.expression().toAst(encoding),
|
cvarinit.expression().toAst(encoding),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
@ -126,11 +95,10 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
|||||||
val vd = mvarinit.vardecl()
|
val vd = mvarinit.vardecl()
|
||||||
return VarDecl(
|
return VarDecl(
|
||||||
VarDeclType.MEMORY,
|
VarDeclType.MEMORY,
|
||||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
vd.arrayindex()?.toAst(encoding),
|
vd.arrayindex()?.toAst(encoding),
|
||||||
vd.varname.text,
|
vd.varname.text,
|
||||||
null,
|
|
||||||
mvarinit.expression().toAst(encoding),
|
mvarinit.expression().toAst(encoding),
|
||||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
@ -138,12 +106,6 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
structdecl()?.let {
|
|
||||||
return StructDecl(it.identifier().text,
|
|
||||||
it.vardecl().map { vd->vd.toAst(encoding) }.toMutableList(),
|
|
||||||
toPosition())
|
|
||||||
}
|
|
||||||
|
|
||||||
throw FatalAstException("weird variable decl $this")
|
throw FatalAstException("weird variable decl $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +256,7 @@ private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
|||||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||||
= asmsub_param().map {
|
= asmsub_param().map {
|
||||||
val vardecl = it.vardecl()
|
val vardecl = it.vardecl()
|
||||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
val datatype = vardecl.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||||
val register = it.register().text
|
val register = it.register().text
|
||||||
var registerorpair: RegisterOrPair? = null
|
var registerorpair: RegisterOrPair? = null
|
||||||
var statusregister: Statusflag? = null
|
var statusregister: Statusflag? = null
|
||||||
@ -360,7 +322,7 @@ private fun prog8Parser.Sub_return_partContext.toAst(): List<DataType> {
|
|||||||
|
|
||||||
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||||
vardecl().map {
|
vardecl().map {
|
||||||
val datatype = it.datatype()?.toAst() ?: DataType.STRUCT
|
val datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||||
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +341,7 @@ private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
|
|||||||
return names.map { CpuRegister.valueOf(it) }.toSet()
|
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.uppercase())
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
private fun prog8Parser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
||||||
ArrayIndex(expression().toAst(encoding), toPosition())
|
ArrayIndex(expression().toAst(encoding), toPosition())
|
||||||
@ -575,7 +537,9 @@ private fun prog8Parser.Branch_stmtContext.toAst(encoding: IStringEncoding): Bra
|
|||||||
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(
|
||||||
|
text.substringAfter('_').uppercase()
|
||||||
|
)
|
||||||
|
|
||||||
private fun prog8Parser.ForloopContext.toAst(encoding: IStringEncoding): ForLoop {
|
private fun prog8Parser.ForloopContext.toAst(encoding: IStringEncoding): ForLoop {
|
||||||
val loopvar = identifier().toAst()
|
val loopvar = identifier().toAst()
|
||||||
@ -633,12 +597,11 @@ private fun prog8Parser.When_choiceContext.toAst(encoding: IStringEncoding): Whe
|
|||||||
private fun prog8Parser.VardeclContext.toAst(encoding: IStringEncoding): VarDecl {
|
private fun prog8Parser.VardeclContext.toAst(encoding: IStringEncoding): VarDecl {
|
||||||
return VarDecl(
|
return VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
datatype()?.toAst() ?: DataType.STRUCT,
|
datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||||
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||||
arrayindex()?.toAst(encoding),
|
arrayindex()?.toAst(encoding),
|
||||||
varname.text,
|
varname.text,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
ARRAYSIG() != null || arrayindex() != null,
|
ARRAYSIG() != null || arrayindex() != null,
|
||||||
false,
|
false,
|
||||||
toPosition()
|
toPosition()
|
||||||
|
@ -17,7 +17,7 @@ enum class DataType {
|
|||||||
ARRAY_UW, // pass by reference
|
ARRAY_UW, // pass by reference
|
||||||
ARRAY_W, // pass by reference
|
ARRAY_W, // pass by reference
|
||||||
ARRAY_F, // pass by reference
|
ARRAY_F, // pass by reference
|
||||||
STRUCT; // pass by reference
|
UNDEFINED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||||
@ -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 {
|
||||||
@ -134,8 +140,8 @@ val IterableDatatypes = setOf(
|
|||||||
DataType.ARRAY_F
|
DataType.ARRAY_F
|
||||||
)
|
)
|
||||||
val PassByValueDatatypes = NumericDatatypes
|
val PassByValueDatatypes = NumericDatatypes
|
||||||
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
|
val PassByReferenceDatatypes = IterableDatatypes
|
||||||
val ArrayElementTypes = mapOf(
|
val ArrayToElementTypes = mapOf(
|
||||||
DataType.STR to DataType.UBYTE,
|
DataType.STR to DataType.UBYTE,
|
||||||
DataType.ARRAY_B to DataType.BYTE,
|
DataType.ARRAY_B to DataType.BYTE,
|
||||||
DataType.ARRAY_UB to DataType.UBYTE,
|
DataType.ARRAY_UB to DataType.UBYTE,
|
||||||
@ -143,7 +149,7 @@ val ArrayElementTypes = mapOf(
|
|||||||
DataType.ARRAY_UW to DataType.UWORD,
|
DataType.ARRAY_UW to DataType.UWORD,
|
||||||
DataType.ARRAY_F to DataType.FLOAT
|
DataType.ARRAY_F to DataType.FLOAT
|
||||||
)
|
)
|
||||||
val ElementArrayTypes = mapOf(
|
val ElementToArrayTypes = mapOf(
|
||||||
DataType.BYTE to DataType.ARRAY_B,
|
DataType.BYTE to DataType.ARRAY_B,
|
||||||
DataType.UBYTE to DataType.ARRAY_UB,
|
DataType.UBYTE to DataType.ARRAY_UB,
|
||||||
DataType.WORD to DataType.ARRAY_W,
|
DataType.WORD to DataType.ARRAY_W,
|
||||||
|
@ -6,13 +6,15 @@ 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
|
||||||
|
|
||||||
|
|
||||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||||
|
val logicalOperators = setOf("and", "or", "xor", "not")
|
||||||
|
|
||||||
|
|
||||||
sealed class Expression: Node {
|
sealed class Expression: Node {
|
||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
@ -20,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)
|
||||||
|
|
||||||
@ -86,14 +89,14 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
return when(operator) {
|
return when(operator) {
|
||||||
"+" -> inferred
|
"+" -> inferred
|
||||||
"~", "not" -> {
|
"~", "not" -> {
|
||||||
when(inferred.typeOrElse(DataType.STRUCT)) {
|
when(inferred.typeOrElse(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
||||||
else -> inferred
|
else -> inferred
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
when(inferred.typeOrElse(DataType.STRUCT)) {
|
when(inferred.typeOrElse(DataType.UNDEFINED)) {
|
||||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.BYTE)
|
in ByteDatatypes -> InferredTypes.knownFor(DataType.BYTE)
|
||||||
in WordDatatypes -> InferredTypes.knownFor(DataType.WORD)
|
in WordDatatypes -> InferredTypes.knownFor(DataType.WORD)
|
||||||
else -> inferred
|
else -> inferred
|
||||||
@ -103,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)"
|
||||||
}
|
}
|
||||||
@ -131,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
|
||||||
|
|
||||||
@ -240,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
|
||||||
@ -259,7 +268,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
|||||||
if (target is VarDecl) {
|
if (target is VarDecl) {
|
||||||
return when (target.datatype) {
|
return when (target.datatype) {
|
||||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
|
in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(target.datatype))
|
||||||
else -> InferredTypes.unknown()
|
else -> InferredTypes.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,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() {
|
||||||
@ -279,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
|
||||||
@ -312,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
|
||||||
@ -333,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
|
||||||
@ -349,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
|
||||||
@ -356,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)
|
||||||
@ -478,19 +499,17 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
|
|
||||||
|
|
||||||
class StringLiteralValue(val value: String,
|
class StringLiteralValue(val value: String,
|
||||||
val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64
|
val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64
|
||||||
override val position: Position) : Expression() {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
val heapId = ++heapIdSequence
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
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")
|
||||||
}
|
}
|
||||||
@ -516,13 +535,13 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
override val position: Position) : Expression() {
|
override val position: Position) : Expression() {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
val heapId = ++heapIdSequence
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
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 }
|
||||||
@ -546,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 = ArrayToElementTypes.getValue(type.typeOrElse(DataType.UNDEFINED))
|
||||||
|
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.
|
||||||
@ -553,17 +580,17 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
if(forloop != null) {
|
if(forloop != null) {
|
||||||
val loopvarDt = forloop.loopVarDt(program)
|
val loopvarDt = forloop.loopVarDt(program)
|
||||||
if(loopvarDt.isKnown) {
|
if(loopvarDt.isKnown) {
|
||||||
return if(loopvarDt.typeOrElse(DataType.STRUCT) !in ElementArrayTypes)
|
return if(!loopvarDt.isArrayElement())
|
||||||
InferredTypes.InferredType.unknown()
|
InferredTypes.InferredType.unknown()
|
||||||
else
|
else
|
||||||
InferredTypes.InferredType.known(ElementArrayTypes.getValue(loopvarDt.typeOrElse(DataType.STRUCT)))
|
InferredTypes.InferredType.known(ElementToArrayTypes.getValue(loopvarDt.typeOrElse(DataType.UNDEFINED)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
||||||
val datatypesInArray = value.map { it.inferType(program) }
|
val datatypesInArray = value.map { it.inferType(program) }
|
||||||
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
||||||
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
val dts = datatypesInArray.map { it.typeOrElse(DataType.UNDEFINED) }
|
||||||
return when {
|
return when {
|
||||||
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||||
DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
@ -575,8 +602,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
DataType.ARRAY_W in dts ||
|
DataType.ARRAY_W in dts ||
|
||||||
DataType.ARRAY_UB in dts ||
|
DataType.ARRAY_UB in dts ||
|
||||||
DataType.ARRAY_B in dts ||
|
DataType.ARRAY_B in dts ||
|
||||||
DataType.ARRAY_F in dts ||
|
DataType.ARRAY_F in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||||
DataType.STRUCT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
|
||||||
else -> InferredTypes.InferredType.unknown()
|
else -> InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,7 +611,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
if(type.istype(targettype))
|
if(type.istype(targettype))
|
||||||
return this
|
return this
|
||||||
if(targettype in ArrayDatatypes) {
|
if(targettype in ArrayDatatypes) {
|
||||||
val elementType = ArrayElementTypes.getValue(targettype)
|
val elementType = ArrayToElementTypes.getValue(targettype)
|
||||||
val castArray = value.map{
|
val castArray = value.map{
|
||||||
val num = it as? NumericLiteralValue
|
val num = it as? NumericLiteralValue
|
||||||
if(num==null) {
|
if(num==null) {
|
||||||
@ -621,6 +647,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 {
|
||||||
@ -648,12 +676,12 @@ class RangeExpr(var from: Expression,
|
|||||||
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||||
else -> {
|
else -> {
|
||||||
val fdt = fromDt.typeOrElse(DataType.STRUCT)
|
val fdt = fromDt.typeOrElse(DataType.UNDEFINED)
|
||||||
val tdt = toDt.typeOrElse(DataType.STRUCT)
|
val tdt = toDt.typeOrElse(DataType.UNDEFINED)
|
||||||
if(fdt largerThan tdt)
|
if(fdt largerThan tdt)
|
||||||
InferredTypes.knownFor(ElementArrayTypes.getValue(fdt))
|
InferredTypes.knownFor(ElementToArrayTypes.getValue(fdt))
|
||||||
else
|
else
|
||||||
InferredTypes.knownFor(ElementArrayTypes.getValue(tdt))
|
InferredTypes.knownFor(ElementToArrayTypes.getValue(tdt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -707,20 +735,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) {
|
||||||
@ -756,38 +785,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
return when (val targetStmt = targetStatement(program)) {
|
return when (val targetStmt = targetStatement(program)) {
|
||||||
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||||
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
|
|
||||||
else -> InferredTypes.InferredType.unknown()
|
else -> InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
|
|
||||||
|
|
||||||
fun heapId(namespace: INameScope): Int {
|
|
||||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
|
||||||
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
|
||||||
return when (value) {
|
|
||||||
is IdentifierReference -> value.heapId(namespace)
|
|
||||||
is StringLiteralValue -> value.heapId
|
|
||||||
is ArrayLiteralValue -> value.heapId
|
|
||||||
else -> throw FatalAstException("requires a reference value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun firstStructVarName(program: Program): String? {
|
|
||||||
// take the name of the first struct member of the structvariable instead
|
|
||||||
// if it's just a regular variable, return null.
|
|
||||||
val struct = memberOfStruct(program) ?: return null
|
|
||||||
val decl = targetVarDecl(program)!!
|
|
||||||
if(decl.datatype!=DataType.STRUCT)
|
|
||||||
return null
|
|
||||||
|
|
||||||
val firstStructMember = struct.nameOfFirstMember()
|
|
||||||
// find the flattened var that belongs to this first struct member
|
|
||||||
val firstVarName = listOf(decl.name, firstStructMember)
|
|
||||||
val firstVar = definingScope().lookup(firstVarName, this) as VarDecl
|
|
||||||
return firstVar.name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCall(override var target: IdentifierReference,
|
class FunctionCall(override var target: IdentifierReference,
|
||||||
@ -801,6 +801,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
|
||||||
@ -818,7 +820,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
// lenghts of arrays and strings are constants that are determined at compile time!
|
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||||
if(target.nameInSource.size>1)
|
if(target.nameInSource.size>1)
|
||||||
return null
|
return null
|
||||||
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position, program.memsizer)
|
||||||
if(withDatatypeCheck) {
|
if(withDatatypeCheck) {
|
||||||
val resultDt = this.inferType(program)
|
val resultDt = this.inferType(program)
|
||||||
if(resultValue==null || resultDt istype resultValue.type)
|
if(resultValue==null || resultDt istype resultValue.type)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.ast.expressions
|
package prog8.ast.expressions
|
||||||
|
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ object InferredTypes {
|
|||||||
require(!(datatype!=null && (isUnknown || isVoid))) { "invalid combination of args" }
|
require(!(datatype!=null && (isUnknown || isVoid))) { "invalid combination of args" }
|
||||||
}
|
}
|
||||||
|
|
||||||
val isKnown = datatype!=null
|
val isKnown = datatype!=null && datatype!=DataType.UNDEFINED
|
||||||
fun typeOrElse(alternative: DataType) = if(isUnknown || isVoid) alternative else datatype!!
|
fun typeOrElse(alternative: DataType) = if(isUnknown || isVoid) alternative else datatype!!
|
||||||
infix fun istype(type: DataType): Boolean = if(isUnknown || isVoid) false else this.datatype==type
|
infix fun istype(type: DataType): Boolean = if(isUnknown || isVoid) false else this.datatype==type
|
||||||
|
|
||||||
@ -42,6 +42,17 @@ object InferredTypes {
|
|||||||
isKnown && (datatype!! isAssignableTo targetDt)
|
isKnown && (datatype!! isAssignableTo targetDt)
|
||||||
infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt)
|
infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt)
|
||||||
infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt)
|
infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt)
|
||||||
|
|
||||||
|
fun isBytes() = datatype in ByteDatatypes
|
||||||
|
fun isWords() = datatype in WordDatatypes
|
||||||
|
fun isInteger() = datatype in IntegerDatatypes
|
||||||
|
fun isNumeric() = datatype in NumericDatatypes
|
||||||
|
fun isArray() = datatype in ArrayDatatypes
|
||||||
|
fun isString() = datatype in StringlyDatatypes
|
||||||
|
fun isIterable() = datatype in IterableDatatypes
|
||||||
|
fun isPassByReference() = datatype in PassByReferenceDatatypes
|
||||||
|
fun isPassByValue() = datatype in PassByValueDatatypes
|
||||||
|
fun isArrayElement() = datatype in ElementToArrayTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
private val unknownInstance = InferredType.unknown()
|
private val unknownInstance = InferredType.unknown()
|
||||||
@ -57,8 +68,7 @@ object InferredTypes {
|
|||||||
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
||||||
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),
|
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),
|
||||||
DataType.ARRAY_W to InferredType.known(DataType.ARRAY_W),
|
DataType.ARRAY_W to InferredType.known(DataType.ARRAY_W),
|
||||||
DataType.ARRAY_F to InferredType.known(DataType.ARRAY_F),
|
DataType.ARRAY_F to InferredType.known(DataType.ARRAY_F)
|
||||||
DataType.STRUCT to InferredType.known(DataType.STRUCT)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun void() = voidInstance
|
fun void() = voidInstance
|
||||||
|
@ -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,22 +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?,
|
|
||||||
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)
|
|
||||||
private set
|
|
||||||
var structHasBeenFlattened = false // set later
|
|
||||||
private set
|
|
||||||
var allowInitializeWithZero = true
|
var allowInitializeWithZero = true
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
@ -176,22 +173,16 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
companion object {
|
companion object {
|
||||||
private var autoHeapValueSequenceNumber = 0
|
private var autoHeapValueSequenceNumber = 0
|
||||||
|
|
||||||
fun createAuto(string: StringLiteralValue): VarDecl {
|
|
||||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
|
||||||
return VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
|
|
||||||
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createAuto(array: ArrayLiteralValue): VarDecl {
|
fun createAuto(array: ArrayLiteralValue): VarDecl {
|
||||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||||
val arrayDt =
|
val arrayDt =
|
||||||
if(!array.type.isKnown)
|
if(!array.type.isKnown)
|
||||||
throw FatalAstException("unknown dt")
|
throw FatalAstException("unknown dt")
|
||||||
else
|
else
|
||||||
array.type.typeOrElse(DataType.STRUCT)
|
array.type.typeOrElse(DataType.UNDEFINED)
|
||||||
val declaredType = ArrayElementTypes.getValue(arrayDt)
|
val declaredType = ArrayToElementTypes.getValue(arrayDt)
|
||||||
val arraysize = ArrayIndex.forArray(array)
|
val arraysize = ArrayIndex.forArray(array)
|
||||||
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array,
|
||||||
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,16 +216,10 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
arraysize?.linkParents(this)
|
arraysize?.linkParents(this)
|
||||||
value?.linkParents(this)
|
value?.linkParents(this)
|
||||||
if(structName!=null) {
|
|
||||||
val structStmt = definingScope().lookup(listOf(structName), this)
|
|
||||||
if(structStmt!=null)
|
|
||||||
struct = definingScope().lookup(listOf(structName), this) as StructDecl
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
@ -243,7 +228,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zeroElementValue(): NumericLiteralValue {
|
fun zeroElementValue(): NumericLiteralValue {
|
||||||
@ -253,73 +238,30 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
throw IllegalArgumentException("attempt to get zero value for vardecl that shouldn't get it")
|
throw IllegalArgumentException("attempt to get zero value for vardecl that shouldn't get it")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun copy(): VarDecl {
|
||||||
val result = struct!!.statements.mapIndexed { index, statement ->
|
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, value, isArray, autogeneratedDontRemove, position)
|
||||||
val member = statement as VarDecl
|
c.allowInitializeWithZero = this.allowInitializeWithZero
|
||||||
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[index] else null
|
return c
|
||||||
VarDecl(
|
|
||||||
VarDeclType.VAR,
|
|
||||||
member.datatype,
|
|
||||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
|
||||||
member.arraysize,
|
|
||||||
mangledStructMemberName(name, member.name),
|
|
||||||
struct!!.name,
|
|
||||||
initvalue,
|
|
||||||
member.isArray,
|
|
||||||
true,
|
|
||||||
member.position
|
|
||||||
)
|
|
||||||
}.toMutableList<Statement>()
|
|
||||||
structHasBeenFlattened = true
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, 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 {
|
||||||
@ -329,29 +271,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() {
|
||||||
@ -469,9 +399,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")
|
||||||
}
|
}
|
||||||
@ -516,8 +447,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
|
||||||
@ -645,20 +577,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,
|
||||||
@ -674,7 +602,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)
|
||||||
@ -734,10 +662,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 {
|
||||||
@ -984,34 +911,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,
|
|
||||||
override var statements: MutableList<Statement>, // actually, only vardecls here
|
|
||||||
override val position: Position): Statement(), INameScope {
|
|
||||||
|
|
||||||
override lateinit var parent: Node
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
this.parent = parent
|
|
||||||
this.statements.forEach { it.linkParents(this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(replacement is Statement)
|
|
||||||
val idx = statements.indexOfFirst { it===node }
|
|
||||||
statements[idx] = replacement
|
|
||||||
replacement.parent = this
|
|
||||||
}
|
|
||||||
|
|
||||||
val numberOfElements: Int
|
|
||||||
get() = this.statements.size
|
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
|
||||||
|
|
||||||
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectMemoryWrite(var addressExpression: Expression, override val position: Position) : Node {
|
class DirectMemoryWrite(var addressExpression: Expression, override val position: Position) : Node {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
@ -1032,4 +931,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,115 @@ 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(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
|
||||||
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(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()
|
||||||
}
|
}
|
||||||
@ -212,7 +233,6 @@ abstract class AstWalker {
|
|||||||
track(before(decl, parent), decl, parent)
|
track(before(decl, parent), decl, parent)
|
||||||
decl.value?.accept(this, decl)
|
decl.value?.accept(this, decl)
|
||||||
decl.arraysize?.accept(this, decl)
|
decl.arraysize?.accept(this, decl)
|
||||||
decl.struct?.accept(this, decl)
|
|
||||||
track(after(decl, parent), decl, parent)
|
track(after(decl, parent), decl, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,11 +432,5 @@ abstract class AstWalker {
|
|||||||
whenChoice.statements.accept(this, whenChoice)
|
whenChoice.statements.accept(this, whenChoice)
|
||||||
track(after(whenChoice, parent), whenChoice, parent)
|
track(after(whenChoice, parent), whenChoice, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structDecl: StructDecl, parent: Node) {
|
|
||||||
track(before(structDecl, parent), structDecl, parent)
|
|
||||||
structDecl.statements.forEach { it.accept(this, structDecl) }
|
|
||||||
track(after(structDecl, parent), structDecl, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ interface IAstVisitor {
|
|||||||
fun visit(decl: VarDecl) {
|
fun visit(decl: VarDecl) {
|
||||||
decl.value?.accept(this)
|
decl.value?.accept(this)
|
||||||
decl.arraysize?.accept(this)
|
decl.arraysize?.accept(this)
|
||||||
decl.struct?.accept(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(subroutine: Subroutine) {
|
fun visit(subroutine: Subroutine) {
|
||||||
@ -170,8 +169,4 @@ interface IAstVisitor {
|
|||||||
whenChoice.values?.forEach { it.accept(this) }
|
whenChoice.values?.forEach { it.accept(this) }
|
||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(structDecl: StructDecl) {
|
|
||||||
structDecl.statements.forEach { it.accept(this) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,9 @@ fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
|||||||
internal fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
internal fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
||||||
|
|
||||||
|
|
||||||
class ModuleImporter {
|
class ModuleImporter(val program: Program, val encoder: IStringEncoding, val compilationTargetName: String, val libdirs: List<String>) {
|
||||||
|
|
||||||
fun importModule(program: Program, filePath: Path, encoder: IStringEncoding, compilationTargetName: String): Module {
|
fun importModule(filePath: Path): Module {
|
||||||
print("importing '${moduleName(filePath.fileName)}'")
|
print("importing '${moduleName(filePath.fileName)}'")
|
||||||
if(filePath.parent!=null) {
|
if(filePath.parent!=null) {
|
||||||
var importloc = filePath.toString()
|
var importloc = filePath.toString()
|
||||||
@ -41,16 +41,15 @@ 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(CharStreams.fromString(content), filePath, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importLibraryModule(program: Program, name: String,
|
fun importLibraryModule(name: String): Module? {
|
||||||
encoder: IStringEncoding, compilationTargetName: String): Module? {
|
|
||||||
val import = Directive("%import", listOf(
|
val import = Directive("%import", listOf(
|
||||||
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
||||||
), Position("<<<implicit-import>>>", 0, 0, 0))
|
), Position("<<<implicit-import>>>", 0, 0, 0))
|
||||||
return executeImportDirective(program, import, Paths.get(""), encoder, compilationTargetName)
|
return executeImportDirective(import, Paths.get(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyErrorListener: ConsoleErrorListener() {
|
private class MyErrorListener: ConsoleErrorListener() {
|
||||||
@ -62,11 +61,12 @@ 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.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean,
|
private fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||||
encoder: IStringEncoding, compilationTargetName: String): Module {
|
|
||||||
val moduleName = moduleName(modulePath.fileName)
|
val moduleName = moduleName(modulePath.fileName)
|
||||||
val lexer = CustomLexer(modulePath, stream)
|
val lexer = CustomLexer(modulePath, stream)
|
||||||
lexer.removeErrorListeners()
|
lexer.removeErrorListeners()
|
||||||
@ -95,14 +95,13 @@ class ModuleImporter {
|
|||||||
lines.asSequence()
|
lines.asSequence()
|
||||||
.mapIndexed { i, it -> i to it }
|
.mapIndexed { i, it -> i to it }
|
||||||
.filter { (it.second as? Directive)?.directive == "%import" }
|
.filter { (it.second as? Directive)?.directive == "%import" }
|
||||||
.forEach { executeImportDirective(program, it.second as Directive, modulePath, encoder, compilationTargetName) }
|
.forEach { executeImportDirective(it.second as Directive, modulePath) }
|
||||||
|
|
||||||
moduleAst.statements = lines
|
moduleAst.statements = lines
|
||||||
return moduleAst
|
return moduleAst
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeImportDirective(program: Program, import: Directive, source: Path,
|
private fun executeImportDirective(import: Directive, source: Path): Module? {
|
||||||
encoder: IStringEncoding, compilationTargetName: String): Module? {
|
|
||||||
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
|
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
|
||||||
throw SyntaxError("invalid import directive", import.position)
|
throw SyntaxError("invalid import directive", import.position)
|
||||||
val moduleName = import.args[0].name!!
|
val moduleName = import.args[0].name!!
|
||||||
@ -120,12 +119,12 @@ 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")
|
||||||
true, encoder, compilationTargetName)
|
importModule(CharStreams.fromString(content), Paths.get("@embedded@/$resourcePath"), true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val modulePath = tryGetModuleFromFile(moduleName, source, import.position)
|
val modulePath = tryGetModuleFromFile(moduleName, source, import.position)
|
||||||
importModule(program, modulePath, encoder, compilationTargetName)
|
importModule(modulePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDirectivesFromImportedModule(importedModule)
|
removeDirectivesFromImportedModule(importedModule)
|
||||||
@ -157,15 +156,10 @@ class ModuleImporter {
|
|||||||
|
|
||||||
private fun tryGetModuleFromFile(name: String, source: Path, position: Position?): Path {
|
private fun tryGetModuleFromFile(name: String, source: Path, position: Position?): Path {
|
||||||
val fileName = "$name.p8"
|
val fileName = "$name.p8"
|
||||||
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
|
val libpaths = libdirs.map {Path.of(it)}
|
||||||
|
val locations =
|
||||||
val propPath = System.getProperty("prog8.libdir")
|
(if(source.toString().isEmpty()) libpaths else libpaths.drop(1) + listOf(source.parent ?: Path.of("."))) +
|
||||||
if(propPath!=null)
|
listOf(Paths.get(Paths.get("").toAbsolutePath().toString(), "prog8lib"))
|
||||||
locations.add(pathFrom(propPath))
|
|
||||||
val envPath = System.getenv("PROG8_LIBDIR")
|
|
||||||
if(envPath!=null)
|
|
||||||
locations.add(pathFrom(envPath))
|
|
||||||
locations.add(Paths.get(Paths.get("").toAbsolutePath().toString(), "prog8lib"))
|
|
||||||
|
|
||||||
locations.forEach {
|
locations.forEach {
|
||||||
val file = pathFrom(it.toString(), fileName)
|
val file = pathFrom(it.toString(), fileName)
|
||||||
|
@ -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.5.0"
|
||||||
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"
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.8 virtualenv" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -94,7 +94,7 @@ Start the compiler with the ``-watch`` argument to enable this.
|
|||||||
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
||||||
As soon as a change happens, the program gets compiled again.
|
As soon as a change happens, the program gets compiled again.
|
||||||
It is possible to use the watch mode with multiple modules as well, but it will
|
It is possible to use the watch mode with multiple modules as well, but it will
|
||||||
recompile everything in that list even if only of the files got updated.
|
recompile everything in that list even if only one of the files got updated.
|
||||||
|
|
||||||
Other options
|
Other options
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
@ -117,11 +117,11 @@ They are embedded into the packaged release version of the compiler so you don't
|
|||||||
where they are, but their names are still reserved.
|
where they are, but their names are still reserved.
|
||||||
|
|
||||||
|
|
||||||
User defined library files
|
User defined library files and -location
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
You can create library files yourself too that can be shared among programs.
|
You can create library files yourself too that can be shared among programs.
|
||||||
You can tell the compiler where it should look for these files, by setting the java command line property ``prog8.libdir``
|
You can tell the compiler where it should look for these files, by using
|
||||||
or by setting the ``PROG8_LIBDIR`` environment variable to the correct directory.
|
the libdirs command line option.
|
||||||
|
|
||||||
|
|
||||||
.. _debugging:
|
.. _debugging:
|
||||||
|
@ -20,7 +20,7 @@ import os
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'Prog8'
|
project = 'Prog8'
|
||||||
copyright = '2019, Irmen de Jong'
|
copyright = '2021, Irmen de Jong'
|
||||||
author = 'Irmen de Jong'
|
author = 'Irmen de Jong'
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,22 +42,27 @@ 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.
|
||||||
|
- Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do).
|
||||||
|
- Strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||||
- 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 kernel 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)!
|
||||||
|
|
||||||
|
|
||||||
Code example
|
Code example
|
||||||
@ -145,6 +150,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 +168,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.*)
|
||||||
@ -182,7 +201,7 @@ Provides string manipulation routines.
|
|||||||
|
|
||||||
floats
|
floats
|
||||||
------
|
------
|
||||||
Provides definitions for the ROM/kernel subroutines and utility routines dealing with floating
|
Provides definitions for the ROM/kernal subroutines and utility routines dealing with floating
|
||||||
point variables. This includes ``print_f``, the routine used to print floating point numbers.
|
point variables. This includes ``print_f``, the routine used to print floating point numbers.
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
^^^^^^
|
^^^^^^
|
||||||
@ -326,39 +330,6 @@ read the syntax reference on strings.
|
|||||||
The same is true for arrays! So be careful to (re)initialize them if needed.
|
The same is true for arrays! So be careful to (re)initialize them if needed.
|
||||||
|
|
||||||
|
|
||||||
Structs
|
|
||||||
^^^^^^^
|
|
||||||
|
|
||||||
A struct is a group of one or more other variables.
|
|
||||||
This allows you to reuse the definition and manipulate it as a whole.
|
|
||||||
Individual variables in the struct are accessed as you would expect, just
|
|
||||||
use a scoped name to refer to them: ``structvariable.membername``.
|
|
||||||
|
|
||||||
Structs are a bit limited in Prog8: you can only use numerical variables
|
|
||||||
as member of a struct, so strings and arrays and other structs can not be part of a struct.
|
|
||||||
Also, it is not possible to use a struct itself inside an array.
|
|
||||||
Structs are mainly syntactic sugar for repeated groups of vardecls
|
|
||||||
and assignments that belong together. However,
|
|
||||||
*they are layed out in sequence in memory as the members are defined*
|
|
||||||
which may be usefulif you want to pass pointers around.
|
|
||||||
|
|
||||||
To create a variable of a struct type you need to define the struct itself,
|
|
||||||
and then create a variable with it::
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
ubyte red
|
|
||||||
ubyte green
|
|
||||||
ubyte blue
|
|
||||||
}
|
|
||||||
|
|
||||||
Color rgb = [255,122,0] ; note that struct initializer value is same as an array
|
|
||||||
Color another ; the init value is optional, like arrays
|
|
||||||
|
|
||||||
another = rgb ; assign all of the values of rgb to another
|
|
||||||
another.blue = 255 ; set a single member
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Special types: const and memory-mapped
|
Special types: const and memory-mapped
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -451,6 +422,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
|
||||||
---------------------
|
---------------------
|
||||||
@ -522,12 +501,12 @@ Use a ``when`` statement if you have a set of fixed choices that each should res
|
|||||||
action. It is possible to combine several choices to result in the same action::
|
action. It is possible to combine several choices to result in the same action::
|
||||||
|
|
||||||
when value {
|
when value {
|
||||||
4 -> c64scr.print("four")
|
4 -> txt.print("four")
|
||||||
5 -> c64scr.print("five")
|
5 -> txt.print("five")
|
||||||
10,20,30 -> {
|
10,20,30 -> {
|
||||||
c64scr.print("ten or twenty or thirty")
|
txt.print("ten or twenty or thirty")
|
||||||
}
|
}
|
||||||
else -> c64scr.print("don't know")
|
else -> txt.print("don't know")
|
||||||
}
|
}
|
||||||
|
|
||||||
The when-*value* can be any expression but the choice values have to evaluate to
|
The when-*value* can be any expression but the choice values have to evaluate to
|
||||||
@ -642,7 +621,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 +718,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 +738,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 +770,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".
|
||||||
|
|
||||||
@ -854,11 +842,6 @@ sizeof(name)
|
|||||||
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||||
|
|
||||||
offsetof(membername)
|
|
||||||
Number of bytes from the start of a struct variable that this member variable is located.
|
|
||||||
For now, this only works on members of a declared struct variable and not yet on members
|
|
||||||
referenced from the struct type itself. This might be improved in a future version of the language.
|
|
||||||
|
|
||||||
swap(x, y)
|
swap(x, y)
|
||||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||||
|
|
||||||
@ -873,6 +856,29 @@ memory(name, size)
|
|||||||
The return value is just a simple uword address so it cannot be used as an array in your program.
|
The return value is just a simple uword address so it cannot be used as an array in your program.
|
||||||
You can only treat it as a pointer or use it in inline assembly.
|
You can only treat it as a pointer or use it in inline assembly.
|
||||||
|
|
||||||
|
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler target for now
|
||||||
|
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
|
||||||
|
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte).
|
||||||
|
Notice that bank $00 is used by the Kernal and should not be used by user code.
|
||||||
|
The third argument can be used to designate the memory address
|
||||||
|
of an argument for the routine; it will be loaded into the A register and will
|
||||||
|
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||||
|
no argument passing will be done.
|
||||||
|
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
||||||
|
and you'll have to set up a call to ``jsrfar`` yourself to process this.
|
||||||
|
|
||||||
|
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler target for now
|
||||||
|
Calls an assembly routine in another rom-bank on the CommanderX16
|
||||||
|
The banked ROM is located in the address range $C000-$FFFF (16 kilobyte).
|
||||||
|
There are 32 banks (0 to 31).
|
||||||
|
The third argument can be used to designate the memory address
|
||||||
|
of an argument for the routine; it will be loaded into the A register and will
|
||||||
|
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||||
|
no argument passing will be done.
|
||||||
|
If the routine requires different arguments or return values, ``callrom`` cannot be used
|
||||||
|
and you'll have to set up a call in assembly code yourself that handles the banking and
|
||||||
|
argument/returnvalues.
|
||||||
|
|
||||||
|
|
||||||
Library routines
|
Library routines
|
||||||
----------------
|
----------------
|
||||||
|
@ -70,7 +70,7 @@ Directives
|
|||||||
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
||||||
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
||||||
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
||||||
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
|
are required to perform floating point operations (from the BASIC kernal). No clean exit is possible.
|
||||||
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
||||||
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
|
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
|
||||||
during normal system operation.
|
during normal system operation.
|
||||||
@ -120,17 +120,18 @@ Directives
|
|||||||
Level: module, block.
|
Level: module, block.
|
||||||
Sets special compiler options.
|
Sets special compiler options.
|
||||||
|
|
||||||
- For a module option, there is ``enable_floats``, which will tell the compiler
|
- ``enable_floats`` (module level) tells the compiler
|
||||||
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
|
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
|
||||||
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
|
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
|
||||||
importing the ``floats`` library is required anyway and that will enable it for you automatically.
|
importing the ``floats`` library is required anyway and that will enable it for you automatically.
|
||||||
- There's also ``no_sysinit`` which cause the resulting program to *not* include
|
- ``no_sysinit`` (module level) which cause the resulting program to *not* include
|
||||||
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
|
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
|
||||||
take care of that yourself. The program will just start running from whatever state the machine is in when the
|
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.
|
||||||
- When used in a block with the ``force_output`` option, it will force the block to be outputted
|
- ``force_output`` (in a block) will force the block to be outputted in the final program.
|
||||||
in the final program. Can be useful to make sure some
|
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)
|
||||||
data is generated that would otherwise be discarded because 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_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).
|
||||||
|
|
||||||
|
|
||||||
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
|
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
|
||||||
@ -252,7 +253,6 @@ Various examples::
|
|||||||
byte[5] values = 255 ; initialize with five 255 bytes
|
byte[5] values = 255 ; initialize with five 255 bytes
|
||||||
|
|
||||||
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
||||||
Color rgb = {1,255,0} ; a struct variable with initial values
|
|
||||||
|
|
||||||
|
|
||||||
Data types
|
Data types
|
||||||
@ -386,27 +386,6 @@ Syntax is familiar with brackets: ``arrayvar[x]`` ::
|
|||||||
string[4] ; the fifth character (=byte) in the string
|
string[4] ; the fifth character (=byte) in the string
|
||||||
|
|
||||||
|
|
||||||
Struct
|
|
||||||
^^^^^^
|
|
||||||
|
|
||||||
A *struct* has to be defined to specify what its member variables are.
|
|
||||||
There are one or more members::
|
|
||||||
|
|
||||||
struct <structname> {
|
|
||||||
<vardecl>
|
|
||||||
[ <vardecl> ...]
|
|
||||||
}
|
|
||||||
|
|
||||||
You can only use numerical variables as member of a struct, so strings and arrays
|
|
||||||
and other structs can not be part of a struct. Vice versa, a struct can not occur in an array.
|
|
||||||
|
|
||||||
After defining a struct you can use the name of the struct as a data type to declare variables with.
|
|
||||||
|
|
||||||
Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
|
|
||||||
|
|
||||||
Color rgb = [255, 100, 0] ; note that the value is an array
|
|
||||||
|
|
||||||
|
|
||||||
String
|
String
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
@ -424,6 +403,9 @@ There are several escape sequences available to put special characters into your
|
|||||||
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
|
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
|
||||||
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
|
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
|
||||||
|
|
||||||
|
- String literals can contain many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳".
|
||||||
|
Characters like ^, _, \\, {, } and | (that have no direct PETSCII counterpart) are still accepted and converted to the closest PETSCII equivalents. (Make sure you save the source file in UTF-8 encoding if you use this.)
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
@ -513,7 +495,7 @@ Multiple return values
|
|||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Normal subroutines can only return zero or one return values.
|
Normal subroutines can only return zero or one return values.
|
||||||
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
||||||
(referencing a routine in kernel ROM) can return more than one return value.
|
(referencing a routine in kernal ROM) can return more than one return value.
|
||||||
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
||||||
It is not possible to process the results of a call to these kind of routines
|
It is not possible to process the results of a call to these kind of routines
|
||||||
directly from the language, because only single value assignments are possible.
|
directly from the language, because only single value assignments are possible.
|
||||||
@ -549,7 +531,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.
|
||||||
|
|
||||||
|
|
||||||
@ -591,7 +573,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::
|
||||||
@ -772,11 +754,11 @@ Choices can result in a single statement or a block of multiple statements in w
|
|||||||
case you have to use { } to enclose them::
|
case you have to use { } to enclose them::
|
||||||
|
|
||||||
when value {
|
when value {
|
||||||
4 -> c64scr.print("four")
|
4 -> txt.print("four")
|
||||||
5 -> c64scr.print("five")
|
5 -> txt.print("five")
|
||||||
10,20,30 -> {
|
10,20,30 -> {
|
||||||
c64scr.print("ten or twenty or thirty")
|
txt.print("ten or twenty or thirty")
|
||||||
}
|
}
|
||||||
else -> c64scr.print("don't know")
|
else -> txt.print("don't know")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user