mirror of
https://github.com/irmen/prog8.git
synced 2025-06-15 18:23:35 +00:00
Compare commits
133 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
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>
|
13
README.md
13
README.md
@ -23,13 +23,14 @@ https://prog8.readthedocs.io/
|
|||||||
What does Prog8 provide?
|
What does Prog8 provide?
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
- big reduction of source code length over raw assembly
|
- reduction of source code length over raw assembly
|
||||||
- modularity, symbol scoping, subroutines
|
- modularity, symbol scoping, subroutines
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string and array variables and string sharing
|
- automatic variable allocations, automatic string and array variables and string sharing
|
||||||
- subroutines with an input- and output parameter signature
|
- subroutines with input parameters and result values
|
||||||
- no stack frame allocations because parameters and local variables are automatically allocated statically
|
- high-level program optimizations
|
||||||
- constant folding in expressions and other high-level program optimizations
|
- small program boilerplate/compilersupport overhead
|
||||||
|
- sane variable initialization, programs can be restarted again just fine after exiting to basic
|
||||||
- conditional branches
|
- conditional branches
|
||||||
- floating point operations (requires the C64 Basic ROM routines for this)
|
- floating point operations (requires the C64 Basic ROM routines for this)
|
||||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||||
@ -38,8 +39,10 @@ What does Prog8 provide?
|
|||||||
- 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
|
- fast execution speed due to compilation to native assembly code
|
||||||
|
- variables are allocated statically
|
||||||
- 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 +55,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)!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ dependencies {
|
|||||||
compileKotlin {
|
compileKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
// verbose = true
|
// verbose = true
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||||
}
|
}
|
||||||
@ -42,6 +43,7 @@ compileKotlin {
|
|||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
|
useIR = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,8 +211,8 @@ 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
|
||||||
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)
|
||||||
@ -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 ----
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,10 @@ 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,12 @@
|
|||||||
; 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 +36,7 @@ gfx2 {
|
|||||||
sub screen_mode(ubyte mode) {
|
sub screen_mode(ubyte mode) {
|
||||||
when mode {
|
when mode {
|
||||||
1 -> {
|
1 -> {
|
||||||
; lores monchrome
|
; lores monochrome
|
||||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||||
cx16.VERA_DC_HSCALE = 64
|
cx16.VERA_DC_HSCALE = 64
|
||||||
cx16.VERA_DC_VSCALE = 64
|
cx16.VERA_DC_VSCALE = 64
|
||||||
@ -85,7 +84,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 +166,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 +208,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++
|
||||||
}
|
}
|
||||||
@ -299,6 +297,7 @@ _done
|
|||||||
1, 5 -> {
|
1, 5 -> {
|
||||||
; monochrome, either resolution
|
; monochrome, either resolution
|
||||||
; 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.
|
; 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.
|
||||||
|
; TODO 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
|
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||||
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||||
if active_mode>=5
|
if active_mode>=5
|
||||||
@ -326,7 +325,7 @@ _done
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
; stippling.
|
; stippling.
|
||||||
height = (height+1)/2
|
height = (height+1)/2 ; TODO is the line sometimes 1 pixel too long now because of rounding?
|
||||||
%asm {{
|
%asm {{
|
||||||
lda x
|
lda x
|
||||||
eor y
|
eor y
|
||||||
@ -375,7 +374,8 @@ _done
|
|||||||
lda cx16.VERA_ADDR_M
|
lda cx16.VERA_ADDR_M
|
||||||
adc #0
|
adc #0
|
||||||
sta cx16.VERA_ADDR_M
|
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.
|
; the bitmap size is small enough to not have to deal with the _H part:
|
||||||
|
; lda cx16.VERA_ADDR_H
|
||||||
; adc #0
|
; adc #0
|
||||||
; sta cx16.VERA_ADDR_H
|
; sta cx16.VERA_ADDR_H
|
||||||
}}
|
}}
|
||||||
@ -399,11 +399,12 @@ _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.
|
; note for this mode we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
||||||
|
; TODO 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
|
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||||
; TODO also mostly usable for lores 4c?
|
; TODO also mostly usable for lores 4c?
|
||||||
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||||
|
|
||||||
; TODO optimize the loop in pure assembly
|
; TODO optimize this vertical line loop in pure assembly
|
||||||
color &= 3
|
color &= 3
|
||||||
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||||
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||||
@ -432,48 +433,46 @@ _done
|
|||||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||||
; Bresenham algorithm.
|
; Bresenham algorithm.
|
||||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||||
; TODO there are some slight errors at the first/last pixels in certain slopes...
|
|
||||||
if y1>y2 {
|
if y1>y2 {
|
||||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
}
|
}
|
||||||
word @zp dx = x2-x1 as word
|
word @zp dx = (x2 as word)-x1
|
||||||
word @zp dy = y2-y1 as word
|
word @zp dy = (y2 as word)-y1
|
||||||
|
|
||||||
if dx==0 {
|
if dx==0 {
|
||||||
vertical_line(x1, y1, abs(dy)+1 as uword, color)
|
vertical_line(x1, y1, abs(dy) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if dy==0 {
|
if dy==0 {
|
||||||
if x1>x2
|
if x1>x2
|
||||||
x1=x2
|
x1=x2
|
||||||
horizontal_line(x1, y1, abs(dx)+1 as uword, color)
|
horizontal_line(x1, y1, abs(dx) as uword +1, color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
; TODO rewrite the rest in optimized assembly (or reuse GRAPH_draw_line if we can get the FB replacement vector layer working)
|
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
cx16.r13 = true ; 'positive_ix'
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
positive_ix = false
|
cx16.r13 = false
|
||||||
}
|
}
|
||||||
dx *= 2
|
word @zp dx2 = dx*2
|
||||||
dy *= 2
|
word @zp dy2 = dy*2
|
||||||
cx16.r14 = x1 ; internal plot X
|
cx16.r14 = x1 ; internal plot X
|
||||||
|
|
||||||
if dx >= dy {
|
if dx >= dy {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -482,25 +481,25 @@ _done
|
|||||||
if cx16.r14==x2
|
if cx16.r14==x2
|
||||||
return
|
return
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d += dy
|
d += dy2
|
||||||
if d > dx {
|
if d > dx {
|
||||||
y1++
|
y1++
|
||||||
d -= dx
|
d -= dx2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if positive_ix {
|
if cx16.r13 {
|
||||||
repeat {
|
repeat {
|
||||||
plot(cx16.r14, y1, color)
|
plot(cx16.r14, y1, color)
|
||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14++
|
cx16.r14++
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -509,10 +508,10 @@ _done
|
|||||||
if y1 == y2
|
if y1 == y2
|
||||||
return
|
return
|
||||||
y1++
|
y1++
|
||||||
d += dx
|
d += dx2
|
||||||
if d > dy {
|
if d > dy {
|
||||||
cx16.r14--
|
cx16.r14--
|
||||||
d -= dy
|
d -= dy2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -812,6 +811,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 +840,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 +877,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 +914,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
|
||||||
|
@ -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,8 +44,8 @@ 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
|
||||||
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)
|
||||||
@ -102,10 +102,11 @@ asmsub MEMTOP2() -> ubyte @A {
|
|||||||
|
|
||||||
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
|
||||||
@ -349,75 +350,115 @@ 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
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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,7 +494,7 @@ _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 {{
|
||||||
@ -461,7 +502,7 @@ asmsub init_system() {
|
|||||||
cld
|
cld
|
||||||
;stz $00
|
;stz $00
|
||||||
;stz $01
|
;stz $01
|
||||||
;stz d1prb ; select rom bank 0
|
;stz d1prb ; select rom bank 0 (enable kernal)
|
||||||
lda #$80
|
lda #$80
|
||||||
sta VERA_CTRL
|
sta VERA_CTRL
|
||||||
jsr c64.IOINIT
|
jsr c64.IOINIT
|
||||||
@ -485,8 +526,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 --------
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -244,8 +244,8 @@ randseed .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
randbyte .proc
|
fast_randbyte .proc
|
||||||
; -- 8-bit pseudo random number generator into A
|
; -- fast but bad 8-bit pseudo random number generator into A
|
||||||
lda _seed
|
lda _seed
|
||||||
beq _eor
|
beq _eor
|
||||||
asl a
|
asl a
|
||||||
@ -263,6 +263,10 @@ _seed .byte $3a
|
|||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
randbyte .proc
|
||||||
|
; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||||
|
jmp randword
|
||||||
|
.pend
|
||||||
|
|
||||||
randword .proc
|
randword .proc
|
||||||
; -- 16 bit pseudo random number generator into AY
|
; -- 16 bit pseudo random number generator into AY
|
||||||
@ -1537,3 +1541,71 @@ _negative lsr a
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
square .proc
|
||||||
|
; -- calculate square root of signed word in AY, result in AY
|
||||||
|
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
|
||||||
|
; using this routine is about twice as fast as doing a regular multiplication.
|
||||||
|
;
|
||||||
|
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
|
||||||
|
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
|
||||||
|
; Squarel/Squareh
|
||||||
|
;
|
||||||
|
; The maximum input range is only +/-255 and no checking is done to ensure that
|
||||||
|
; this is so.
|
||||||
|
;
|
||||||
|
; This routine is useful if you are trying to draw circles as for any circle
|
||||||
|
;
|
||||||
|
; x^2+y^2=r^2 where x and y are the co-ordinates of any point on the circle and
|
||||||
|
; r is the circle radius
|
||||||
|
|
||||||
|
numberl = P8ZP_SCRATCH_W1 ; number to square low byte
|
||||||
|
numberh = P8ZP_SCRATCH_W1+1 ; number to square high byte
|
||||||
|
squarel = P8ZP_SCRATCH_W2 ; square low byte
|
||||||
|
squareh = P8ZP_SCRATCH_W2+1 ; square high byte
|
||||||
|
tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
|
||||||
|
|
||||||
|
sta numberl
|
||||||
|
sty numberh
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
|
||||||
|
lda #$00 ; clear a
|
||||||
|
sta squarel ; clear square low byte
|
||||||
|
; (no need to clear the high byte, it gets shifted out)
|
||||||
|
lda numberl ; get number low byte
|
||||||
|
ldx numberh ; get number high byte
|
||||||
|
bpl _nonneg ; if +ve don't negate it
|
||||||
|
; else do a two's complement
|
||||||
|
eor #$ff ; invert
|
||||||
|
sec ; +1
|
||||||
|
adc #$00 ; and add it
|
||||||
|
|
||||||
|
_nonneg:
|
||||||
|
sta tempsq ; save abs(number)
|
||||||
|
ldx #$08 ; set bit count
|
||||||
|
|
||||||
|
_nextr2bit:
|
||||||
|
asl squarel ; low byte *2
|
||||||
|
rol squareh ; high byte *2+carry from low
|
||||||
|
asl a ; shift number byte
|
||||||
|
bcc _nosqadd ; don't do add if c = 0
|
||||||
|
tay ; save a
|
||||||
|
clc ; clear carry for add
|
||||||
|
lda tempsq ; get number
|
||||||
|
adc squarel ; add number^2 low byte
|
||||||
|
sta squarel ; save number^2 low byte
|
||||||
|
lda #$00 ; clear a
|
||||||
|
adc squareh ; add number^2 high byte
|
||||||
|
sta squareh ; save number^2 high byte
|
||||||
|
tya ; get a back
|
||||||
|
|
||||||
|
_nosqadd:
|
||||||
|
dex ; decrement bit count
|
||||||
|
bne _nextr2bit ; go do next bit
|
||||||
|
|
||||||
|
lda squarel
|
||||||
|
ldy squareh
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
rts
|
||||||
|
|
||||||
|
.pend
|
||||||
|
@ -387,6 +387,14 @@ func_sqrt16_into_A .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
func_fastrnd8_stack .proc
|
||||||
|
; -- put a random ubyte on the estack (using fast but bad RNG)
|
||||||
|
jsr math.fast_randbyte
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
func_rnd_stack .proc
|
func_rnd_stack .proc
|
||||||
; -- put a random ubyte on the estack
|
; -- put a random ubyte on the estack
|
||||||
jsr math.randbyte
|
jsr math.randbyte
|
||||||
@ -432,6 +440,7 @@ func_min_ub_stack .proc
|
|||||||
func_min_b_into_A .proc
|
func_min_b_into_A .proc
|
||||||
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #127
|
lda #127
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
@ -548,6 +557,7 @@ func_min_w_stack .proc
|
|||||||
func_max_ub_into_A .proc
|
func_max_ub_into_A .proc
|
||||||
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
|
||||||
tay
|
tay
|
||||||
|
dey
|
||||||
lda #0
|
lda #0
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
- lda (P8ZP_SCRATCH_W1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
@ -1072,3 +1072,14 @@ sign_extend_AY_byte .proc
|
|||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
strlen .proc
|
||||||
|
; -- returns the number of bytes in the string in AY, in Y.
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
|
beq +
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ rts
|
||||||
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
6.1
|
6.4
|
||||||
|
@ -8,7 +8,6 @@ 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.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
@ -117,7 +116,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,10 +8,11 @@ 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>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
@ -107,7 +108,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)
|
||||||
@ -154,16 +155,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) {
|
||||||
@ -216,4 +207,93 @@ 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.STRUCT)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if(dt1 in ByteDatatypes) {
|
||||||
|
if(dt2 in ByteDatatypes)
|
||||||
|
return noModifications
|
||||||
|
val cast1 = TypecastExpression(arg1, if(dt1==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(arg1, cast1, functionCallStatement))
|
||||||
|
} else {
|
||||||
|
if(dt2 in WordDatatypes)
|
||||||
|
return noModifications
|
||||||
|
val cast2 = TypecastExpression(arg2, if(dt2==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(arg2, cast2, functionCallStatement))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||||
|
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||||
|
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val index = arrayIndexedExpression.indexer.indexExpr
|
||||||
|
if(index !is NumericLiteralValue && index !is IdentifierReference) {
|
||||||
|
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||||
|
return getAutoIndexerVarFor(arrayIndexedExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getComplexArrayIndexedExpressions(stmt: Statement): List<ArrayIndexedExpression> {
|
||||||
|
|
||||||
|
class Searcher : IAstVisitor {
|
||||||
|
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
|
||||||
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
|
val ix = arrayIndexedExpression.indexer.indexExpr
|
||||||
|
if(ix !is NumericLiteralValue && ix !is IdentifierReference)
|
||||||
|
complexArrayIndexedExpressions.add(arrayIndexedExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(branchStatement: BranchStatement) {}
|
||||||
|
|
||||||
|
override fun visit(forLoop: ForLoop) {}
|
||||||
|
|
||||||
|
override fun visit(ifStatement: IfStatement) {
|
||||||
|
ifStatement.condition.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(untilLoop: UntilLoop) {
|
||||||
|
untilLoop.condition.accept(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val searcher = Searcher()
|
||||||
|
stmt.accept(searcher)
|
||||||
|
return searcher.complexArrayIndexedExpressions
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getContainingStatement(expression: Expression): Statement {
|
||||||
|
var node: Node = expression
|
||||||
|
while(node !is Statement)
|
||||||
|
node = node.parent
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
val statement = expr.containingStatement()
|
||||||
|
// replace the indexer with just the variable (simply use a cx16 virtual register r9, that we HOPE is not used for other things in the expression...)
|
||||||
|
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf("cx16", "r9"), expr.indexer.position), null, null, expr.indexer.position)
|
||||||
|
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
|
||||||
|
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
|
||||||
|
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
|
||||||
|
return modifications
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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>)
|
||||||
|
|
||||||
|
|
||||||
@ -73,27 +76,28 @@ fun compileProgram(filepath: Path,
|
|||||||
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)
|
||||||
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)
|
||||||
postprocessAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
@ -103,7 +107,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 +131,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 +141,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 +157,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(func.known_returntype==null)
|
else if(func.known_returntype==null)
|
||||||
throw IllegalArgumentException("builtin function $name can't be used here because it doesn't return a value")
|
return null // builtin function $name can't be used here because it doesn't return a value
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -161,34 +165,34 @@ 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): 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 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)
|
importer.importModule(programAst, filepath, compTarget, compilationTargetName)
|
||||||
errors.handle()
|
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(programAst, lib, compTarget, compilationTargetName)
|
||||||
|
|
||||||
// always import prog8_lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule(programAst, "math", ICompilationTarget.instance, compilationTargetName)
|
importer.importLibraryModule(programAst, "math", compTarget, compilationTargetName)
|
||||||
importer.importLibraryModule(programAst, "prog8_lib", ICompilationTarget.instance, compilationTargetName)
|
importer.importLibraryModule(programAst, "prog8_lib", compTarget, compilationTargetName)
|
||||||
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?.toUpperCase()
|
||||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||||
@ -209,7 +213,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
// 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: Cx16 target must use zp option basicsafe instead of floatsafe")
|
||||||
zpType = ZeropageType.BASICSAFE
|
zpType = ZeropageType.BASICSAFE
|
||||||
}
|
}
|
||||||
@ -233,78 +237,81 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
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.checkValid(compilerOptions, errors, compTarget)
|
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||||
errors.handle()
|
errors.report()
|
||||||
programAst.checkIdentifiers(errors, compTarget)
|
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||||
errors.handle()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAst(programAst: Program, errors: ErrorReporter, functions: IBuiltinFunctions) {
|
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||||
// optimize the parse tree
|
// optimize the parse tree
|
||||||
println("Optimizing...")
|
println("Optimizing...")
|
||||||
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, ::loadAsmIncludeFile)
|
||||||
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 remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
|
||||||
remover.visit(programAst)
|
remover.visit(programAst)
|
||||||
remover.applyModifications()
|
remover.applyModifications()
|
||||||
errors.handle()
|
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.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, ::loadAsmIncludeFile)
|
||||||
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 isEmpty(): 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 isEmpty() = messages.isEmpty()
|
||||||
}
|
}
|
||||||
|
@ -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,7 +8,7 @@ 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.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
|
||||||
@ -18,7 +18,7 @@ import java.io.File
|
|||||||
|
|
||||||
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,19 +41,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// there can be an optional single 'irq' block with a 'irq' subroutine in it,
|
|
||||||
// which will be used as the 60hz irq routine in the vm if it's present
|
|
||||||
val irqBlocks = program.modules.flatMap { it.statements }.filter { it is Block && it.name=="irq" }.map { it as Block }
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +174,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,13 +218,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("subroutines can only have one return value")
|
err("subroutines can only have one return value")
|
||||||
|
|
||||||
// subroutine must contain at least one 'return' or 'goto'
|
// subroutine must contain at least one 'return' or 'goto'
|
||||||
// (or if it has an asm block, that must contain a 'rts' or 'jmp')
|
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
|
||||||
if(subroutine.statements.count { it is Return || it is Jump } == 0) {
|
if(subroutine.statements.count { it is Return || it is Jump } == 0) {
|
||||||
if (subroutine.amountOfRtsInAsm() == 0) {
|
if (subroutine.amountOfRtsInAsm() == 0) {
|
||||||
if (subroutine.returntypes.isNotEmpty()) {
|
if (subroutine.returntypes.isNotEmpty()) {
|
||||||
// for asm subroutines with an address, no statement check is possible.
|
// for asm subroutines with an address, no statement check is possible.
|
||||||
if (subroutine.asmAddress == null && !subroutine.inline)
|
if (subroutine.asmAddress == null && !subroutine.inline)
|
||||||
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)")
|
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,10 +338,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).
|
||||||
@ -391,7 +374,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!idt.isKnown) {
|
if(!idt.isKnown) {
|
||||||
errors.err("return type mismatch", assignment.value.position)
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
}
|
||||||
if(stmt.returntypes.size <= 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) {
|
if(stmt.returntypes.isEmpty() || (stmt.returntypes.size == 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE))) {
|
||||||
errors.err("return type mismatch", assignment.value.position)
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,7 +493,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)
|
||||||
@ -756,7 +739,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" -> {
|
||||||
@ -970,6 +953,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,15 +1125,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
||||||
|
|
||||||
// check index value 0..255
|
// check index value 0..255
|
||||||
val dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
|
||||||
if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE)
|
if(!dtxNum.istype(DataType.UBYTE) && !dtxNum.istype(DataType.BYTE))
|
||||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
||||||
val dtxVar = arrayIndexedExpression.indexer.indexVar?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
|
||||||
if(dtxVar!=null && dtxVar != DataType.UBYTE && dtxVar != DataType.BYTE)
|
|
||||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
|
||||||
|
|
||||||
if(arrayIndexedExpression.indexer.origExpression!=null)
|
|
||||||
throw FatalAstException("array indexer should have been replaced with a temp var @ ${arrayIndexedExpression.indexer.position}")
|
|
||||||
|
|
||||||
super.visit(arrayIndexedExpression)
|
super.visit(arrayIndexedExpression)
|
||||||
}
|
}
|
||||||
@ -1145,12 +1136,18 @@ internal class AstChecker(private val program: Program,
|
|||||||
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if(conditionType !in IntegerDatatypes)
|
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)
|
||||||
|
|
||||||
@ -1361,7 +1358,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)
|
||||||
@ -1420,7 +1417,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
else -> errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
else -> {
|
||||||
|
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
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()
|
fixer.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
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,7 +37,7 @@ 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)
|
||||||
|
@ -3,7 +3,6 @@ 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.ast.base.DataType
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
import prog8.ast.base.NumericDatatypes
|
import prog8.ast.base.NumericDatatypes
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
import prog8.ast.expressions.ArrayLiteralValue
|
||||||
@ -11,10 +10,11 @@ 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) {
|
||||||
|
@ -32,7 +32,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
@ -17,13 +17,10 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
|||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
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.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.
|
||||||
@ -89,8 +92,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||||
// rewrite pointervar[index] into @(pointervar+index)
|
// rewrite pointervar[index] into @(pointervar+index)
|
||||||
val indexer = arrayIndexedExpression.indexer
|
val indexer = arrayIndexedExpression.indexer
|
||||||
val index = (indexer.indexNum ?: indexer.indexVar)!!
|
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||||
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
|
|
||||||
return if(parent is AssignTarget) {
|
return if(parent is AssignTarget) {
|
||||||
// we're part of the target of an assignment, we have to actually change the assign target itself
|
// we're part of the target of an assignment, we have to actually change the assign target itself
|
||||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||||
@ -102,23 +104,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
|
return noModifications
|
||||||
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> {
|
||||||
@ -167,41 +153,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
|
||||||
@ -236,32 +222,23 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
val valueType = assignment.value.inferType(program)
|
val valueType = assignment.value.inferType(program)
|
||||||
val targetType = assignment.target.inferType(program)
|
val targetType = assignment.target.inferType(program)
|
||||||
var assignments = emptyList<Assignment>()
|
|
||||||
|
|
||||||
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
|
errors.err("cannot assign non-const array value, use separate assignment per field", assignment.position)
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
return copyStructValue(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
||||||
assignments = if (assignment.value is ArrayLiteralValue) {
|
if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
errors.err("cannot assign non-const array value, use separate assignment per element", assignment.position)
|
||||||
} else {
|
} else {
|
||||||
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
return copyArrayValue(assignment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(assignments.isNotEmpty()) {
|
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
val scope = assignment.definingScope()
|
|
||||||
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) }
|
|
||||||
modifications.add(IAstModification.Remove(assignment, scope))
|
|
||||||
return modifications
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,113 +291,98 @@ 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.isEmpty())
|
||||||
|
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> {
|
private fun copyStructValue(structAssignment: Assignment): List<IAstModification> {
|
||||||
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 identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
|
||||||
val targetVar = identifier.targetVarDecl(program)!!
|
|
||||||
val struct = targetVar.struct!!
|
|
||||||
|
|
||||||
val slv = structAssignment.value as? ArrayLiteralValue
|
|
||||||
if(slv==null || slv.value.size != struct.numberOfElements) {
|
|
||||||
errors.err("element count mismatch", structAssignment.position)
|
|
||||||
return emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
|
||||||
targetDecl as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
|
||||||
sourceValue, sourceValue.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
|
|
||||||
// TODO use memcopy beyond a certain number of elements
|
|
||||||
val identifier = structAssignment.target.identifier!!
|
|
||||||
val identifierName = identifier.nameInSource.single()
|
|
||||||
val targetVar = identifier.targetVarDecl(program)!!
|
val targetVar = identifier.targetVarDecl(program)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
when (structAssignment.value) {
|
when (structAssignment.value) {
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
|
||||||
|
val memsize = struct.memsize(program.memsizer)
|
||||||
when {
|
when {
|
||||||
sourceVar.struct!=null -> {
|
sourceVar.struct!=null -> {
|
||||||
// struct memberwise copy
|
// struct memberwise copy
|
||||||
val sourceStruct = sourceVar.struct!!
|
val sourceStruct = sourceVar.struct!!
|
||||||
if(sourceStruct!==targetVar.struct) {
|
if(sourceStruct!==targetVar.struct) {
|
||||||
// structs are not the same in assignment
|
errors.err("struct type mismatch", structAssignment.position)
|
||||||
return listOf() // error will be printed elsewhere
|
return listOf()
|
||||||
}
|
}
|
||||||
if(struct.statements.size!=sourceStruct.statements.size)
|
if(struct.statements.size!=sourceStruct.statements.size) {
|
||||||
return listOf() // error will be printed elsewhere
|
errors.err("struct element count mismatch", structAssignment.position)
|
||||||
return struct.statements.zip(sourceStruct.statements).map { member ->
|
return listOf()
|
||||||
val targetDecl = member.first as VarDecl
|
|
||||||
val sourceDecl = member.second as VarDecl
|
|
||||||
if(targetDecl.name != sourceDecl.name)
|
|
||||||
throw FatalAstException("struct member mismatch")
|
|
||||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
|
||||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
|
||||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
|
||||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
}
|
||||||
|
if(memsize!=sourceStruct.memsize(program.memsizer)) {
|
||||||
|
errors.err("memory size mismatch", structAssignment.position)
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
|
||||||
|
mutableListOf(
|
||||||
|
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
|
||||||
|
AddressOf(identifier, structAssignment.position),
|
||||||
|
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
structAssignment.position
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
|
||||||
}
|
}
|
||||||
sourceVar.isArray -> {
|
sourceVar.isArray -> {
|
||||||
val array = (sourceVar.value as ArrayLiteralValue).value
|
val array = sourceVar.value as ArrayLiteralValue
|
||||||
if(struct.statements.size!=array.size)
|
if(struct.statements.size!=array.value.size) {
|
||||||
return listOf() // error will be printed elsewhere
|
errors.err("struct element count mismatch", structAssignment.position)
|
||||||
return struct.statements.zip(array).map {
|
return listOf()
|
||||||
val decl = it.first as VarDecl
|
|
||||||
val mangled = mangledStructMemberName(identifierName, decl.name)
|
|
||||||
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
|
|
||||||
val target = AssignTarget(targetName, null, null, structAssignment.position)
|
|
||||||
val assign = Assignment(target, it.second, structAssignment.position)
|
|
||||||
assign.linkParents(structAssignment)
|
|
||||||
assign
|
|
||||||
}
|
}
|
||||||
|
if(memsize!=array.memsize(program.memsizer)) {
|
||||||
|
errors.err("memory size mismatch", structAssignment.position)
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
|
||||||
|
mutableListOf(
|
||||||
|
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
|
||||||
|
AddressOf(identifier, structAssignment.position),
|
||||||
|
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
structAssignment.position
|
||||||
|
)
|
||||||
|
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
throw FatalAstException("can only assign arrays or structs to structs")
|
||||||
@ -428,7 +390,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue -> {
|
is ArrayLiteralValue -> {
|
||||||
throw IllegalArgumentException("not going to flatten a structLv assignment here")
|
throw IllegalArgumentException("not going to do a structLv assignment here")
|
||||||
}
|
}
|
||||||
else -> throw FatalAstException("strange struct value")
|
else -> throw FatalAstException("strange struct value")
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,11 @@ 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)
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
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.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 +88,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,46 +97,48 @@ 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),
|
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),
|
||||||
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
|
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
|
||||||
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
|
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
|
||||||
|
FSignature("fastrnd8" , false, emptyList(), DataType.UBYTE),
|
||||||
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
|
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
|
||||||
FSignature("rndw" , false, emptyList(), DataType.UWORD),
|
FSignature("rndw" , false, emptyList(), DataType.UWORD),
|
||||||
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
|
||||||
@ -271,7 +274,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,7 +288,7 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): 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("offsetof requires one argument", position)
|
throw SyntaxError("offsetof requires one argument", position)
|
||||||
@ -301,12 +305,12 @@ private fun builtinOffsetof(args: List<Expression>, position: Position, program:
|
|||||||
for(member in struct.statements) {
|
for(member in struct.statements) {
|
||||||
if((member as VarDecl).name == membername)
|
if((member as VarDecl).name == membername)
|
||||||
return NumericLiteralValue(DataType.UBYTE, offset, position)
|
return NumericLiteralValue(DataType.UBYTE, offset, position)
|
||||||
offset += ICompilationTarget.instance.memorySize(member.datatype)
|
offset += memsizer.memorySize(member.datatype)
|
||||||
}
|
}
|
||||||
throw SyntaxError("undefined struct member", position)
|
throw SyntaxError("undefined struct member", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinSizeof(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
|
// 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,14 +322,13 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
|||||||
val target = (args[0] as IdentifierReference).targetStatement(program)
|
val target = (args[0] as IdentifierReference).targetStatement(program)
|
||||||
?: throw CannotEvaluateException("sizeof", "no target")
|
?: throw CannotEvaluateException("sizeof", "no target")
|
||||||
|
|
||||||
fun structSize(target: StructDecl) =
|
fun structSize(target: StructDecl) = NumericLiteralValue(DataType.UBYTE, target.memsize(memsizer), position)
|
||||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { ICompilationTarget.instance.memorySize((it as VarDecl).datatype) }.sum(), position)
|
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||||
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 = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||||
numericLiteral(ICompilationTarget.instance.memorySize(elementDt) * length, position)
|
numericLiteral(memsizer.memorySize(elementDt) * length, position)
|
||||||
}
|
}
|
||||||
dt.istype(DataType.STRUCT) -> {
|
dt.istype(DataType.STRUCT) -> {
|
||||||
when (target) {
|
when (target) {
|
||||||
@ -335,14 +338,15 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.STRUCT)), 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,7 +370,7 @@ 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)
|
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||||
@ -376,7 +380,8 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 +390,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 +399,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 +408,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 +417,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 +426,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 +435,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 +444,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 +453,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 +462,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,5 +1,6 @@
|
|||||||
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.*
|
||||||
@ -7,25 +8,20 @@ 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.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.c64.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.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 +63,6 @@ internal interface ICompilationTarget: IStringEncoding {
|
|||||||
else -> return true
|
else -> return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -113,11 +108,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,
|
||||||
|
@ -172,7 +172,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
@ -236,7 +236,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
@ -431,7 +431,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
@ -495,7 +495,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||||
@ -626,7 +626,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||||
@ -885,7 +885,7 @@ object Petscii {
|
|||||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||||
|
@ -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
|
||||||
@ -11,10 +11,11 @@ import prog8.compiler.functions.FSignature
|
|||||||
import prog8.compiler.target.*
|
import prog8.compiler.target.*
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
|
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
|
||||||
import java.io.CharConversionException
|
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,10 +23,10 @@ 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:
|
||||||
@ -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"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,12 +133,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
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,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
pha""")
|
pha""")
|
||||||
}
|
}
|
||||||
|
|
||||||
out(" jmp main.start ; start program / force start proc to be included")
|
jmp("main.start")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun slaballocations() {
|
private fun slaballocations() {
|
||||||
@ -176,14 +181,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)
|
||||||
@ -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
|
||||||
@ -348,7 +356,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 +369,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()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,8 +407,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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())
|
||||||
@ -527,7 +536,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 +571,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 +600,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 +609,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 +621,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 +646,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 +655,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")
|
||||||
@ -686,7 +695,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
is Break -> {
|
is Break -> {
|
||||||
if(loopEndLabels.isEmpty())
|
if(loopEndLabels.isEmpty())
|
||||||
throw AssemblyError("break statement out of context ${stmt.position}")
|
throw AssemblyError("break statement out of context ${stmt.position}")
|
||||||
out(" jmp ${loopEndLabels.peek()}")
|
jmp(loopEndLabels.peek())
|
||||||
}
|
}
|
||||||
is WhileLoop -> translate(stmt)
|
is WhileLoop -> translate(stmt)
|
||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
@ -711,7 +720,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val indexName = asmVariableName(expr.indexer.indexVar!!)
|
val indexVar = expr.indexer.indexExpr as? IdentifierReference
|
||||||
|
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
|
||||||
|
|
||||||
|
val indexName = asmVariableName(indexVar)
|
||||||
if(addOneExtra) {
|
if(addOneExtra) {
|
||||||
// add 1 to the result
|
// add 1 to the result
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
@ -921,7 +933,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 +955,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 +979,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")
|
||||||
}
|
}
|
||||||
@ -997,10 +1011,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 +1024,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 +1078,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 +1112,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 +1131,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 +1194,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 +1237,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 +1251,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)
|
||||||
@ -1329,7 +1312,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 +1326,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 +1350,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 +1386,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,19 @@
|
|||||||
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.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) {
|
||||||
@ -48,7 +51,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"ln", "log2", "sqrt", "rad",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
|
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
"fastrnd8", "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
@ -61,14 +64,83 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||||
"pokew" -> funcPokeW(fcall)
|
"pokew" -> funcPokeW(fcall)
|
||||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||||
else -> TODO("missing asmgen for builtin func ${func.name}")
|
"cmp" -> funcCmp(fcall)
|
||||||
|
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcCmp(fcall: IFunctionCall) {
|
||||||
|
val arg1 = fcall.args[0]
|
||||||
|
val arg2 = fcall.args[1]
|
||||||
|
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
if(dt1 in ByteDatatypes) {
|
||||||
|
if(dt2 in ByteDatatypes) {
|
||||||
|
when (arg2) {
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp #${arg2.number}")
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
if(arg2.addressExpression is NumericLiteralValue) {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||||
|
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("args for cmp() should have same dt")
|
||||||
|
} else {
|
||||||
|
// dt1 is a word
|
||||||
|
if(dt2 in WordDatatypes) {
|
||||||
|
when (arg2) {
|
||||||
|
is IdentifierReference -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy ${asmgen.asmVariableName(arg2)}+1
|
||||||
|
bne +
|
||||||
|
cmp ${asmgen.asmVariableName(arg2)}
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy #>${arg2.number}
|
||||||
|
bne +
|
||||||
|
cmp #<${arg2.number}
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine())
|
||||||
|
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||||
|
asmgen.out("""
|
||||||
|
cpy P8ZP_SCRATCH_W1+1
|
||||||
|
bne +
|
||||||
|
cmp P8ZP_SCRATCH_W1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("args for cmp() should have same dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +157,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,8 +464,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
||||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||||
val indexerExpr = if(indexer.indexVar!=null) indexer.indexVar!! else indexer.indexNum!!
|
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||||
asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
@ -480,11 +547,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
||||||
@ -511,19 +578,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
DataType.ARRAY_UB, DataType.STR -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_sum_f_fac1")
|
asmgen.out(" jsr floats.func_sum_f_fac1")
|
||||||
@ -612,10 +679,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
val firstNum = first.indexer.indexNum
|
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
|
||||||
val firstVar = first.indexer.indexVar
|
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
||||||
val secondNum = second.indexer.indexNum
|
val secondNum = second.indexer.indexExpr as? NumericLiteralValue
|
||||||
val secondVar = second.indexer.indexVar
|
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
||||||
|
|
||||||
if(firstNum!=null && secondNum!=null) {
|
if(firstNum!=null && secondNum!=null) {
|
||||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
||||||
@ -651,12 +718,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 +735,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 +750,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 +864,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 +923,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("""
|
||||||
@ -926,15 +993,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" jsr floats.abs_f_fac1")
|
asmgen.out(" jsr floats.abs_f_fac1")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1)
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -943,6 +1010,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||||
when(func.name) {
|
when(func.name) {
|
||||||
|
"fastrnd8" -> {
|
||||||
|
if(resultToStack)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_fastrnd8_stack")
|
||||||
|
else {
|
||||||
|
asmgen.out(" jsr math.fast_randbyte")
|
||||||
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
"rnd" -> {
|
"rnd" -> {
|
||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
||||||
@ -977,7 +1052,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
|
||||||
@ -1028,7 +1103,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
|
||||||
@ -1292,7 +1367,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 +1383,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,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.ArrayElementTypes
|
import prog8.ast.base.ArrayElementTypes
|
||||||
@ -57,9 +57,9 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
lda $varname
|
lda $varname
|
||||||
$modifiedLabel cmp #0 ; modified
|
$modifiedLabel cmp #0 ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
$incdec $varname
|
$incdec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -117,16 +117,15 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
""")
|
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname
|
+ dec $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
@ -386,23 +385,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 +414,8 @@ $loopLabel""")
|
|||||||
beq $endLabel
|
beq $endLabel
|
||||||
clc
|
clc
|
||||||
adc #${range.step}
|
adc #${range.step}
|
||||||
sta $varname
|
sta $varname""")
|
||||||
jmp $loopLabel""")
|
asmgen.jmp(loopLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
@ -450,9 +451,9 @@ $loopLabel""")
|
|||||||
sta $varname
|
sta $varname
|
||||||
lda $varname+1
|
lda $varname+1
|
||||||
adc #>${range.step}
|
adc #>${range.step}
|
||||||
sta $varname+1
|
sta $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,9 +503,9 @@ $loopLabel""")
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $varname
|
lda $varname
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
dec $varname
|
dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -545,9 +546,9 @@ $loopLabel""")
|
|||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ inc $varname
|
+ inc $varname
|
||||||
bne $loopLabel
|
bne $loopLabel
|
||||||
inc $varname+1
|
inc $varname+1""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,9 +574,9 @@ $loopLabel""")
|
|||||||
+ lda $varname
|
+ lda $varname
|
||||||
bne +
|
bne +
|
||||||
dec $varname+1
|
dec $varname+1
|
||||||
+ dec $varname
|
+ dec $varname""")
|
||||||
jmp $loopLabel
|
asmgen.jmp(loopLabel)
|
||||||
$endLabel""")
|
asmgen.out(endLabel)
|
||||||
asmgen.loopEndLabels.pop()
|
asmgen.loopEndLabels.pop()
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
@ -8,10 +8,10 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -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 -> {
|
||||||
@ -168,12 +172,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
lda P8ESTACK_LO$plusIdxStr,x
|
lda P8ESTACK_LO$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||||
""")
|
""")
|
||||||
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().toLowerCase()}+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().toLowerCase()}+1")
|
||||||
}
|
}
|
||||||
in WordDatatypes ->
|
in WordDatatypes, in IterableDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda P8ESTACK_LO$plusIdxStr,x
|
lda P8ESTACK_LO$plusIdxStr,x
|
||||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||||
@ -318,7 +322,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.*
|
||||||
@ -66,8 +66,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
if(targetArrayIdx.indexer.indexNum!=null) {
|
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * 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 {
|
||||||
@ -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)
|
||||||
@ -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.STRUCT) { "must not be placeholder 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")
|
||||||
@ -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,7 +358,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
|
|
||||||
// special case optimizations
|
// special case optimizations
|
||||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
if(target.kind== TargetStorageKind.VARIABLE) {
|
||||||
if(value is IdentifierReference && valueDt != DataType.STRUCT)
|
if(value is IdentifierReference && valueDt != DataType.STRUCT)
|
||||||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
||||||
|
|
||||||
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -592,7 +590,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().toLowerCase()} $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().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
@ -620,7 +618,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
asmgen.out(" st${regs.toString().toLowerCase()} $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().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||||
else
|
else
|
||||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||||
@ -708,11 +706,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.STR -> {
|
DataType.STR -> throw AssemblyError("cannot typecast a string value")
|
||||||
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
|
|
||||||
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
|
||||||
TODO("assign typecasted string into target var")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -762,7 +756,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")
|
||||||
@ -968,7 +962,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")
|
||||||
@ -1073,11 +1067,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty P8ZP_SCRATCH_W1+1""")
|
sty P8ZP_SCRATCH_W1+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float_from_fac1")
|
asmgen.out(" jsr floats.set_array_float_from_fac1")
|
||||||
@ -1109,11 +1103,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1""")
|
sty P8ZP_SCRATCH_W2+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float")
|
asmgen.out(" jsr floats.set_array_float")
|
||||||
@ -1156,11 +1150,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
ldy #>${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1""")
|
sty P8ZP_SCRATCH_W2+1""")
|
||||||
if(target.array!!.indexer.indexNum!=null) {
|
val constIndex = target.array!!.indexer.constIndex()
|
||||||
val index = target.array.indexer.constIndex()!!
|
if(constIndex!=null) {
|
||||||
asmgen.out(" lda #$index")
|
asmgen.out(" lda #$constIndex")
|
||||||
} else {
|
} else {
|
||||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
|
||||||
asmgen.out(" lda $asmvarname")
|
asmgen.out(" lda $asmvarname")
|
||||||
}
|
}
|
||||||
asmgen.out(" jsr floats.set_array_float")
|
asmgen.out(" jsr floats.set_array_float")
|
||||||
@ -1191,7 +1185,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 {
|
||||||
@ -1295,7 +1289,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 +1298,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 +1323,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,8 +1333,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||||
if(target.register !in Cx16VirtualRegisters)
|
// we make an exception in the type check for assigning something to a cx16 virtual register
|
||||||
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 -> {
|
||||||
@ -1368,7 +1370,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 -> {
|
||||||
@ -1571,7 +1574,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 -> {
|
||||||
@ -1666,7 +1669,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 -> {
|
||||||
@ -1736,7 +1739,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
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(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
|
||||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
|
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+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().toLowerCase()}+1\n")
|
||||||
@ -1757,7 +1760,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 +1779,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 +1800,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 +1845,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 +1861,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
|
||||||
@ -1975,7 +1980,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 +1996,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 +2008,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 +2028,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 +2046,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 +2056,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 +2068,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 +2114,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)
|
||||||
@ -199,9 +199,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
with(target.array!!.indexer) {
|
with(target.array!!.indexer) {
|
||||||
|
val indexNum = indexExpr as? NumericLiteralValue
|
||||||
|
val indexVar = indexExpr as? IdentifierReference
|
||||||
when {
|
when {
|
||||||
indexNum!=null -> {
|
indexNum!=null -> {
|
||||||
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*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 {
|
||||||
@ -245,19 +247,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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,7 +667,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -857,14 +853,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 +880,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 +936,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 +950,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 +974,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1041,7 +1036,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 +1049,49 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
lda math.multiply_words.result+1
|
lda math.multiply_words.result+1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"/" -> TODO("div (u)wordvar/bytevar")
|
"/" -> {
|
||||||
"%" -> TODO("(u)word remainder bytevar")
|
if(dt==DataType.UWORD) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_w_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"%" -> {
|
||||||
|
if(valueDt!=DataType.UBYTE || dt!=DataType.UWORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $otherName
|
||||||
|
ldy #0
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
sta $name
|
||||||
|
lda P8ZP_SCRATCH_W2+1
|
||||||
|
sta $name+1
|
||||||
|
""") }
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
@ -1092,7 +1128,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 +1136,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||||
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1174,7 +1209,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1351,7 +1385,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 +1399,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" eor $name | sta $name")
|
asmgen.out(" eor $name | sta $name")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1406,7 +1439,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1454,7 +1486,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1474,7 +1505,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 +1525,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 +1566,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
@ -1552,7 +1582,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 +1602,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 +1650,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
@ -1644,7 +1673,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
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 +1683,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")
|
||||||
@ -1730,9 +1759,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place not of ubyte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of ubyte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg not")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
||||||
TargetStorageKind.STACK -> TODO("stack not")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1749,9 +1778,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place not of uword array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of uword array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg not")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
||||||
TargetStorageKind.STACK -> TODO("stack not")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("boolean-not of invalid type")
|
else -> throw AssemblyError("boolean-not of invalid type")
|
||||||
@ -1796,9 +1825,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place invert ubyte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert ubyte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg invert")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
||||||
TargetStorageKind.STACK -> TODO("stack invert")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1813,9 +1842,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place invert uword array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert uword array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg invert")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
||||||
TargetStorageKind.STACK -> TODO("stack invert")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
else -> throw AssemblyError("invert of invalid type")
|
||||||
@ -1834,9 +1863,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}""")
|
sta ${target.asmVarname}""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate byte array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate byte array")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg negate")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
||||||
TargetStorageKind.STACK -> TODO("stack negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -1851,10 +1880,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc ${target.asmVarname}+1
|
sbc ${target.asmVarname}+1
|
||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate word array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate word array")
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
|
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
|
||||||
TargetStorageKind.REGISTER -> TODO("reg negate")
|
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
||||||
TargetStorageKind.STACK -> TODO("stack negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -1867,8 +1896,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1
|
sta ${target.asmVarname}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> TODO("in-place negate float array")
|
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate float array")
|
||||||
TargetStorageKind.STACK -> TODO("stack float negate")
|
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack float negate")
|
||||||
else -> throw AssemblyError("weird target kind for float")
|
else -> throw AssemblyError("weird target kind for float")
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,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) {
|
||||||
|
@ -10,7 +10,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>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
@ -80,7 +80,7 @@ X = BinExpr X = LeftExpr
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -5,25 +5,25 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
|
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
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
private val alwaysKeepSubroutines = setOf(
|
private val alwaysKeepSubroutines = setOf(
|
||||||
Pair("main", "start"),
|
Pair("main", "start")
|
||||||
Pair("irq", "irq")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \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, private val asmFileLoader: (filename: String, source: Path)->String) : IAstVisitor {
|
||||||
|
|
||||||
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
@ -80,7 +80,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
||||||
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive == "%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = asmFileLoader(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingSubroutine()
|
val scope = directive.definingSubroutine()
|
||||||
if(scope!=null) {
|
if(scope!=null) {
|
||||||
scanAssemblyCode(asm, directive, scope)
|
scanAssemblyCode(asm, directive, scope)
|
||||||
@ -150,6 +150,17 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
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).plus(otherSub)
|
||||||
|
calledBy[otherSub] = calledBy.getValue(otherSub).plus(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) {
|
||||||
@ -167,7 +178,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(inlineAssembly: InlineAssembly) {
|
override fun visit(inlineAssembly: InlineAssembly) {
|
||||||
// parse inline asm for subroutine calls (jmp, jsr)
|
// parse inline asm for subroutine calls (jmp, jsr, bra)
|
||||||
val scope = inlineAssembly.definingSubroutine()
|
val scope = inlineAssembly.definingSubroutine()
|
||||||
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
||||||
super.visit(inlineAssembly)
|
super.visit(inlineAssembly)
|
||||||
@ -213,7 +224,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkRecursiveCalls(errors: ErrorReporter) {
|
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||||
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)
|
||||||
|
@ -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,7 +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>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
@ -224,7 +224,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.
|
||||||
|
@ -10,11 +10,11 @@ import prog8.ast.statements.ForLoop
|
|||||||
import prog8.ast.statements.VarDecl
|
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>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
@ -39,7 +39,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>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||||
@ -75,7 +75,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 +93,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +179,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.
|
||||||
|
@ -2,16 +2,18 @@ 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
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
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.isEmpty()) {
|
||||||
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.isEmpty()) {
|
||||||
replacer.applyModifications()
|
replacer.applyModifications()
|
||||||
@ -20,7 +22,7 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
if(errors.isEmpty()) {
|
if(errors.isEmpty()) {
|
||||||
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.isEmpty() && optimizer.applyModifications() > 0) {
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
@ -39,8 +41,11 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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,
|
||||||
|
asmFileLoader: (filename: String, source: Path)->String): Int {
|
||||||
|
val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
|
|
||||||
@ -55,8 +60,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,23 +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 java.nio.file.Path
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
|
||||||
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,
|
||||||
|
private val compTarget: ICompilationTarget,
|
||||||
|
asmFileLoader: (filename: String, source: Path)->String
|
||||||
) : AstWalker() {
|
) : AstWalker() {
|
||||||
|
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
private val callgraph = CallGraph(program)
|
private val callgraph = CallGraph(program, asmFileLoader)
|
||||||
|
|
||||||
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("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
if(block.name != program.internedStringsModuleName)
|
||||||
|
errors.warn("removing empty block '${block.name}'", block.position)
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +56,10 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
if(!subroutine.isAsmSubroutine) {
|
||||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
|
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -81,11 +87,9 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||||
// this is a C-64 specific optimization
|
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
|
||||||
val arg = functionCallStatement.args.single()
|
val arg = functionCallStatement.args.single()
|
||||||
val stringVar: IdentifierReference?
|
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||||
stringVar = if(arg is AddressOf) {
|
|
||||||
arg.identifier
|
arg.identifier
|
||||||
} else {
|
} else {
|
||||||
arg as? IdentifierReference
|
arg as? IdentifierReference
|
||||||
@ -96,29 +100,29 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(string!=null) {
|
if(string!=null) {
|
||||||
val pos = functionCallStatement.position
|
val pos = functionCallStatement.position
|
||||||
if (string.value.length == 1) {
|
if (string.value.length == 1) {
|
||||||
val firstCharEncoded = ICompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||||
val chrout = FunctionCallStatement(
|
val chrout = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||||
} else if (string.value.length == 2) {
|
} else if (string.value.length == 2) {
|
||||||
val firstTwoCharsEncoded = ICompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||||
val chrout1 = FunctionCallStatement(
|
val chrout1 = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
val chrout2 = FunctionCallStatement(
|
val chrout2 = FunctionCallStatement(
|
||||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
return listOf(
|
||||||
anonscope.statements.add(chrout1)
|
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
|
||||||
anonscope.statements.add(chrout2)
|
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,7 +216,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 +297,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()
|
||||||
|
@ -3,7 +3,6 @@ package prog8.optimizer
|
|||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
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,20 +10,27 @@ 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
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
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,
|
||||||
|
private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() {
|
||||||
|
|
||||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
||||||
val callgraph = CallGraph(program)
|
val callgraph = CallGraph(program, asmFileLoader)
|
||||||
val removals = mutableListOf<IAstModification>()
|
val removals = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
// remove all subroutines that aren't called, or are empty
|
// remove all subroutines that aren't called, or are empty
|
||||||
|
// NOTE: part of this is also done already in the StatementOptimizer
|
||||||
val entrypoint = program.entrypoint()
|
val entrypoint = program.entrypoint()
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
callgraph.forAllSubroutines(it) { sub ->
|
||||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
val forceOutput = "force_output" in sub.definingBlock().options()
|
||||||
|
if (sub !== entrypoint && !forceOutput && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||||
removals.add(IAstModification.Remove(sub, sub.definingScope()))
|
removals.add(IAstModification.Remove(sub, sub.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +102,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,21 +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.c64.Petscii
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
@ -134,7 +135,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 +148,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 +188,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 +209,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 +222,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 +245,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 +275,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 +294,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 +304,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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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)
|
||||||
@ -490,7 +516,7 @@ 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,7 +527,7 @@ class TestMemory {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
@ -514,7 +540,7 @@ class TestMemory {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
@ -527,7 +553,7 @@ class TestMemory {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
@ -540,7 +566,7 @@ class TestMemory {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
@ -554,7 +580,7 @@ class TestMemory {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
@ -568,7 +594,7 @@ class TestMemory {
|
|||||||
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")
|
@ -396,14 +396,18 @@ galaxy10 {
|
|||||||
sub init(ubyte galaxy10num) {
|
sub init(ubyte galaxy10num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet10.number = 255
|
planet10.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy10num-1 {
|
repeat galaxy10num-1 {
|
||||||
nextgalaxy10()
|
nextgalaxy10()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy10() {
|
sub nextgalaxy10() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -576,8 +580,10 @@ galaxy10 {
|
|||||||
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
planet10.goatsoup_seed[0] = lsb(seed[1])
|
||||||
planet10.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet10.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet10.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet10.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy2 {
|
|||||||
sub init(ubyte galaxy2num) {
|
sub init(ubyte galaxy2num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet2.number = 255
|
planet2.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy2num-1 {
|
repeat galaxy2num-1 {
|
||||||
nextgalaxy2()
|
nextgalaxy2()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy2() {
|
sub nextgalaxy2() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy2 {
|
|||||||
planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet2.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet2.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet2.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet2.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet2.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy3 {
|
|||||||
sub init(ubyte galaxy3num) {
|
sub init(ubyte galaxy3num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet3.number = 255
|
planet3.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy3num-1 {
|
repeat galaxy3num-1 {
|
||||||
nextgalaxy3()
|
nextgalaxy3()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy3() {
|
sub nextgalaxy3() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy3 {
|
|||||||
planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet3.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet3.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet3.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet3.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet3.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy4 {
|
|||||||
sub init(ubyte galaxy4num) {
|
sub init(ubyte galaxy4num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet4.number = 255
|
planet4.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy4num-1 {
|
repeat galaxy4num-1 {
|
||||||
nextgalaxy4()
|
nextgalaxy4()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy4() {
|
sub nextgalaxy4() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy4 {
|
|||||||
planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet4.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet4.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet4.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet4.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet4.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy5 {
|
|||||||
sub init(ubyte galaxy5num) {
|
sub init(ubyte galaxy5num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet5.number = 255
|
planet5.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy5num-1 {
|
repeat galaxy5num-1 {
|
||||||
nextgalaxy5()
|
nextgalaxy5()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy5() {
|
sub nextgalaxy5() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy5 {
|
|||||||
planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet5.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet5.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet5.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet5.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet5.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy6 {
|
|||||||
sub init(ubyte galaxy6num) {
|
sub init(ubyte galaxy6num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet6.number = 255
|
planet6.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy6num-1 {
|
repeat galaxy6num-1 {
|
||||||
nextgalaxy6()
|
nextgalaxy6()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy6() {
|
sub nextgalaxy6() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy6 {
|
|||||||
planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet6.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet6.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet6.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet6.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet6.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy7 {
|
|||||||
sub init(ubyte galaxy7num) {
|
sub init(ubyte galaxy7num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet7.number = 255
|
planet7.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy7num-1 {
|
repeat galaxy7num-1 {
|
||||||
nextgalaxy7()
|
nextgalaxy7()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy7() {
|
sub nextgalaxy7() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy7 {
|
|||||||
planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet7.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet7.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet7.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet7.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet7.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy8 {
|
|||||||
sub init(ubyte galaxy8num) {
|
sub init(ubyte galaxy8num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet8.number = 255
|
planet8.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy8num-1 {
|
repeat galaxy8num-1 {
|
||||||
nextgalaxy8()
|
nextgalaxy8()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy8() {
|
sub nextgalaxy8() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -577,7 +581,10 @@ galaxy8 {
|
|||||||
planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet8.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet8.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet8.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet8.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet8.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -396,14 +396,18 @@ galaxy9 {
|
|||||||
sub init(ubyte galaxy9num) {
|
sub init(ubyte galaxy9num) {
|
||||||
number = 1
|
number = 1
|
||||||
planet9.number = 255
|
planet9.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxy9num-1 {
|
repeat galaxy9num-1 {
|
||||||
nextgalaxy9()
|
nextgalaxy9()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy9() {
|
sub nextgalaxy9() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -576,8 +580,10 @@ galaxy9 {
|
|||||||
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
planet9.goatsoup_seed[0] = lsb(seed[1])
|
||||||
planet9.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet9.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet9.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet9.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -406,14 +406,18 @@ galaxy {
|
|||||||
sub init(ubyte galaxynum) {
|
sub init(ubyte galaxynum) {
|
||||||
number = 1
|
number = 1
|
||||||
planet.number = 255
|
planet.number = 255
|
||||||
seed = [base0, base1, base2]
|
seed[0] = base0
|
||||||
|
seed[1] = base1
|
||||||
|
seed[2] = base2
|
||||||
repeat galaxynum-1 {
|
repeat galaxynum-1 {
|
||||||
nextgalaxy()
|
nextgalaxy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub nextgalaxy() {
|
sub nextgalaxy() {
|
||||||
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
seed[0] = twist(seed[0])
|
||||||
|
seed[1] = twist(seed[1])
|
||||||
|
seed[2] = twist(seed[2])
|
||||||
number++
|
number++
|
||||||
if number==9
|
if number==9
|
||||||
number = 1
|
number = 1
|
||||||
@ -587,7 +591,10 @@ galaxy {
|
|||||||
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
}
|
}
|
||||||
|
|
||||||
planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
planet.goatsoup_seed[0] = lsb(seed[1])
|
||||||
|
planet.goatsoup_seed[1] = msb(seed[1])
|
||||||
|
planet.goatsoup_seed[2] = lsb(seed[2])
|
||||||
|
planet.goatsoup_seed[3] = seed2_msb
|
||||||
}
|
}
|
||||||
|
|
||||||
sub tweakseed() {
|
sub tweakseed() {
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +121,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.indexNum?.accept(this)
|
decl.arraysize!!.indexExpr.accept(this)
|
||||||
decl.arraysize!!.indexVar?.accept(this)
|
|
||||||
}
|
}
|
||||||
if(decl.isArray)
|
if(decl.isArray)
|
||||||
output("]")
|
output("]")
|
||||||
@ -365,8 +364,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
|
||||||
@ -244,10 +241,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,13 +256,32 @@ 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"
|
||||||
|
|
||||||
|
init {
|
||||||
|
// insert a container module for all interned strings later
|
||||||
|
if(modules.firstOrNull()?.name != internedStringsModuleName) {
|
||||||
|
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of(""))
|
||||||
|
modules.add(0, internedStringsModule)
|
||||||
|
val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY)
|
||||||
|
internedStringsModule.statements.add(block)
|
||||||
|
internedStringsModule.linkParents(this)
|
||||||
|
internedStringsModule.program = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun entrypoint(): Subroutine? {
|
fun entrypoint(): Subroutine? {
|
||||||
val mainBlocks = allBlocks().filter { it.name=="main" }
|
val mainBlocks = allBlocks().filter { it.name=="main" }
|
||||||
@ -274,6 +294,22 @@ class Program(val name: String, val modules: MutableList<Module>, val builtinFun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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}", null, 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>() }
|
||||||
|
|
||||||
override val position: Position = Position.DUMMY
|
override val position: Position = Position.DUMMY
|
||||||
|
@ -60,7 +60,13 @@ enum class DataType {
|
|||||||
enum class CpuRegister {
|
enum class CpuRegister {
|
||||||
A,
|
A,
|
||||||
X,
|
X,
|
||||||
Y
|
Y;
|
||||||
|
|
||||||
|
fun asRegisterOrPair(): RegisterOrPair = when(this) {
|
||||||
|
A -> RegisterOrPair.A
|
||||||
|
X -> RegisterOrPair.X
|
||||||
|
Y -> RegisterOrPair.Y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RegisterOrPair {
|
enum class RegisterOrPair {
|
||||||
|
@ -6,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?
|
||||||
@ -478,15 +480,11 @@ 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
|
||||||
}
|
}
|
||||||
@ -516,8 +514,6 @@ 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)}
|
||||||
@ -546,6 +542,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
return type==other.type && value.contentEquals(other.value)
|
return type==other.type && value.contentEquals(other.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun memsize(memsizer: IMemSizer): Int {
|
||||||
|
if(type.isKnown) {
|
||||||
|
val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT))
|
||||||
|
return memsizer.memorySize(eltType) * value.size
|
||||||
|
}
|
||||||
|
else throw IllegalArgumentException("array datatype is not yet known")
|
||||||
|
}
|
||||||
|
|
||||||
fun guessDatatype(program: Program): InferredTypes.InferredType {
|
fun guessDatatype(program: Program): InferredTypes.InferredType {
|
||||||
// Educated guess of the desired array literal's datatype.
|
// Educated guess of the desired array literal's datatype.
|
||||||
// If it's inside a for loop, assume the data type of the loop variable is what we want.
|
// If it's inside a for loop, assume the data type of the loop variable is what we want.
|
||||||
@ -763,17 +767,6 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
|
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? {
|
fun firstStructVarName(program: Program): String? {
|
||||||
// take the name of the first struct member of the structvariable instead
|
// take the name of the first struct member of the structvariable instead
|
||||||
// if it's just a regular variable, return null.
|
// if it's just a regular variable, return null.
|
||||||
@ -818,7 +811,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)
|
||||||
|
@ -176,12 +176,6 @@ 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 =
|
||||||
@ -227,14 +221,20 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
value?.linkParents(this)
|
value?.linkParents(this)
|
||||||
if(structName!=null) {
|
if(structName!=null) {
|
||||||
val structStmt = definingScope().lookup(listOf(structName), this)
|
val structStmt = definingScope().lookup(listOf(structName), this)
|
||||||
if(structStmt!=null)
|
if(structStmt!=null) {
|
||||||
struct = definingScope().lookup(listOf(structName), this) as StructDecl
|
val node = definingScope().lookup(listOf(structName), this)
|
||||||
|
if(node is StructDecl)
|
||||||
|
struct = node
|
||||||
|
else
|
||||||
|
datatypeErrors.add(SyntaxError("invalid datatype declaration", position))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
// TODO the check that node===value is too strict sometimes, but leaving it out allows for bugs to creep through ... :( Perhaps check when adding the replace if there is already a replace on the same node?
|
require(replacement is Expression && node===value)
|
||||||
require(replacement is Expression)
|
// NOTE: ideally you also want to check that node===value but this sometimes crashes the optimizer when queueing multiple node replacements
|
||||||
|
// just accept the risk of having the wrong node specified in the IAstModification object...
|
||||||
value = replacement
|
value = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -280,46 +280,19 @@ class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Posit
|
|||||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||||
|
|
||||||
|
|
||||||
class ArrayIndex(var origExpression: Expression?, // will be replaced later by either the number or the identifier
|
class ArrayIndex(var indexExpr: Expression,
|
||||||
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 +302,16 @@ 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)
|
|
||||||
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() {
|
||||||
@ -651,14 +611,11 @@ class AsmGenInfo {
|
|||||||
// 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,
|
||||||
@ -734,7 +691,7 @@ 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1006,6 +963,9 @@ class StructDecl(override val name: String,
|
|||||||
val numberOfElements: Int
|
val numberOfElements: Int
|
||||||
get() = this.statements.size
|
get() = this.statements.size
|
||||||
|
|
||||||
|
fun memsize(memsizer: IMemSizer) =
|
||||||
|
statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum()
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
@ -158,9 +158,19 @@ abstract class AstWalker {
|
|||||||
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
|
||||||
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
||||||
|
// private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>()
|
||||||
|
|
||||||
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 {
|
||||||
@ -169,6 +179,7 @@ abstract class AstWalker {
|
|||||||
}
|
}
|
||||||
val amount = modifications.size
|
val amount = modifications.size
|
||||||
modifications.clear()
|
modifications.clear()
|
||||||
|
// modificationsReplacedNodes.clear()
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
@ -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'
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,14 +50,15 @@ Language features
|
|||||||
- Provide high level programming constructs but at the same time stay close to the metal;
|
- Provide high level programming constructs but at the same time stay close to the metal;
|
||||||
still able to directly use memory addresses and ROM subroutines,
|
still able to directly use memory addresses and ROM subroutines,
|
||||||
and inline assembly to have full control when every register, cycle or byte matters
|
and inline assembly to have full control when every register, cycle or byte matters
|
||||||
- Arbitrary number of subroutine parameters, Complex nested expressions are possible
|
- Subroutines with parameters and return values
|
||||||
- No stack frame allocations because parameters and local variables are automatically allocated statically
|
- complex nested expressions are possible
|
||||||
|
- Variables are allocated statically
|
||||||
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||||
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
|
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
|
||||||
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
||||||
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||||
- 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
|
||||||
|
@ -105,7 +105,7 @@ 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 +182,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.
|
||||||
|
|
||||||
|
|
||||||
|
@ -522,12 +522,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
|
||||||
@ -739,9 +739,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 +759,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 +791,12 @@ sort(array)
|
|||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
cmp(x,y)
|
||||||
|
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
|
||||||
|
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
|
||||||
|
Normally you should just use a comparison expression (``x < y``)
|
||||||
|
|
||||||
lsb(x)
|
lsb(x)
|
||||||
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
||||||
|
|
||||||
@ -824,6 +833,9 @@ rndw()
|
|||||||
rndf()
|
rndf()
|
||||||
returns a pseudo-random float between 0.0 and 1.0
|
returns a pseudo-random float between 0.0 and 1.0
|
||||||
|
|
||||||
|
fastrnd8()
|
||||||
|
returns a pseudo-random byte from 0..255 (using a fast but not very good rng)
|
||||||
|
|
||||||
rol(x)
|
rol(x)
|
||||||
Rotate the bits in x (byte or word) one position to the left.
|
Rotate the bits in x (byte or word) one position to the left.
|
||||||
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
||||||
|
@ -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 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>]]
|
||||||
@ -513,7 +514,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.
|
||||||
@ -591,7 +592,7 @@ flag such as Carry (Pc).
|
|||||||
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
|
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
|
||||||
directly instead of a call to them. Note that it is literal copy-paste of code that is done,
|
directly instead of a call to them. Note that it is literal copy-paste of code that is done,
|
||||||
so make sure the assembly is actually written to behave like such - which probably means you
|
so make sure the assembly is actually written to behave like such - which probably means you
|
||||||
don't want a ``rts`` or ``jmp`` in it!
|
don't want a ``rts`` or ``jmp`` or ``bra`` in it!
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
@ -772,11 +773,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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Currently there are two machines that are supported as compiler target (selectab
|
|||||||
This chapter explains the relevant system details of these machines.
|
This chapter explains the relevant system details of these machines.
|
||||||
|
|
||||||
.. hint::
|
.. hint::
|
||||||
If you only use standard kernel and prog8 library routines,
|
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)!
|
it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
@ -139,26 +139,17 @@ IRQ Handling
|
|||||||
============
|
============
|
||||||
|
|
||||||
Normally, the system's default IRQ handling is not interfered with.
|
Normally, the system's default IRQ handling is not interfered with.
|
||||||
You can however install your own IRQ handler.
|
You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block).
|
||||||
This is possible ofcourse by doing it all using customized inline assembly,
|
There are a few library routines available to make setting up C-64 60hz IRQs and Raster IRQs a lot easier (no assembly code required).
|
||||||
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
|
||||||
|
|
||||||
For the C64 these routines are::
|
For the C64 these routines are::
|
||||||
|
|
||||||
c64.set_irqvec()
|
c64.set_irq(uword handler_address, boolean useKernal)
|
||||||
c64.set_irqvec_excl()
|
c64.set_rasterirq(uword handler_address, uword rasterline, boolean useKernal)
|
||||||
|
c64.restore_irq() ; set everything back to the systems default irq handler
|
||||||
|
|
||||||
c64.set_rasterirq( <raster line> )
|
And for the Commander X16:
|
||||||
c64.set_rasterirq_excl( <raster line> )
|
|
||||||
|
|
||||||
c64.restore_irqvec() ; set it back to the systems default irq handler
|
|
||||||
|
|
||||||
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
|
||||||
as a subroutine ``irq`` in the module ``irq`` so like this::
|
|
||||||
|
|
||||||
irq {
|
|
||||||
sub irq() {
|
|
||||||
; ... irq handling here ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
cx16.set_irq(uword handler_address, boolean useKernal) ; vsync irq
|
||||||
|
cx16.set_rasterirq(uword handler_address, uword rasterline) ; note: disables kernal irq handler! sys.wait() won't work anymore
|
||||||
|
cx16.restore_irq() ; set everything back to the systems default irq handler
|
||||||
|
@ -50,7 +50,7 @@ Calling the routine is just a simple JSR instruction, but the other two work lik
|
|||||||
``asmsub`` routines
|
``asmsub`` routines
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
These are usually declarations of kernel (ROM) routines or low-level assembly only routines,
|
These are usually declarations of kernal (ROM) routines or low-level assembly only routines,
|
||||||
that have their arguments solely passed into specific registers.
|
that have their arguments solely passed into specific registers.
|
||||||
Sometimes even via a processor status flag such as the Carry flag.
|
Sometimes even via a processor status flag such as the Carry flag.
|
||||||
Return values also via designated registers.
|
Return values also via designated registers.
|
||||||
|
@ -2,21 +2,19 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
|
- optimize several inner loops in gfx2
|
||||||
|
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||||
|
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
||||||
|
- add a flood fill routine to gfx2?
|
||||||
|
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color) ?
|
||||||
|
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
||||||
- refactor the asmgen into their own submodule?
|
- refactor the asmgen into their own submodule?
|
||||||
- refactor the compiler optimizers into their own submodule?
|
- refactor the compiler optimizers into their own submodule?
|
||||||
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
|
||||||
- optimize several inner loops in gfx2 (highres 4 color mode)
|
|
||||||
- use the 65c02 bit clear/set/test instructions for single-bit operations
|
|
||||||
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
|
|
||||||
- add a flood fill routine to gfx2?
|
|
||||||
- add sound to the cx16 tehtriz
|
|
||||||
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
|
||||||
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
||||||
- add a compiler option to not remove unused subroutines. this allows for building library programs
|
- add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
|
||||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
|
||||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||||
- c64: make the graphics.BITMAP_ADDRESS configurable
|
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||||
- some support for recursive subroutines?
|
- some support for recursive subroutines?
|
||||||
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
|
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
|
||||||
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
|
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
|
||||||
@ -28,30 +26,12 @@ More optimizations
|
|||||||
Add more compiler optimizations to the existing ones.
|
Add more compiler optimizations to the existing ones.
|
||||||
|
|
||||||
- further optimize assignment codegeneration, such as the following:
|
- further optimize assignment codegeneration, such as the following:
|
||||||
|
- rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?)
|
||||||
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
The eval stack is now a split lsb/msb stack using X as the stackpointer.
|
|
||||||
Is it easier/faster to just use a single page unsplit stack?
|
|
||||||
It could then even be moved into the zeropage to reduce code size and slowness.
|
|
||||||
|
|
||||||
Or just move the LSB portion into a slab of the zeropage.
|
|
||||||
|
|
||||||
Allocate a fixed word in ZP that is the Top Of Stack value so we can always operate on TOS directly
|
|
||||||
without having to index with X into the eval stack all the time?
|
|
||||||
This could GREATLY improve code size and speed for operations that work on just a single value.
|
|
||||||
|
|
||||||
|
|
||||||
Bug Fixing
|
|
||||||
^^^^^^^^^^
|
|
||||||
Ofcourse there are always bugs to fix ;)
|
|
||||||
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
144
examples/animals.p8
Normal file
144
examples/animals.p8
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
%import textio
|
||||||
|
%import string
|
||||||
|
|
||||||
|
; Animal guessing game where the computer gets smarter every time.
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
|
|
||||||
|
main {
|
||||||
|
const ubyte database_size = 100
|
||||||
|
|
||||||
|
uword animal_names_buf
|
||||||
|
uword questions_buf
|
||||||
|
uword animal_names_ptr
|
||||||
|
uword questions_ptr
|
||||||
|
|
||||||
|
uword[database_size] animals
|
||||||
|
uword[database_size] questions
|
||||||
|
uword[database_size] answers_questions
|
||||||
|
uword[database_size] answers_animals
|
||||||
|
ubyte new_animal_number
|
||||||
|
ubyte new_question_number
|
||||||
|
str userinput = "x"*80
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
; initialize the database
|
||||||
|
animal_names_buf = memory("animalnames", 500)
|
||||||
|
questions_buf = memory("questions", 2000)
|
||||||
|
animal_names_ptr = animal_names_buf
|
||||||
|
questions_ptr = questions_buf
|
||||||
|
|
||||||
|
animals[0] = 0
|
||||||
|
animals[1] = "dolphin"
|
||||||
|
animals[2] = "eagle"
|
||||||
|
animals[3] = "horse"
|
||||||
|
new_animal_number = 4
|
||||||
|
|
||||||
|
questions[0] = 0
|
||||||
|
questions[1] = "does it swim"
|
||||||
|
questions[2] = "can it fly"
|
||||||
|
new_question_number = 3
|
||||||
|
|
||||||
|
answers_questions[0] = mkword(0, 0)
|
||||||
|
answers_questions[1] = mkword(0, 2)
|
||||||
|
answers_questions[2] = mkword(0, 0)
|
||||||
|
|
||||||
|
answers_animals[0] = mkword(0, 0)
|
||||||
|
answers_animals[1] = mkword(1, 0)
|
||||||
|
answers_animals[2] = mkword(2, 3)
|
||||||
|
|
||||||
|
; play the game
|
||||||
|
game()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub game() {
|
||||||
|
repeat {
|
||||||
|
ubyte current_question = 1
|
||||||
|
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
|
||||||
|
ubyte guessed = false
|
||||||
|
while not guessed {
|
||||||
|
txt.print(questions[current_question])
|
||||||
|
txt.print("? ")
|
||||||
|
if txt.input_chars(userinput) {
|
||||||
|
txt.nl()
|
||||||
|
ubyte animal_number
|
||||||
|
if userinput[0]=='y' {
|
||||||
|
animal_number = msb(answers_animals[current_question])
|
||||||
|
if animal_number {
|
||||||
|
guess(current_question, true, animal_number)
|
||||||
|
guessed = true
|
||||||
|
} else {
|
||||||
|
current_question = msb(answers_questions[current_question])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if userinput[0]=='n' {
|
||||||
|
animal_number = lsb(answers_animals[current_question])
|
||||||
|
if animal_number {
|
||||||
|
guess(current_question, false, animal_number)
|
||||||
|
guessed = true
|
||||||
|
} else {
|
||||||
|
current_question = lsb(answers_questions[current_question])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
txt.print("answer (y)es or (n)o please.\n")
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
txt.nl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub guess(ubyte question_number, ubyte given_answer_yesno, ubyte animal_number) {
|
||||||
|
txt.print("is it a ")
|
||||||
|
txt.print(animals[animal_number])
|
||||||
|
txt.print("? ")
|
||||||
|
txt.input_chars(userinput)
|
||||||
|
if userinput[0] == 'y' {
|
||||||
|
txt.print("\n\nsee, i knew it!\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
str name = "x"*30
|
||||||
|
txt.print("\n\ni give up. what is it? ")
|
||||||
|
txt.input_chars(name)
|
||||||
|
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
|
||||||
|
txt.print(animals[animal_number])
|
||||||
|
txt.print(" and a ")
|
||||||
|
txt.print(name)
|
||||||
|
txt.print("? ")
|
||||||
|
txt.input_chars(userinput)
|
||||||
|
txt.print("\nfor a ")
|
||||||
|
txt.print(name)
|
||||||
|
txt.print(", what is the answer to that; yes or no? ")
|
||||||
|
str answer = "x"*10
|
||||||
|
txt.input_chars(answer)
|
||||||
|
|
||||||
|
animals[new_animal_number] = animal_names_ptr
|
||||||
|
questions[new_question_number] = questions_ptr
|
||||||
|
animal_names_ptr += string.copy(name, animal_names_ptr)+1 ; store animal name in buffer
|
||||||
|
questions_ptr += string.copy(userinput, questions_ptr)+1 ; store question in buffer
|
||||||
|
|
||||||
|
answers_questions[new_question_number] = mkword(0, 0)
|
||||||
|
if answer[0]=='y'
|
||||||
|
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
|
||||||
|
else
|
||||||
|
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
|
||||||
|
|
||||||
|
uword previous_animals = answers_animals[question_number]
|
||||||
|
uword previous_questions = answers_questions[question_number]
|
||||||
|
if given_answer_yesno {
|
||||||
|
answers_animals[question_number] = mkword(0, lsb(previous_animals))
|
||||||
|
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
|
||||||
|
} else {
|
||||||
|
answers_animals[question_number] = mkword(msb(previous_animals), 0)
|
||||||
|
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
new_animal_number++
|
||||||
|
new_question_number++
|
||||||
|
|
||||||
|
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ main {
|
|||||||
|
|
||||||
c64.SCROLX &= %11110111 ; 38 column mode
|
c64.SCROLX &= %11110111 ; 38 column mode
|
||||||
|
|
||||||
c64.set_rasterirq(200) ; enable animation
|
c64.set_rasterirq(&irq.irq, 200, false) ; enable animation via raster interrupt
|
||||||
|
|
||||||
ubyte target_height = 10
|
ubyte target_height = 10
|
||||||
ubyte active_height = 24
|
ubyte active_height = 24
|
||||||
@ -42,7 +42,7 @@ main {
|
|||||||
active_height--
|
active_height--
|
||||||
upwards = false
|
upwards = false
|
||||||
} else {
|
} else {
|
||||||
target_height = 8 + rnd() % 16
|
target_height = 8 + fastrnd8() % 16
|
||||||
if upwards
|
if upwards
|
||||||
mountain = 233
|
mountain = 233
|
||||||
else
|
else
|
||||||
@ -57,7 +57,7 @@ main {
|
|||||||
txt.scroll_left(true)
|
txt.scroll_left(true)
|
||||||
|
|
||||||
; float the balloon
|
; float the balloon
|
||||||
if rnd() & %10000
|
if fastrnd8() & %10000
|
||||||
c64.SPXY[1] ++
|
c64.SPXY[1] ++
|
||||||
else
|
else
|
||||||
c64.SPXY[1] --
|
c64.SPXY[1] --
|
||||||
@ -71,10 +71,10 @@ main {
|
|||||||
txt.setcc(39, yy, 160, 8) ; draw mountain
|
txt.setcc(39, yy, 160, 8) ; draw mountain
|
||||||
}
|
}
|
||||||
|
|
||||||
yy = rnd()
|
yy = fastrnd8()
|
||||||
if yy > 100 {
|
if yy > 100 {
|
||||||
; draw a star
|
; draw a star
|
||||||
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
txt.setcc(39, yy % (active_height-1), '.', fastrnd8())
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 200 {
|
if yy > 200 {
|
||||||
@ -85,7 +85,7 @@ main {
|
|||||||
tree = 88
|
tree = 88
|
||||||
else if yy & %00100000 != 0
|
else if yy & %00100000 != 0
|
||||||
tree = 65
|
tree = 65
|
||||||
if rnd() > 130
|
if fastrnd8() > 130
|
||||||
treecolor = 13
|
treecolor = 13
|
||||||
txt.setcc(39, active_height, tree, treecolor)
|
txt.setcc(39, active_height, tree, treecolor)
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ main {
|
|||||||
; Setup Starting Ball Positions
|
; Setup Starting Ball Positions
|
||||||
ubyte lp
|
ubyte lp
|
||||||
for lp in 0 to ballCount-1 {
|
for lp in 0 to ballCount-1 {
|
||||||
BX[lp] = rnd() % txt.DEFAULT_WIDTH
|
BX[lp] = fastrnd8() % txt.DEFAULT_WIDTH
|
||||||
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
|
BY[lp] = fastrnd8() % txt.DEFAULT_HEIGHT
|
||||||
BC[lp] = rnd() & 15
|
BC[lp] = fastrnd8() & 15
|
||||||
DX[lp] = rnd() & 1
|
DX[lp] = fastrnd8() & 1
|
||||||
DY[lp] = rnd() & 1
|
DY[lp] = fastrnd8() & 1
|
||||||
void rnd()
|
void fastrnd8()
|
||||||
}
|
}
|
||||||
|
|
||||||
; start clock
|
; start clock
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user