Compare commits

...

49 Commits
v6.0 ... v6.1

Author SHA1 Message Date
53f0318187 version 6.1 2021-02-14 00:07:45 +01:00
5e6e711f33 optimize pokew() 2021-02-14 00:05:57 +01:00
78af2cd4dc optimize peekw() 2021-02-13 23:52:08 +01:00
02cb237623 added poke() and pokew() builtin functions 2021-02-13 23:16:50 +01:00
cc0f19653e added peek() and peekw() builtin functions 2021-02-13 22:38:39 +01:00
4fff150c7b fixed mkword() bug 2021-02-13 22:00:13 +01:00
f6136891cc optimized for loop over const bytes, fixed downto 1 2021-02-13 13:46:02 +01:00
1e22170302 added graphical starmaps to textelite 2021-02-11 00:23:36 +01:00
bdda6f502a textelite output cleanups and alignments 2021-02-10 23:19:07 +01:00
1bbd77fddb added txt.column() 2021-02-10 22:47:49 +01:00
321fdd10d1 ported tehtriz to Cx16 2021-02-10 21:55:14 +01:00
9867dfcdeb ported tehtriz to Cx16 2021-02-10 21:44:35 +01:00
7c09ac632c got rid of the --longOptionNames in the cli argparser 2021-02-10 21:26:46 +01:00
3502f65332 reducing dependencies 2021-02-09 19:03:21 +01:00
628390c3b5 reducing dependencies 2021-02-09 18:56:47 +01:00
bc37097df2 reducing dependencies 2021-02-09 18:49:25 +01:00
7d98275763 reducing dependencies 2021-02-09 02:06:27 +01:00
d6ffb549f6 reducing dependencies 2021-02-09 01:47:05 +01:00
bcd0db984d reducing ast dependencies - moved ErrorReporter back to compiler module 2021-02-09 01:15:31 +01:00
d9244f22c2 reducing ast dependencies - separate Ast compilation module 2021-02-09 01:06:11 +01:00
c97d76dbf2 reducing ast dependencies 2021-02-09 00:05:56 +01:00
9e05e97d7f reducing ast dependencies 2021-02-07 19:38:20 +01:00
1070dedd7c todo 2021-02-07 19:08:47 +01:00
ccd1516637 reducing ast dependencies 2021-02-07 18:44:38 +01:00
f1f51a01c6 reducing ast dependencies 2021-02-07 18:34:55 +01:00
be75b8dbe5 reducing ast dependencies 2021-02-07 07:05:00 +01:00
02fae0e722 reducing ast dependencies 2021-02-07 06:50:59 +01:00
e35b739579 reducing ast dependencies 2021-02-07 06:39:08 +01:00
34aa6cc8a2 compiler checks for conflicting register usage in sub arguments vs target parameter registers 2021-02-07 05:25:50 +01:00
d7a6b20028 todo 2021-02-07 01:14:10 +01:00
eb2d5bb1f8 fix bank arg error in gfx2.position 2021-02-06 16:58:17 +01:00
cefef3d1be todo 2021-02-06 15:22:31 +01:00
cc96ab7a9b assignment source now also treats cx16.r[0-15] as registers
no longer create useless assignment code for r0=r0
2021-02-06 13:01:45 +01:00
49ea31c0a4 fix shift signed word right 2021-02-06 01:23:31 +01:00
f1478d776b fix vertical line highres 4color 2021-02-05 18:09:21 +01:00
40e4cfb686 amiga 2021-02-04 17:47:52 +01:00
76f459ee95 amiga 2021-02-02 23:09:03 +01:00
c478718019 fixed and optimized horiz_line for highres 4c 2021-02-01 22:03:10 +01:00
c27248a58b amiga 2021-01-29 23:52:29 +01:00
51bc539468 added palette.set_rgb() 2021-01-29 02:46:07 +01:00
2395863e7e asmsubs: fix clobbering and optimize register usage for loading the arguments 2021-01-29 01:52:49 +01:00
69c459c8ac gfx2 highres 4colors 2021-01-28 22:28:14 +01:00
c8855b2b10 better error msg 2021-01-27 02:40:56 +01:00
a910c0fddb gfx2 highres 4colors 2021-01-27 02:31:20 +01:00
fd55611cac asmsubs: don't use stack for simple lsb/msb/mkword arguments 2021-01-27 01:41:55 +01:00
52f6be2bb0 gfx2: changed screen mode numbering to a more intuitive sequence 2021-01-26 18:17:20 +01:00
857f930dc2 amiga 2021-01-26 00:09:42 +01:00
dd2c436dc6 tweaked repeat 2021-01-25 23:39:54 +01:00
9f047ba752 palette.set_monochrome() now has 2 arguments: screen and draw color RGB values 2021-01-24 04:15:15 +01:00
85 changed files with 3467 additions and 1660 deletions

View File

@ -3,14 +3,11 @@
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true"> <inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages> <Languages>
<language minSize="100" isEnabled="false" name="JavaScript" />
<language isEnabled="false" name="Groovy" />
<language isEnabled="false" name="Style Sheets" />
<language minSize="70" name="Kotlin" /> <language minSize="70" name="Kotlin" />
<language isEnabled="false" name="TypeScript" /> <language isEnabled="false" name="Groovy" />
<language isEnabled="false" name="ActionScript" />
</Languages> </Languages>
</inspection_tool> </inspection_tool>
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true"> <inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
<option name="processCode" value="false" /> <option name="processCode" value="false" />
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />

1
.idea/modules.xml generated
View File

@ -3,6 +3,7 @@
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" /> <module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" /> <module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" /> <module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" /> <module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />

View File

@ -1,7 +1,7 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" version "1.4.21" id "org.jetbrains.kotlin.jvm" version "1.4.30"
id 'org.jetbrains.dokka' version "0.9.18" id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '6.1.0' id 'com.github.johnrengelman.shadow' version '6.1.0'
} }
@ -18,14 +18,12 @@ repositories {
def prog8version = rootProject.file('compiler/res/version.txt').text.trim() def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies { dependencies {
implementation project(':parser') implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.antlr:antlr4-runtime:4.8'
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.1' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.1'
// implementation 'net.razorvine:ksim65:1.8' // implementation 'net.razorvine:ksim65:1.8'
// implementation "com.github.hypfvieh:dbus-java:3.2.4" // implementation "com.github.hypfvieh:dbus-java:3.2.4"
implementation project(':parser')
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'

View File

@ -11,9 +11,8 @@
<orderEntry type="jdk" jdkName="11" jdkType="JavaSDK" /> <orderEntry type="jdk" jdkName="11" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="parser" />
<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="library" name="antlr-runtime-4.9" level="project" /> <orderEntry type="module" module-name="compilerAst" />
</component> </component>
</module> </module>

View File

@ -34,6 +34,7 @@ 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)

View File

@ -31,6 +31,16 @@ sub spc() {
txt.chrout(' ') txt.chrout(' ')
} }
asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{
sec
jsr c64.PLOT
tay
clc
jmp c64.PLOT
}}
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color. ; ---- fill the character screen with the given fill character and character color.

View File

@ -1,33 +1,43 @@
%target cx16 %target cx16
; Bitmap pixel graphics module 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.
; This only works on the Cx16. No text layer is currently shown, text can be drawn as part of the bitmap itself. ; (These modes are not supported by the documented GRAPH_xxxx kernel routines)
; Note: for compatible graphics code that words on C64 too, use the "graphics" module instead. ;
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module. ; 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 color palette manipulation, use the "palette" module or write Vera registers yourself.
; Note: this library implements code for various resolutions and color depths. This takes up memory.
; If you're memory constrained you should probably not use this built-in library,
; but make a copy in your project only containing the code for the required resolution.
;
;
; SCREEN MODE LIST:
; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (unsupported TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (unsupported TODO not yet implemented)
; mode 4 = bitmap 320 x 240 x 256c
; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c (unsupported TODO being implemented)
; mode 7 = bitmap 640 x 480 x 16c (unsupported 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?
gfx2 { gfx2 {
; read-only control variables: ; read-only control variables:
ubyte active_mode = 255 ubyte active_mode = 0
uword width = 0 uword width = 0
uword height = 0 uword height = 0
ubyte bpp = 0 ubyte bpp = 0
ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes ubyte monochrome_dont_stipple_flag = false ; set to false to enable stippling mode in monochrome displaymodes
sub screen_mode(ubyte mode) { sub screen_mode(ubyte mode) {
; mode 0 = bitmap 320 x 240 x 1c monochrome
; mode 1 = bitmap 320 x 240 x 256c
; mode 128 = bitmap 640 x 480 x 1c monochrome
; ...other modes?
; copy the lower-case charset to the upper part of the vram, so we can use it later to plot text
when mode { when mode {
0 -> { 1 -> {
; 320 x 240 x 1c ; lores monchrome
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
@ -38,8 +48,9 @@ gfx2 {
height = 240 height = 240
bpp = 1 bpp = 1
} }
1 -> { ; TODO modes 2, 3 not yet implemented
; 320 x 240 x 256c 4 -> {
; lores 256c
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
@ -50,8 +61,8 @@ gfx2 {
height = 240 height = 240
bpp = 8 bpp = 8
} }
128 -> { 5 -> {
; 640 x 480 x 1c ; highres 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 = 128 cx16.VERA_DC_HSCALE = 128
cx16.VERA_DC_VSCALE = 128 cx16.VERA_DC_VSCALE = 128
@ -62,13 +73,27 @@ gfx2 {
height = 480 height = 480
bpp = 1 bpp = 1
} }
255 -> { 6 -> {
; highres 4c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 128
cx16.VERA_DC_VSCALE = 128
cx16.VERA_L1_CONFIG = %00000101
cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = %00000001
width = 640
height = 480
bpp = 2
}
; modes 7 and 8 not supported due to lack of VRAM
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
c64.CINT() ; back to text mode c64.CINT() ; back to text mode
width = 0 width = 0
height = 0 height = 0
bpp = 0 bpp = 0
mode = 0
} }
} }
@ -81,21 +106,28 @@ gfx2 {
monochrome_stipple(false) monochrome_stipple(false)
position(0, 0) position(0, 0)
when active_mode { when active_mode {
0 -> { 1 -> {
; 320 x 240 x 1c ; lores monochrome
repeat 240/2/8 repeat 240/2/8
cs_innerloop640() cs_innerloop640()
} }
1 -> { ; TODO mode 2, 3
; 320 x 240 x 256c 4 -> {
; lores 256c
repeat 240/2 repeat 240/2
cs_innerloop640() cs_innerloop640()
} }
128 -> { 5 -> {
; 640 x 480 x 1c ; highres monochrome
repeat 480/8 repeat 480/8
cs_innerloop640() cs_innerloop640()
} }
6 -> {
; highres 4c
repeat 480/4
cs_innerloop640()
}
; modes 7 and 8 not supported due to lack of VRAM
} }
position(0, 0) position(0, 0)
} }
@ -130,30 +162,8 @@ gfx2 {
if length==0 if length==0
return return
when active_mode { when active_mode {
1 -> { 1, 5 -> {
; 8bpp mode ; monochrome modes, either resolution
position(x, y)
%asm {{
lda color
phx
ldx length+1
beq +
ldy #0
- sta cx16.VERA_DATA0
iny
bne -
dex
bne -
+ ldy length ; remaining
beq +
- sta cx16.VERA_DATA0
dey
bne -
+ plx
}}
}
0, 128 -> {
; 1 bpp mode
ubyte separate_pixels = (8-lsb(x)) & 7 ubyte separate_pixels = (8-lsb(x)) & 7
if separate_pixels as uword > length if separate_pixels as uword > length
separate_pixels = lsb(length) separate_pixels = lsb(length)
@ -205,32 +215,93 @@ _done
x++ x++
} }
} }
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) ; vera auto-increment off again cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
}
4 -> {
; lores 256c
position(x, y)
%asm {{
lda color
phx
ldx length+1
beq +
ldy #0
- sta cx16.VERA_DATA0
iny
bne -
dex
bne -
+ ldy length ; remaining
beq +
- sta cx16.VERA_DATA0
dey
bne -
+ plx
}}
}
6 -> {
; highres 4c
; TODO also mostly usable for lores 4c?
color &= 3
ubyte[4] colorbits
ubyte ii
for ii in 3 downto 0 {
colorbits[ii] = color
color <<= 2
}
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
lda cx16.VERA_ADDR_H
and #%00000111 ; no auto advance
sta cx16.VERA_ADDR_H
stz cx16.VERA_CTRL ; setup vera addr 0
lda cx16.r1
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
phx
ldx x
}}
repeat length {
%asm {{
txa
and #3
tay
lda cx16.VERA_DATA0
and gfx2.plot.mask4c,y
ora colorbits,y
sta cx16.VERA_DATA0
cpy #%00000011 ; next vera byte?
bne +
inc cx16.VERA_ADDR_L
bne +
inc cx16.VERA_ADDR_M
+ bne +
inc cx16.VERA_ADDR_H
+ inx ; next pixel
}}
}
%asm {{
plx
}}
} }
} }
} }
sub vertical_line(uword x, uword y, uword height, ubyte color) { sub vertical_line(uword x, uword y, uword height, ubyte color) {
position(x,y) position(x,y)
if active_mode==1 { when active_mode {
; set vera auto-increment to 320 pixel increment (=next line) 1, 5 -> {
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) | (14<<4) ; monochrome, either resolution
%asm {{
ldy height
beq +
lda color
- sta cx16.VERA_DATA0
dey
bne -
+
}}
return
}
; 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.
cx16.VERA_ADDR_H = (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>=128 if active_mode>=5
cx16.r14 = 640/8 cx16.r14 = 640/8
else else
cx16.r14 = 320/8 cx16.r14 = 320/8
@ -268,11 +339,11 @@ _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
+ +
asl cx16.r14 asl cx16.r14
ldy height ldy height
beq + beq +
- lda cx16.VERA_DATA0 - lda cx16.VERA_DATA0
ora cx16.r15 ora cx16.r15
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
lda cx16.VERA_ADDR_L lda cx16.VERA_ADDR_L
@ -287,7 +358,7 @@ _done
; sta cx16.VERA_ADDR_H ; sta cx16.VERA_ADDR_H
dey dey
bne - bne -
+ +
}} }}
} }
} else { } else {
@ -311,10 +382,57 @@ _done
} }
} }
} }
4 -> {
; lores 256c
; set vera auto-increment to 320 pixel increment (=next line)
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
%asm {{
ldy height
beq +
lda color
- sta cx16.VERA_DATA0
dey
bne -
+
}}
}
6 -> {
; 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.
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
; 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)
; TODO optimize the loop in pure assembly
color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3]
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat height {
ubyte value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask | color
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
%asm {{
; 24 bits add 160 (640/4)
clc
lda cx16.r0
adc #640/4
sta cx16.r0
lda cx16.r0+1
adc #0
sta cx16.r0+1
bcc +
inc cx16.r1
+
}}
}
}
}
}
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)
@ -468,11 +586,14 @@ _done
sub plot(uword @zp x, uword y, ubyte color) { sub plot(uword @zp x, uword y, ubyte color) {
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1] ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] shift4c = [6,4,2,0]
uword addr uword addr
ubyte value ubyte value
when active_mode { when active_mode {
0 -> { 1 -> {
; lores monochrome
%asm {{ %asm {{
lda x lda x
eor y eor y
@ -490,7 +611,16 @@ _done
} }
} }
} }
128 -> { 4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.vpoke(lsb(cx16.r1), cx16.r0, color)
; activate vera auto-increment mode so next_pixel() can be used after this
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | %00010000
color = cx16.VERA_DATA0
}
5 -> {
; highres monochrome
%asm {{ %asm {{
lda x lda x
eor y eor y
@ -508,30 +638,44 @@ _done
} }
} }
} }
1 -> { 6 -> {
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L ; highres 4c
value = lsb(cx16.r1) ; TODO also mostly usable for lores 4c?
cx16.vpoke(value, cx16.r0, color) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
; activate vera auto-increment mode so next_pixel() can be used after this color &= 3
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) | %00010000 color <<= shift4c[lsb(x) & 3]
color = cx16.VERA_DATA0 ; TODO optimize the vera memory manipulation in pure assembly
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask4c[lsb(x) & 3] | color
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
} }
} }
} }
sub position(uword @zp x, uword y) { sub position(uword @zp x, uword y) {
ubyte bank
when active_mode { when active_mode {
0 -> { 1 -> {
; lores monochrome
cx16.r0 = y*(320/8) + x/8 cx16.r0 = y*(320/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1) cx16.vaddr(0, cx16.r0, 0, 1)
} }
128 -> { ; TODO modes 2,3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1)
}
5 -> {
; highres monochrome
cx16.r0 = y*(640/8) + x/8 cx16.r0 = y*(640/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1) cx16.vaddr(0, cx16.r0, 0, 1)
} }
1 -> { 6 -> {
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L ; highres 4c
ubyte bank = lsb(cx16.r1) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1) cx16.vaddr(bank, cx16.r0, 0, 1)
} }
} }
@ -541,6 +685,7 @@ _done
; -- sets the next pixel byte to the graphics chip. ; -- sets the next pixel byte to the graphics chip.
; for 8 bpp screens this will plot 1 pixel. ; for 8 bpp screens this will plot 1 pixel.
; for 1 bpp screens it will plot 8 pixels at once (color = bit pattern). ; for 1 bpp screens it will plot 8 pixels at once (color = bit pattern).
; for 2 bpp screens it will plot 4 pixels at once (color = bit pattern).
%asm {{ %asm {{
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
}} }}
@ -550,6 +695,7 @@ _done
; -- sets the next bunch of pixels from a prepared array of bytes. ; -- sets the next bunch of pixels from a prepared array of bytes.
; for 8 bpp screens this will plot 1 pixel per byte. ; for 8 bpp screens this will plot 1 pixel per byte.
; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte). ; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte).
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
%asm {{ %asm {{
phx phx
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -614,13 +760,13 @@ _done
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) { sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use. ; You must also have called text_charset() first to select and prepare the character set to use.
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 ! ; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 ! TODO allow per-pixel horizontal positioning
uword chardataptr uword chardataptr
when active_mode { when active_mode {
0, 128 -> { 1, 5 -> {
; 1-bitplane modes ; monochrome mode, either resolution
cx16.r2 = 40 cx16.r2 = 40
if active_mode>=128 if active_mode>=5
cx16.r2 = 80 cx16.r2 = 80
while @(sctextptr) { while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
@ -660,8 +806,8 @@ _done
sctextptr++ sctextptr++
} }
} }
1 -> { 4 -> {
; 320 x 240 x 256c ; lores 256c
while @(sctextptr) { while @(sctextptr) {
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)
@ -689,6 +835,28 @@ _done
sctextptr++ sctextptr++
} }
} }
6 -> {
; hires 4c
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
repeat 8 {
; TODO rewrite this inner loop in assembly
ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
repeat 8 {
charbits <<= 1
if_cs
plot(x, y, color)
x++
}
x-=8
chardataptr++
y++
}
x+=8
y-=8
sctextptr++
}
}
} }
} }
@ -709,7 +877,40 @@ _done
}} }}
} }
asmsub addr_mul_320_add_24(uword address @R0, uword value @AY) clobbers(A) -> uword @R0, ubyte @R1 { sub addr_mul_24_for_highres_4c(uword yy, uword xx) {
; TODO turn into asmsub
; 24 bits result is in r0 and r1L (highest byte)
cx16.r0 = yy*128
cx16.r2 = yy*32
xx >>= 2
%asm {{
; add r2 and xx to r0 (24-bits)
stz cx16.r1
clc
lda cx16.r0
adc cx16.r2
sta cx16.r0
lda cx16.r0+1
adc cx16.r2+1
sta cx16.r0+1
bcc +
inc cx16.r1
+ clc
lda cx16.r0
adc xx
sta cx16.r0
lda cx16.r0+1
adc xx+1
sta cx16.r0+1
bcc +
inc cx16.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)
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1

View File

@ -13,30 +13,40 @@ palette {
cx16.vpoke(1, $fa01+index*2, msb(color)) cx16.vpoke(1, $fa01+index*2, msb(color))
} }
sub set_rgb4(uword palletteptr, uword num_colors) { sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped ; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat num_colors { repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr)) cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
palletteptr++ palette_bytes_ptr++
cx16.vpoke(1, vera_palette_ptr, @(palletteptr)) cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
palletteptr++ palette_bytes_ptr++
vera_palette_ptr+=2 vera_palette_ptr+=2
} }
} }
sub set_rgb8(uword palletteptr, uword num_colors) { sub set_rgb(uword palette_words_ptr, uword num_colors) {
; 1 word per color entry (in little endian format so $gb0r)
vera_palette_ptr = $fa00
repeat num_colors*2 {
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
palette_words_ptr++
vera_palette_ptr++
}
}
sub set_rgb8(uword palette_bytes_ptr, uword num_colors) {
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel. ; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
ubyte red ubyte red
ubyte greenblue ubyte greenblue
repeat num_colors { repeat num_colors {
red = @(palletteptr) >> 4 red = @(palette_bytes_ptr) >> 4
palletteptr++ palette_bytes_ptr++
greenblue = @(palletteptr) & %11110000 greenblue = @(palette_bytes_ptr) & %11110000
palletteptr++ palette_bytes_ptr++
greenblue |= @(palletteptr) >> 4 ; add Blue greenblue |= @(palette_bytes_ptr) >> 4 ; add Blue
palletteptr++ palette_bytes_ptr++
cx16.vpoke(1, vera_palette_ptr, greenblue) cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red) cx16.vpoke(1, vera_palette_ptr, red)
@ -44,16 +54,16 @@ palette {
} }
} }
sub set_monochrome() { sub set_monochrome(uword screencolorRGB, uword drawcolorRGB) {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
cx16.vpoke(1, vera_palette_ptr, 0) cx16.vpoke(1, vera_palette_ptr, lsb(screencolorRGB)) ; G,B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 0) cx16.vpoke(1, vera_palette_ptr, msb(screencolorRGB)) ; R
vera_palette_ptr++ vera_palette_ptr++
repeat 255 { repeat 255 {
cx16.vpoke(1, vera_palette_ptr, 255) cx16.vpoke(1, vera_palette_ptr, lsb(drawcolorRGB)) ; G,B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 255) cx16.vpoke(1, vera_palette_ptr, msb(drawcolorRGB)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }

View File

@ -31,6 +31,16 @@ sub spc() {
txt.chrout(' ') txt.chrout(' ')
} }
asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{
sec
jsr c64.PLOT
tay
clc
jmp c64.PLOT
}}
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) { asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
; ---- fill the character screen with the given fill character and character color. ; ---- fill the character screen with the given fill character and character color.

View File

@ -1262,7 +1262,7 @@ mul_word_100 .proc
.pend .pend
mul_word_320 .proc mul_word_320 .proc
; AY = A * 256 + A * 64 (msb doesn't matter) ; AY = A * 256 + A * 64 (msb in Y doesn't matter)
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
ldy #0 ldy #0
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG

View File

@ -1078,3 +1078,28 @@ _loop_hi ldy _index_first
.pend .pend
func_peekw .proc
; -- read the word value on the address in AY
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
pha
iny
lda (P8ZP_SCRATCH_W1),y
tay
pla
rts
.pend
func_pokew .proc
; -- store the word value in AY in the address in P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_REG
ldy #0
sta (P8ZP_SCRATCH_W1),y
iny
lda P8ZP_SCRATCH_REG
sta (P8ZP_SCRATCH_W1),y
rts
.pend

View File

@ -1071,3 +1071,4 @@ sign_extend_AY_byte .proc
pla pla
rts rts
.pend .pend

View File

@ -1 +1 @@
6.0 6.1

View File

@ -1,12 +1,15 @@
package prog8 package prog8
import kotlinx.cli.* import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
import kotlinx.cli.default
import kotlinx.cli.multiple
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
import prog8.compiler.target.CompilationTarget
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path import java.nio.file.Path
@ -32,14 +35,14 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
private fun compileMain(args: Array<String>) { private fun compileMain(args: Array<String>) {
val cli = ArgParser("prog8compiler") val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
val startEmulator by cli.option(ArgType.Boolean, shortName="emu", description = "auto-start emulator after successful compilation") val startEmulator by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
val outputDir by cli.option(ArgType.String, shortName = "out", description = "directory for output files instead of current directory").default(".") val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
val dontWriteAssembly by cli.option(ArgType.Boolean, shortName = "noasm", description="don't create assembly code") val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val dontOptimize by cli.option(ArgType.Boolean, shortName = "noopt", description = "don't perform any optimizations") val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
val watchMode by cli.option(ArgType.Boolean, shortName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed") val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
val slowCodegenWarnings by cli.option(ArgType.Boolean, shortName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation") val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val compilationTarget by cli.option(ArgType.String, shortName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name) val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try { try {
@ -114,7 +117,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 {
CompilationTarget.instance.machine.launchEmulator(compilationResult.programName) ICompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
} }
} }
} }

View File

@ -1,22 +0,0 @@
package prog8.ast.processing
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.statements.Directive
internal class ImportedModuleDirectiveRemover: AstWalker() {
/**
* Most global directives don't apply for imported modules, so remove them
*/
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
private val noModifications = emptyList<IAstModification>()
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
if(directive.directive in moduleLevelDirectives) {
return listOf(IAstModification.Remove(directive, parent as INameScope))
}
return noModifications
}
}

View File

@ -1,44 +0,0 @@
package prog8.ast.processing
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.NopStatement
internal class VariousCleanups: AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
}
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
return if(parent is INameScope)
listOf(ScopeFlatten(scope, parent as INameScope))
else
noModifications
}
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
override fun perform() {
val idx = into.statements.indexOf(scope)
if(idx>=0) {
into.statements.addAll(idx+1, scope.statements)
into.statements.remove(scope)
}
}
}
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
if(typecast.expression is NumericLiteralValue) {
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
if(value.isValid)
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
}
return noModifications
}
}

View File

@ -5,12 +5,13 @@ import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.target.ICompilationTarget
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() { internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
@ -37,7 +38,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF. // But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(!assignment.isAugmentable if(!assignment.isAugmentable
&& assignment.target.identifier != null && assignment.target.identifier != null
&& assignment.target.isInRegularRAM(program.namespace)) { && compTarget.isInRegularRAM(assignment.target, program)) {
val binExpr = assignment.value as? BinaryExpression val binExpr = assignment.value as? BinaryExpression
if (binExpr != null && binExpr.operator !in comparisonOperators) { if (binExpr != null && binExpr.operator !in comparisonOperators) {
if (binExpr.left !is BinaryExpression) { if (binExpr.left !is BinaryExpression) {
@ -156,7 +157,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
if(typecast.type in WordDatatypes) { if(typecast.type in WordDatatypes) {
val fcall = typecast.parent as? IFunctionCall val fcall = typecast.parent as? IFunctionCall
if (fcall != null) { if (fcall != null) {
val sub = fcall.target.targetStatement(program.namespace) as? Subroutine val sub = fcall.target.targetStatement(program) as? Subroutine
if (sub != null && sub.isAsmSubroutine) { if (sub != null && sub.isAsmSubroutine) {
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent)) return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
} }

View File

@ -1,9 +1,29 @@
package prog8.compiler package prog8.compiler
import prog8.ast.AstToSourceCode
import prog8.ast.IBuiltinFunctions
import prog8.ast.Program
import prog8.ast.base.AstException
import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.Directive
import prog8.compiler.astprocessing.*
import prog8.compiler.functions.*
import prog8.compiler.target.C64Target
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.asmGeneratorFor
import prog8.optimizer.*
import prog8.parser.ModuleImporter
import prog8.parser.ParsingFailedError
import prog8.parser.moduleName
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.nio.file.Path import java.nio.file.Path
import kotlin.math.abs import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
enum class OutputType { enum class OutputType {
RAW, RAW,
@ -36,20 +56,263 @@ data class CompilationOptions(val output: OutputType,
class CompilerException(message: String?) : Exception(message) class CompilerException(message: String?) : Exception(message)
fun Number.toHex(): String { class CompilationResult(val success: Boolean,
// 0..15 -> "0".."15" val programAst: Program,
// 16..255 -> "$10".."$ff" val programName: String,
// 256..65536 -> "$0100".."$ffff" val importedFiles: List<Path>)
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0) fun compileProgram(filepath: Path,
return '-' + abs(integer).toHex() optimize: Boolean,
return when (integer) { writeAssembly: Boolean,
in 0 until 16 -> integer.toString() slowCodegenWarnings: Boolean,
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') compilationTarget: String,
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') outputDir: Path): CompilationResult {
else -> throw CompilerException("number too large for 16 bits $this") var programName = ""
lateinit var programAst: Program
lateinit var importedFiles: List<Path>
val errors = ErrorReporter()
when(compilationTarget) {
C64Target.name -> ICompilationTarget.instance = C64Target
Cx16Target.name -> ICompilationTarget.instance = Cx16Target
else -> {
System.err.println("invalid compilation target")
exitProcess(1)
} }
}
try {
val totalTime = measureTimeMillis {
// import main module and everything it needs
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
compilationOptions.optimize = optimize
programAst = ast
importedFiles = imported
processAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
if (compilationOptions.optimize)
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions))
postprocessAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
// printAst(programAst)
if(writeAssembly)
programName = writeAssembly(programAst, errors, outputDir, compilationOptions)
}
System.out.flush()
System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
return CompilationResult(true, programAst, programName, importedFiles)
} catch (px: ParsingFailedError) {
System.err.print("\u001b[91m") // bright red
System.err.println(px.message)
System.err.print("\u001b[0m") // reset
} catch (ax: AstException) {
System.err.print("\u001b[91m") // bright red
System.err.println(ax.toString())
System.err.print("\u001b[0m") // reset
} catch (x: Exception) {
print("\u001b[91m") // bright red
println("\n* internal error *")
print("\u001b[0m") // reset
System.out.flush()
throw x
} catch (x: NotImplementedError) {
print("\u001b[91m") // bright red
println("\n* internal error: missing feature/code *")
print("\u001b[0m") // reset
System.out.flush()
throw x
}
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions))
return CompilationResult(false, failedProgram, programName, emptyList())
}
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
lateinit var program: Program
override val names = functions.keys
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? {
val func = BuiltinFunctions[name]
if(func!=null) {
val exprfunc = func.constExpressionFunc
if(exprfunc!=null) {
return try {
exprfunc(args, position, program)
} catch(x: NotConstArgumentException) {
// const-evaluating the builtin function call failed.
null
} catch(x: CannotEvaluateException) {
// const-evaluating the builtin function call failed.
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
}
override fun returnType(name: String, args: MutableList<Expression>) =
builtinFunctionReturnType(name, args, program)
}
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
val compilationTargetName = ICompilationTarget.instance.name
println("Compiler target: $compilationTargetName. Parsing...")
val importer = ModuleImporter()
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf)
bf.program = programAst
importer.importModule(programAst, filepath, ICompilationTarget.instance, compilationTargetName)
errors.handle()
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
val compilerOptions = determineCompilationOptions(programAst)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.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
ICompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst, ICompilationTarget.instance, compilationTargetName)
// always import prog8_lib and math
importer.importLibraryModule(programAst, "math", ICompilationTarget.instance, compilationTargetName)
importer.importLibraryModule(programAst, "prog8_lib", ICompilationTarget.instance, compilationTargetName)
errors.handle()
return Triple(programAst, compilerOptions, importedFiles)
}
private fun determineCompilationOptions(program: Program): CompilationOptions {
val mainModule = program.modules.first()
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
var zpType: ZeropageType =
if (zpoption == null)
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
else
try {
ZeropageType.valueOf(zpoption)
} catch (x: IllegalArgumentException) {
ZeropageType.KERNALSAFE
// error will be printed by the astchecker
}
if (zpType==ZeropageType.FLOATSAFE && ICompilationTarget.instance.name == Cx16Target.name) {
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
zpType = ZeropageType.BASICSAFE
}
val zpReserved = mainModule.statements
.asSequence()
.filter { it is Directive && it.directive == "%zpreserved" }
.map { (it as Directive).args }
.map { it[0].int!!..it[1].int!! }
.toList()
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
System.err.println("invalid output type $outputType")
exitProcess(1)
}
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
System.err.println("invalid launcher type $launcherType")
exitProcess(1)
}
return CompilationOptions(
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
zpType, zpReserved, floatsEnabled, noSysInit
)
}
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
// perform initial syntax checks and processings
println("Processing for target ${compTarget.name}...")
programAst.checkIdentifiers(errors, compTarget)
errors.handle()
programAst.constantFold(errors)
errors.handle()
programAst.reorderStatements(errors)
errors.handle()
programAst.addTypecasts(errors)
errors.handle()
programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors, compTarget)
errors.handle()
programAst.checkIdentifiers(errors, compTarget)
errors.handle()
}
private fun optimizeAst(programAst: Program, errors: ErrorReporter, functions: IBuiltinFunctions) {
// optimize the parse tree
println("Optimizing...")
while (true) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = programAst.simplifyExpressions()
val optsDone2 = programAst.splitBinaryExpressions()
val optsDone3 = programAst.optimizeStatements(errors, functions)
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
errors.handle()
if (optsDone1 + optsDone2 + optsDone3 == 0)
break
}
val remover = UnusedCodeRemover(programAst, errors)
remover.visit(programAst)
remover.applyModifications()
errors.handle()
}
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
programAst.addTypecasts(errors)
errors.handle()
programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors, compTarget) // check if final tree is still valid
errors.handle()
val callGraph = CallGraph(programAst)
callGraph.checkRecursiveCalls(errors)
errors.handle()
programAst.verifyFunctionArgTypes()
programAst.moveMainAndStartToFirst()
}
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
compilerOptions: CompilationOptions): String {
// asm generation directly from the Ast,
programAst.processAstBeforeAsmGeneration(errors, ICompilationTarget.instance)
errors.handle()
// printAst(programAst)
ICompilationTarget.instance.machine.initializeZeropage(compilerOptions)
val assembly = asmGeneratorFor(ICompilationTarget.instance,
programAst,
errors,
ICompilationTarget.instance.machine.zeropage,
compilerOptions,
outputDir).compileToAssembly()
assembly.assemble(compilerOptions)
errors.handle()
return assembly.name
}
fun printAst(programAst: Program) {
println()
val printer = AstToSourceCode(::print, programAst)
printer.visit(programAst)
println()
} }
fun loadAsmIncludeFile(filename: String, source: Path): String { fun loadAsmIncludeFile(filename: String, source: Path): String {

View File

@ -1,8 +1,8 @@
package prog8.ast.base package prog8.compiler
import prog8.ast.base.Position
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
class ErrorReporter { class ErrorReporter {
private enum class MessageSeverity { private enum class MessageSeverity {
WARNING, WARNING,

View File

@ -1,247 +0,0 @@
package prog8.compiler
import prog8.ast.AstToSourceCode
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.statements.Directive
import prog8.compiler.target.C64Target
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target
import prog8.optimizer.*
import prog8.optimizer.UnusedCodeRemover
import prog8.optimizer.constantFold
import prog8.optimizer.optimizeStatements
import prog8.optimizer.simplifyExpressions
import prog8.parser.ModuleImporter
import prog8.parser.ParsingFailedError
import prog8.parser.moduleName
import java.nio.file.Path
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
class CompilationResult(val success: Boolean,
val programAst: Program,
val programName: String,
val importedFiles: List<Path>)
fun compileProgram(filepath: Path,
optimize: Boolean,
writeAssembly: Boolean,
slowCodegenWarnings: Boolean,
compilationTarget: String,
outputDir: Path): CompilationResult {
var programName = ""
lateinit var programAst: Program
lateinit var importedFiles: List<Path>
val errors = ErrorReporter()
when(compilationTarget) {
C64Target.name -> CompilationTarget.instance = C64Target
Cx16Target.name -> CompilationTarget.instance = Cx16Target
else -> {
System.err.println("invalid compilation target")
exitProcess(1)
}
}
try {
val totalTime = measureTimeMillis {
// import main module and everything it needs
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
compilationOptions.optimize = optimize
programAst = ast
importedFiles = imported
processAst(programAst, errors, compilationOptions)
if (compilationOptions.optimize)
optimizeAst(programAst, errors)
postprocessAst(programAst, errors, compilationOptions)
// printAst(programAst)
if(writeAssembly)
programName = writeAssembly(programAst, errors, outputDir, compilationOptions)
}
System.out.flush()
System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
return CompilationResult(true, programAst, programName, importedFiles)
} catch (px: ParsingFailedError) {
System.err.print("\u001b[91m") // bright red
System.err.println(px.message)
System.err.print("\u001b[0m") // reset
} catch (ax: AstException) {
System.err.print("\u001b[91m") // bright red
System.err.println(ax.toString())
System.err.print("\u001b[0m") // reset
} catch (x: Exception) {
print("\u001b[91m") // bright red
println("\n* internal error *")
print("\u001b[0m") // reset
System.out.flush()
throw x
} catch (x: NotImplementedError) {
print("\u001b[91m") // bright red
println("\n* internal error: missing feature/code *")
print("\u001b[0m") // reset
System.out.flush()
throw x
}
return CompilationResult(false, Program("failed", mutableListOf()), programName, emptyList())
}
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
println("Compiler target: ${CompilationTarget.instance.name}. Parsing...")
val importer = ModuleImporter()
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
importer.importModule(programAst, filepath)
errors.handle()
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
val compilerOptions = determineCompilationOptions(programAst)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.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
CompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst)
// always import prog8_lib and math
importer.importLibraryModule(programAst, "math")
importer.importLibraryModule(programAst, "prog8_lib")
errors.handle()
return Triple(programAst, compilerOptions, importedFiles)
}
private fun determineCompilationOptions(program: Program): CompilationOptions {
val mainModule = program.modules.first()
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.toUpperCase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
val noSysInit = allOptions.any { it.name == "no_sysinit" }
var zpType: ZeropageType =
if (zpoption == null)
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
else
try {
ZeropageType.valueOf(zpoption)
} catch (x: IllegalArgumentException) {
ZeropageType.KERNALSAFE
// error will be printed by the astchecker
}
if (zpType==ZeropageType.FLOATSAFE && CompilationTarget.instance.name == Cx16Target.name) {
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
zpType = ZeropageType.BASICSAFE
}
val zpReserved = mainModule.statements
.asSequence()
.filter { it is Directive && it.directive == "%zpreserved" }
.map { (it as Directive).args }
.map { it[0].int!!..it[1].int!! }
.toList()
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
System.err.println("invalid output type $outputType")
exitProcess(1)
}
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
System.err.println("invalid launcher type $launcherType")
exitProcess(1)
}
return CompilationOptions(
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
zpType, zpReserved, floatsEnabled, noSysInit
)
}
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
// perform initial syntax checks and processings
println("Processing for target ${CompilationTarget.instance.name}...")
programAst.checkIdentifiers(errors)
errors.handle()
programAst.constantFold(errors)
errors.handle()
programAst.reorderStatements(errors)
errors.handle()
programAst.addTypecasts(errors)
errors.handle()
programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors)
errors.handle()
programAst.checkIdentifiers(errors)
errors.handle()
}
private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
// optimize the parse tree
println("Optimizing...")
while (true) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = programAst.simplifyExpressions()
val optsDone2 = programAst.splitBinaryExpressions()
val optsDone3 = programAst.optimizeStatements(errors)
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
errors.handle()
if (optsDone1 + optsDone2 + optsDone3 == 0)
break
}
val remover = UnusedCodeRemover(programAst, errors)
remover.visit(programAst)
remover.applyModifications()
errors.handle()
}
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
programAst.addTypecasts(errors)
errors.handle()
programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
errors.handle()
val callGraph = CallGraph(programAst)
callGraph.checkRecursiveCalls(errors)
errors.handle()
programAst.verifyFunctionArgTypes()
programAst.moveMainAndStartToFirst()
}
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
compilerOptions: CompilationOptions): String {
// asm generation directly from the Ast,
programAst.processAstBeforeAsmGeneration(errors)
errors.handle()
// printAst(programAst)
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
val assembly = CompilationTarget.instance.asmGenerator(
programAst,
errors,
CompilationTarget.instance.machine.zeropage,
compilerOptions,
outputDir).compileToAssembly()
assembly.assemble(compilerOptions)
errors.handle()
return assembly.name
}
fun printAst(programAst: Program) {
println()
val printer = AstToSourceCode(::print, programAst)
printer.visit(programAst)
println()
}

View File

@ -1,4 +1,4 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.INameScope import prog8.ast.INameScope
import prog8.ast.Module import prog8.ast.Module
@ -6,17 +6,21 @@ 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.IAstVisitor
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.ErrorReporter
import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.builtinFunctionReturnType
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
import prog8.functions.BuiltinFunctions import prog8.compiler.target.ICompilationTarget
import prog8.functions.builtinFunctionReturnType
import java.io.File 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) : IAstVisitor { private val errors: ErrorReporter,
private val compTarget: ICompilationTarget
) : IAstVisitor {
override fun visit(program: Program) { override fun visit(program: Program) {
assert(program === this.program) assert(program === this.program)
@ -99,7 +103,7 @@ internal class AstChecker(private val program: Program,
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) { if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
errors.err("can only loop over an iterable type", forLoop.position) errors.err("can only loop over an iterable type", forLoop.position)
} else { } else {
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace) val loopvar = forLoop.loopVar.targetVarDecl(program)
if(loopvar==null || loopvar.type== VarDeclType.CONST) { if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position) errors.err("for loop requires a variable to loop with", forLoop.position)
} else { } else {
@ -150,21 +154,24 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(jump: Jump) { override fun visit(jump: Jump) {
if(jump.identifier!=null) { val ident = jump.identifier
val targetStatement = checkFunctionOrLabelExists(jump.identifier, jump) if(ident!=null) {
val targetStatement = checkFunctionOrLabelExists(ident, jump)
if(targetStatement!=null) { if(targetStatement!=null) {
if(targetStatement is BuiltinFunctionStatementPlaceholder) if(targetStatement is BuiltinFunctionStatementPlaceholder)
errors.err("can't jump to a builtin function", jump.position) errors.err("can't jump to a builtin function", jump.position)
} }
} }
if(jump.address!=null && (jump.address < 0 || jump.address > 65535)) val addr = jump.address
if(addr!=null && (addr < 0 || addr > 65535))
errors.err("jump address must be valid integer 0..\$ffff", jump.position) errors.err("jump address must be valid integer 0..\$ffff", jump.position)
super.visit(jump) super.visit(jump)
} }
override fun visit(block: Block) { override fun visit(block: Block) {
if(block.address!=null && (block.address<0 || block.address>65535)) { val addr = block.address
if(addr!=null && (addr<0 || addr>65535)) {
errors.err("block memory address must be valid integer 0..\$ffff", block.position) errors.err("block memory address must be valid integer 0..\$ffff", block.position)
} }
@ -315,9 +322,11 @@ internal class AstChecker(private val program: Program,
RegisterOrPair.R13, RegisterOrPair.R13,
RegisterOrPair.R14, RegisterOrPair.R14,
RegisterOrPair.R15 -> { /* no sensible way to count this */ } RegisterOrPair.R15 -> { /* no sensible way to count this */ }
null -> null -> {
if(p.statusflag!=null) val statusf = p.statusflag
statusflagCounts[p.statusflag] = statusflagCounts.getValue(p.statusflag) + 1 if (statusf != null)
statusflagCounts[statusf] = statusflagCounts.getValue(statusf) + 1
}
} }
} }
} }
@ -376,7 +385,7 @@ internal class AstChecker(private val program: Program,
override fun visit(assignment: Assignment) { override fun visit(assignment: Assignment) {
if(assignment.value is FunctionCall) { if(assignment.value is FunctionCall) {
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace) val stmt = (assignment.value as FunctionCall).target.targetStatement(program)
if (stmt is Subroutine) { if (stmt is Subroutine) {
val idt = assignment.target.inferType(program) val idt = assignment.target.inferType(program)
if(!idt.isKnown) { if(!idt.isKnown) {
@ -390,7 +399,7 @@ internal class AstChecker(private val program: Program,
val targetIdent = assignment.target.identifier val targetIdent = assignment.target.identifier
if(targetIdent!=null) { if(targetIdent!=null) {
val targetVar = targetIdent.targetVarDecl(program.namespace) val targetVar = targetIdent.targetVarDecl(program)
if(targetVar?.struct != null) { if(targetVar?.struct != null) {
val sourceStructLv = assignment.value as? ArrayLiteralValue val sourceStructLv = assignment.value as? ArrayLiteralValue
if (sourceStructLv != null) { if (sourceStructLv != null) {
@ -399,7 +408,7 @@ internal class AstChecker(private val program: Program,
} else { } else {
val sourceIdent = assignment.value as? IdentifierReference val sourceIdent = assignment.value as? IdentifierReference
if (sourceIdent != null) { if (sourceIdent != null) {
val sourceVar = sourceIdent.targetVarDecl(program.namespace) val sourceVar = sourceIdent.targetVarDecl(program)
if (sourceVar?.struct != null) { if (sourceVar?.struct != null) {
if (sourceVar.struct !== targetVar.struct) if (sourceVar.struct !== targetVar.struct)
errors.err("assignment of different struct types", assignment.position) errors.err("assignment of different struct types", assignment.position)
@ -487,7 +496,7 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(addressOf: AddressOf) { override fun visit(addressOf: AddressOf) {
val variable=addressOf.identifier.targetVarDecl(program.namespace) val variable=addressOf.identifier.targetVarDecl(program)
if(variable!=null if(variable!=null
&& variable.datatype !in ArrayDatatypes && variable.datatype !in ArrayDatatypes
&& variable.type!=VarDeclType.MEMORY && variable.type!=VarDeclType.MEMORY
@ -782,7 +791,7 @@ internal class AstChecker(private val program: Program,
fun isPassByReferenceElement(e: Expression): Boolean { fun isPassByReferenceElement(e: Expression): Boolean {
if(e is IdentifierReference) { if(e is IdentifierReference) {
val decl = e.targetVarDecl(program.namespace)!! val decl = e.targetVarDecl(program)!!
return decl.datatype in PassByReferenceDatatypes return decl.datatype in PassByReferenceDatatypes
} }
return e is StringLiteralValue return e is StringLiteralValue
@ -790,7 +799,7 @@ internal class AstChecker(private val program: Program,
if(array.parent is VarDecl) { if(array.parent is VarDecl) {
if (!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) }) if (!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
errors.err("array literal for variable initialization contains invalid types", array.position) errors.err("array literal for variable initialization contains non-constant elements", array.position)
} else if(array.parent is ForLoop) { } else if(array.parent is ForLoop) {
if (!array.value.all { it.constValue(program) != null }) if (!array.value.all { it.constValue(program) != null })
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position) errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
@ -935,12 +944,12 @@ internal class AstChecker(private val program: Program,
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position) errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
} }
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program) val error = VerifyFunctionArgTypes.checkTypes(functionCall, program)
if(error!=null) if(error!=null)
errors.err(error, functionCall.position) errors.err(error, functionCall.position)
// check the functions that return multiple returnvalues. // check the functions that return multiple returnvalues.
val stmt = functionCall.target.targetStatement(program.namespace) val stmt = functionCall.target.targetStatement(program)
if (stmt is Subroutine) { if (stmt is Subroutine) {
if (stmt.returntypes.size > 1) { if (stmt.returntypes.size > 1) {
// Currently, it's only possible to handle ONE (or zero) return values from a subroutine. // Currently, it's only possible to handle ONE (or zero) return values from a subroutine.
@ -998,7 +1007,8 @@ internal class AstChecker(private val program: Program,
} }
} }
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, functionCallStatement.definingScope(), program) val error =
VerifyFunctionArgTypes.checkTypes(functionCallStatement, program)
if(error!=null) { if(error!=null) {
errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position) errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position)
} }
@ -1025,7 +1035,7 @@ internal class AstChecker(private val program: Program,
errors.err("swap requires args of numerical type", position) errors.err("swap requires args of numerical type", position)
} }
else if(target.name=="all" || target.name=="any") { else if(target.name=="all" || target.name=="any") {
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) { if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR) {
errors.err("any/all on a string is useless (is always true unless the string is empty)", position) errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
} }
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) { if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
@ -1039,6 +1049,28 @@ internal class AstChecker(private val program: Program,
if (!argIDt.isKnown) if (!argIDt.isKnown)
return return
} }
// check that cx16 virtual registers aren't used as arguments in a conflicting way
val params = target.asmParameterRegisters.withIndex().toList()
for(arg in args.withIndex()) {
var ident: IdentifierReference? = null
if(arg.value is IdentifierReference)
ident = arg.value as IdentifierReference
else if(arg.value is FunctionCall) {
val fcall = arg.value as FunctionCall
if(fcall.target.nameInSource == listOf("lsb") || fcall.target.nameInSource == listOf("msb"))
ident = fcall.args[0] as? IdentifierReference
}
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].toUpperCase())
val same = params.filter { it.value.registerOrPair==reg }
for(s in same) {
if(s.index!=arg.index) {
errors.err("conflicting register $reg used as argument but is also a target register for another parameter", ident.position)
}
}
}
}
} }
} }
} }
@ -1058,7 +1090,7 @@ internal class AstChecker(private val program: Program,
} }
} }
} else if(postIncrDecr.target.arrayindexed != null) { } else if(postIncrDecr.target.arrayindexed != null) {
val target = postIncrDecr.target.arrayindexed?.arrayvar?.targetStatement(program.namespace) val target = postIncrDecr.target.arrayindexed?.arrayvar?.targetStatement(program)
if(target==null) { if(target==null) {
errors.err("undefined symbol", postIncrDecr.position) errors.err("undefined symbol", postIncrDecr.position)
} }
@ -1073,7 +1105,7 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
val target = arrayIndexedExpression.arrayvar.targetStatement(program.namespace) val target = arrayIndexedExpression.arrayvar.targetStatement(program)
if(target is VarDecl) { if(target is VarDecl) {
if(target.datatype !in IterableDatatypes) if(target.datatype !in IterableDatatypes)
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position) errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
@ -1165,7 +1197,7 @@ internal class AstChecker(private val program: Program,
} }
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? { private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
val targetStatement = target.targetStatement(program.namespace) val targetStatement = target.targetStatement(program)
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder) if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
return targetStatement return targetStatement
else if(targetStatement==null) else if(targetStatement==null)
@ -1255,7 +1287,7 @@ internal class AstChecker(private val program: Program,
// check if the floating point values are all within range // check if the floating point values are all within range
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray() val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
if(doubles.any { it < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE }) if(doubles.any { it < compTarget.machine.FLOAT_MAX_NEGATIVE || it > compTarget.machine.FLOAT_MAX_POSITIVE })
return err("floating point value overflow") return err("floating point value overflow")
return true return true
} }
@ -1383,7 +1415,7 @@ internal class AstChecker(private val program: Program,
if(sourceDatatype==DataType.STRUCT) { if(sourceDatatype==DataType.STRUCT) {
val structLv = sourceValue as ArrayLiteralValue val structLv = sourceValue as ArrayLiteralValue
val numValues = structLv.value.size val numValues = structLv.value.size
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!! val targetstruct = target.identifier!!.targetVarDecl(program)!!.struct!!
return targetstruct.numberOfElements == numValues return targetstruct.numberOfElements == numValues
} }
false false

View File

@ -1,20 +1,21 @@
package prog8.ast.base package prog8.compiler.astprocessing
import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.processing.* import prog8.compiler.ErrorReporter
import prog8.ast.base.FatalAstException
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.compiler.CompilationOptions
import prog8.compiler.BeforeAsmGenerationAstChanger import prog8.compiler.BeforeAsmGenerationAstChanger
import prog8.compiler.CompilationOptions
import prog8.compiler.target.ICompilationTarget
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) { internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter, compTarget: ICompilationTarget) {
val checker = AstChecker(this, compilerOptions, errors) val checker = AstChecker(this, compilerOptions, errors, compTarget)
checker.visit(this) checker.visit(this)
} }
internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter) { internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter, compTarget: ICompilationTarget) {
val fixer = BeforeAsmGenerationAstChanger(this, errors) val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
fixer.visit(this) fixer.visit(this)
fixer.applyModifications() fixer.applyModifications()
} }
@ -36,15 +37,9 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this) fixer.visit(this)
} }
internal fun Module.checkImportedValid() { internal fun Program.checkIdentifiers(errors: ErrorReporter, compTarget: ICompilationTarget) {
val imr = ImportedModuleDirectiveRemover()
imr.visit(this, this.parent)
imr.applyModifications()
}
internal fun Program.checkIdentifiers(errors: ErrorReporter) { val checker2 = AstIdentifiersChecker(this, errors, compTarget)
val checker2 = AstIdentifiersChecker(this, errors)
checker2.visit(this) checker2.visit(this)
if(errors.isEmpty()) { if(errors.isEmpty()) {

View File

@ -1,14 +1,20 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.DataType
import prog8.ast.expressions.* import prog8.compiler.ErrorReporter
import prog8.ast.base.NumericDatatypes
import prog8.ast.base.Position
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.target.CompilationTarget import prog8.ast.walk.IAstVisitor
import prog8.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.target.ICompilationTarget
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter) : IAstVisitor { internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter, 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) {
@ -22,7 +28,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
} }
override fun visit(block: Block) { override fun visit(block: Block) {
if(block.name in CompilationTarget.instance.machine.opcodeNames) if(block.name in compTarget.machine.opcodeNames)
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position) errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
val existing = blocks[block.name] val existing = blocks[block.name]
@ -44,8 +50,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
override fun visit(directive: Directive) { override fun visit(directive: Directive) {
if(directive.directive=="%target") { if(directive.directive=="%target") {
val compatibleTarget = directive.args.single().name val compatibleTarget = directive.args.single().name
if (compatibleTarget != CompilationTarget.instance.name) if (compatibleTarget != compTarget.name)
errors.err("module's compilation target ($compatibleTarget) differs from active target (${CompilationTarget.instance.name})", directive.position) errors.err("module's compilation target ($compatibleTarget) differs from active target (${compTarget.name})", directive.position)
} }
super.visit(directive) super.visit(directive)
@ -57,7 +63,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
if(decl.name in BuiltinFunctions) if(decl.name in BuiltinFunctions)
errors.err("builtin function cannot be redefined", decl.position) errors.err("builtin function cannot be redefined", decl.position)
if(decl.name in CompilationTarget.instance.machine.opcodeNames) if(decl.name in compTarget.machine.opcodeNames)
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position) errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
if(decl.datatype==DataType.STRUCT) { if(decl.datatype==DataType.STRUCT) {
@ -96,7 +102,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
} }
override fun visit(subroutine: Subroutine) { override fun visit(subroutine: Subroutine) {
if(subroutine.name in CompilationTarget.instance.machine.opcodeNames) { if(subroutine.name in compTarget.machine.opcodeNames) {
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position) errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
} else if(subroutine.name in BuiltinFunctions) { } else if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined // the builtin functions can't be redefined
@ -136,7 +142,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
} }
override fun visit(label: Label) { override fun visit(label: Label) {
if(label.name in CompilationTarget.instance.machine.opcodeNames) if(label.name in compTarget.machine.opcodeNames)
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position) errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
if(label.name in BuiltinFunctions) { if(label.name in BuiltinFunctions) {

View File

@ -1,10 +1,16 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.DataType
import prog8.ast.expressions.* import prog8.ast.expressions.BinaryExpression
import prog8.ast.statements.* import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.ParameterVarDecl
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
internal class AstVariousTransforms(private val program: Program) : AstWalker() { internal class AstVariousTransforms(private val program: Program) : AstWalker() {

View File

@ -1,10 +1,15 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.DataType
import prog8.ast.expressions.* import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.statements.* import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() { internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {

View File

@ -1,4 +1,4 @@
package prog8.ast.processing package prog8.compiler.astprocessing
/* /*

View File

@ -1,10 +1,13 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter
import prog8.compiler.functions.BuiltinFunctions
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() { internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
@ -82,7 +85,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> { override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program.namespace) val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
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
@ -140,7 +143,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
} }
is IFunctionCall -> { is IFunctionCall -> {
val argnum = parent.args.indexOf(expr) val argnum = parent.args.indexOf(expr)
when (val callee = parent.target.targetStatement(program.namespace)) { when (val callee = parent.target.targetStatement(program)) {
is Subroutine -> { is Subroutine -> {
val paramType = callee.parameters[argnum].type val paramType = callee.parameters[argnum].type
if(leftDt isAssignableTo paramType) { if(leftDt isAssignableTo paramType) {
@ -313,16 +316,16 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> { private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
val identifier = assign.target.identifier!! val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program.namespace)!! val targetVar = identifier.targetVarDecl(program)!!
val alv = assign.value as? ArrayLiteralValue val alv = assign.value as? ArrayLiteralValue
return flattenArrayAssign(targetVar, alv, identifier, assign.position) return flattenArrayAssign(targetVar, alv, identifier, assign.position)
} }
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> { private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
val identifier = assign.target.identifier!! val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program.namespace)!! val targetVar = identifier.targetVarDecl(program)!!
val sourceIdent = assign.value as IdentifierReference val sourceIdent = assign.value as IdentifierReference
val sourceVar = sourceIdent.targetVarDecl(program.namespace)!! 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() return emptyList()
@ -352,7 +355,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> { private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> {
val identifier = structAssignment.target.identifier!! val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single() val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program.namespace)!! val targetVar = identifier.targetVarDecl(program)!!
val struct = targetVar.struct!! val struct = targetVar.struct!!
val slv = structAssignment.value as? ArrayLiteralValue val slv = structAssignment.value as? ArrayLiteralValue
@ -376,11 +379,11 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
// TODO use memcopy beyond a certain number of elements // TODO use memcopy beyond a certain number of elements
val identifier = structAssignment.target.identifier!! val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single() val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program.namespace)!! val targetVar = identifier.targetVarDecl(program)!!
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.namespace)!! val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
when { when {
sourceVar.struct!=null -> { sourceVar.struct!=null -> {
// struct memberwise copy // struct memberwise copy

View File

@ -1,13 +1,15 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter
import prog8.compiler.functions.BuiltinFunctions
class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() { class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() {
@ -106,18 +108,18 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
} }
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return afterFunctionCallArgs(functionCallStatement, functionCallStatement.definingScope()) return afterFunctionCallArgs(functionCallStatement)
} }
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> { override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
return afterFunctionCallArgs(functionCall, functionCall.definingScope()) return afterFunctionCallArgs(functionCall)
} }
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> { private fun afterFunctionCallArgs(call: IFunctionCall): Iterable<IAstModification> {
// see if a typecast is needed to convert the arguments into the required parameter's type // see if a typecast is needed to convert the arguments into the required parameter's type
val modifications = mutableListOf<IAstModification>() val modifications = mutableListOf<IAstModification>()
when(val sub = call.target.targetStatement(scope)) { when(val sub = call.target.targetStatement(program)) {
is Subroutine -> { is Subroutine -> {
sub.parameters.zip(call.args).forEachIndexed { index, pair -> sub.parameters.zip(call.args).forEachIndexed { index, pair ->
val argItype = pair.second.inferType(program) val argItype = pair.second.inferType(program)

View File

@ -0,0 +1,73 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.base.Position
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
internal class VariousCleanups: AstWalker() {
private val noModifications = emptyList<IAstModification>()
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
}
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
return if(parent is INameScope)
listOf(ScopeFlatten(scope, parent as INameScope))
else
noModifications
}
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
override fun perform() {
val idx = into.statements.indexOf(scope)
if(idx>=0) {
into.statements.addAll(idx+1, scope.statements)
into.statements.remove(scope)
}
}
}
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
if(typecast.expression is NumericLiteralValue) {
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
if(value.isValid)
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
}
return noModifications
}
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
return before(functionCallStatement as IFunctionCall, parent, functionCallStatement.position)
}
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
return before(functionCall as IFunctionCall, parent, functionCall.position)
}
private fun before(functionCall: IFunctionCall, parent: Node, position: Position): Iterable<IAstModification> {
if(functionCall.target.nameInSource==listOf("peek")) {
// peek(a) is synonymous with @(a)
val memread = DirectMemoryRead(functionCall.args.single(), position)
return listOf(IAstModification.ReplaceNode(functionCall as Node, memread, parent))
}
if(functionCall.target.nameInSource==listOf("poke")) {
// poke(a, v) is synonymous with @(a) = v
val tgt = AssignTarget(null, null, DirectMemoryWrite(functionCall.args[0], position), position)
val assign = Assignment(tgt, functionCall.args[1], position)
return listOf(IAstModification.ReplaceNode(functionCall as Node, assign, parent))
}
return noModifications
}
}

View File

@ -1,26 +1,26 @@
package prog8.ast.processing package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.TypecastExpression import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.compiler.CompilerException import prog8.compiler.CompilerException
import prog8.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
override fun visit(functionCall: FunctionCall) { override fun visit(functionCall: FunctionCall) {
val error = checkTypes(functionCall as IFunctionCall, functionCall.definingScope(), program) val error = checkTypes(functionCall as IFunctionCall, program)
if(error!=null) if(error!=null)
throw CompilerException(error) throw CompilerException(error)
} }
override fun visit(functionCallStatement: FunctionCallStatement) { override fun visit(functionCallStatement: FunctionCallStatement) {
val error = checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope(), program) val error = checkTypes(functionCallStatement as IFunctionCall, program)
if (error!=null) if (error!=null)
throw CompilerException(error) throw CompilerException(error)
} }
@ -39,13 +39,13 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
return false return false
} }
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? { fun checkTypes(call: IFunctionCall, program: Program): String? {
val argITypes = call.args.map { it.inferType(program) } val argITypes = call.args.map { it.inferType(program) }
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown } val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
if(firstUnknownDt>=0) if(firstUnknownDt>=0)
return "argument ${firstUnknownDt+1} invalid argument type" return "argument ${firstUnknownDt+1} invalid argument type"
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) } val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
val target = call.target.targetStatement(scope) val target = call.target.targetStatement(program)
if (target is Subroutine) { if (target is Subroutine) {
if(call.args.size != target.parameters.size) if(call.args.size != target.parameters.size)
return "invalid number of arguments" return "invalid number of arguments"

View File

@ -1,4 +1,4 @@
package prog8.functions package prog8.compiler.functions
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
@ -6,6 +6,7 @@ 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.*
@ -128,9 +129,13 @@ private val functionSignatures: List<FSignature> = listOf(
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 -> 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 -> 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 -> 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 -> 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 -> 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("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("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
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),
@ -286,7 +291,7 @@ private fun builtinOffsetof(args: List<Expression>, position: Position, program:
val idref = args[0] as? IdentifierReference val idref = args[0] as? IdentifierReference
?: throw SyntaxError("offsetof argument should be an identifier", position) ?: throw SyntaxError("offsetof argument should be an identifier", position)
val vardecl = idref.targetVarDecl(program.namespace)!! val vardecl = idref.targetVarDecl(program)!!
val struct = vardecl.struct val struct = vardecl.struct
if (struct == null || vardecl.datatype == DataType.STRUCT) if (struct == null || vardecl.datatype == DataType.STRUCT)
throw SyntaxError("offsetof can only be used on struct members", position) throw SyntaxError("offsetof can only be used on struct members", position)
@ -296,7 +301,7 @@ 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 += member.datatype.memorySize() offset += ICompilationTarget.instance.memorySize(member.datatype)
} }
throw SyntaxError("undefined struct member", position) throw SyntaxError("undefined struct member", position)
} }
@ -310,17 +315,17 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val dt = args[0].inferType(program) val dt = args[0].inferType(program)
if(dt.isKnown) { if(dt.isKnown) {
val target = (args[0] as IdentifierReference).targetStatement(program.namespace) 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.statements.map { (it as VarDecl).datatype.memorySize() }.sum(), 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(elementDt.memorySize() * length, position) numericLiteral(ICompilationTarget.instance.memorySize(elementDt) * length, position)
} }
dt.istype(DataType.STRUCT) -> { dt.istype(DataType.STRUCT) -> {
when (target) { when (target) {
@ -330,7 +335,7 @@ 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, dt.typeOrElse(DataType.STRUCT).memorySize(), position) else -> NumericLiteralValue(DataType.UBYTE, ICompilationTarget.instance.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
} }
} else { } else {
throw SyntaxError("sizeof invalid argument type", position) throw SyntaxError("sizeof invalid argument type", position)
@ -342,7 +347,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
if(args.size!=1) if(args.size!=1)
throw SyntaxError("len requires one argument", position) throw SyntaxError("len requires one argument", position)
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace) val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program)
var arraySize = directMemVar?.arraysize?.constIndex() var arraySize = directMemVar?.arraysize?.constIndex()
if(arraySize != null) if(arraySize != null)
return NumericLiteralValue.optimalInteger(arraySize, position) return NumericLiteralValue.optimalInteger(arraySize, position)
@ -350,7 +355,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position) return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
if(args[0] !is IdentifierReference) if(args[0] !is IdentifierReference)
throw SyntaxError("len argument should be an identifier", position) throw SyntaxError("len argument should be an identifier", position)
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace) val target = (args[0] as IdentifierReference).targetVarDecl(program)
?: throw CannotEvaluateException("len", "no target vardecl") ?: throw CannotEvaluateException("len", "no target vardecl")
return when(target.datatype) { return when(target.datatype) {

View File

@ -1,47 +0,0 @@
package prog8.compiler.target
import prog8.ast.Program
import prog8.ast.base.ErrorReporter
import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.cx16.CX16MachineDefinition
import java.nio.file.Path
internal interface CompilationTarget {
val name: String
val machine: IMachineDefinition
fun encodeString(str: String, altEncoding: Boolean): List<Short>
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
companion object {
lateinit var instance: CompilationTarget
}
}
internal object C64Target: CompilationTarget {
override val name = "c64"
override val machine = C64MachineDefinition
override fun encodeString(str: String, altEncoding: Boolean) =
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
AsmGen(program, errors, zp, options, path)
}
internal object Cx16Target: CompilationTarget {
override val name = "cx16"
override val machine = CX16MachineDefinition
override fun encodeString(str: String, altEncoding: Boolean) =
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
AsmGen(program, errors, zp, options, path)
}

View File

@ -0,0 +1,123 @@
package prog8.compiler.target
import prog8.ast.IStringEncoding
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.AssignTarget
import prog8.compiler.CompilationOptions
import prog8.compiler.ErrorReporter
import prog8.compiler.Zeropage
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.cx16.CX16MachineDefinition
import java.nio.file.Path
internal interface ICompilationTarget: IStringEncoding {
val name: String
val machine: IMachineDefinition
override fun encodeString(str: String, altEncoding: Boolean): List<Short>
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 {
val memAddr = target.memoryAddress
val arrayIdx = target.arrayindexed
val ident = target.identifier
when {
memAddr != null -> {
return when (memAddr.addressExpression) {
is NumericLiteralValue -> {
machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt())
}
is IdentifierReference -> {
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program)
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
false
}
else -> false
}
}
arrayIdx != null -> {
val targetStmt = arrayIdx.arrayvar.targetVarDecl(program)
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteralValue
if (addr != null)
machine.isRegularRAMaddress(addr.number.toInt())
else
false
} else true
}
ident != null -> {
val decl = ident.targetVarDecl(program)!!
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
true
}
else -> return true
}
}
}
internal object C64Target: ICompilationTarget {
override val name = "c64"
override val machine = C64MachineDefinition
override fun encodeString(str: String, altEncoding: Boolean) =
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
else -> -9999999
}
}
}
internal object Cx16Target: ICompilationTarget {
override val name = "cx16"
override val machine = CX16MachineDefinition
override fun encodeString(str: String, altEncoding: Boolean) =
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
else -> -9999999
}
}
}
internal fun asmGeneratorFor(
compTarget: ICompilationTarget,
program: Program,
errors: ErrorReporter,
zp: Zeropage,
options: CompilationOptions,
outputDir: Path
): IAssemblyGenerator
{
return AsmGen(program, errors, zp, options, compTarget, outputDir)
}

View File

@ -1,5 +1,6 @@
package prog8.compiler.target package prog8.compiler.target
import prog8.ast.IStringEncoding
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
@ -32,7 +33,11 @@ internal interface IMachineDefinition {
fun initializeZeropage(compilerOptions: CompilationOptions) fun initializeZeropage(compilerOptions: CompilationOptions)
fun getFloat(num: Number): IMachineFloat fun getFloat(num: Number): IMachineFloat
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program)
// TODO don't do the importing here, just return a list of modules to import...:
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
} }

View File

@ -2,13 +2,12 @@ package prog8.compiler.target.c64
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.OutputType import prog8.compiler.OutputType
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.IAssemblyProgram import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.generatedLabelPrefix import prog8.compiler.target.generatedLabelPrefix
import java.nio.file.Path import java.nio.file.Path
import kotlin.system.exitProcess import kotlin.system.exitProcess
class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyProgram { class AssemblyProgram(override val name: String, outputDir: Path, private val compTarget: String) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm") private val assemblyFile = outputDir.resolve("$name.asm")
private val prgFile = outputDir.resolve("$name.prg") private val prgFile = outputDir.resolve("$name.prg")
private val binFile = outputDir.resolve("$name.bin") private val binFile = outputDir.resolve("$name.bin")
@ -23,12 +22,12 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
val outFile = when (options.output) { val outFile = when (options.output) {
OutputType.PRG -> { OutputType.PRG -> {
command.add("--cbm-prg") command.add("--cbm-prg")
println("\nCreating prg for target ${CompilationTarget.instance.name}.") println("\nCreating prg for target $compTarget.")
prgFile prgFile
} }
OutputType.RAW -> { OutputType.RAW -> {
command.add("--nostart") command.add("--nostart")
println("\nCreating raw binary for target ${CompilationTarget.instance.name}.") println("\nCreating raw binary for target $compTarget.")
binFile binFile
} }
} }

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.c64 package prog8.compiler.target.c64
import prog8.ast.IStringEncoding
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
@ -30,9 +31,15 @@ internal object C64MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) { override fun importLibs(
compilerOptions: CompilationOptions,
importer: ModuleImporter,
program: Program,
encoder: IStringEncoding,
compilationTargetName: String)
{
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
importer.importLibraryModule(program, "syslib") importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
} }
override fun launchEmulator(programName: String) { override fun launchEmulator(programName: String) {

View File

@ -1,26 +1,18 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.c64.codegen
import prog8.ast.IFunctionCall import prog8.ast.*
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.antlr.escape import prog8.ast.antlr.escape
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.* import prog8.compiler.*
import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.FSignature
import prog8.compiler.target.* import prog8.compiler.target.*
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram
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.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
import prog8.compiler.target.generatedLabelPrefix
import prog8.functions.BuiltinFunctions
import prog8.functions.FSignature
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.file.Path import java.nio.file.Path
import java.time.LocalDate import java.time.LocalDate
@ -33,6 +25,7 @@ internal class AsmGen(private val program: Program,
val errors: ErrorReporter, val errors: ErrorReporter,
val zeropage: Zeropage, val zeropage: Zeropage,
val options: CompilationOptions, val options: CompilationOptions,
val compTarget: ICompilationTarget,
private val outputDir: Path): IAssemblyGenerator { private val outputDir: Path): IAssemblyGenerator {
// for expressions and augmented assignments: // for expressions and augmented assignments:
@ -43,12 +36,12 @@ internal class AsmGen(private val program: Program,
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname) private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
private val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>() private val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
private val breakpointLabels = mutableListOf<String>() private val breakpointLabels = mutableListOf<String>()
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this)
private val forloopsAsmGen = ForLoopsAsmGen(program, this) private val forloopsAsmGen = ForLoopsAsmGen(program, this)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen) private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
internal val loopEndLabels = ArrayDeque<String>() internal val loopEndLabels = ArrayDeque<String>()
private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>() private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
internal val slabs = mutableMapOf<String, Int>() internal val slabs = mutableMapOf<String, Int>()
@ -92,13 +85,13 @@ internal class AsmGen(private val program: Program,
} }
} }
return AssemblyProgram(program.name, outputDir) return AssemblyProgram(program.name, outputDir, compTarget.name)
} }
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
val cpu = when(CompilationTarget.instance.machine.cpu) { val cpu = when(compTarget.machine.cpu) {
CpuType.CPU6502 -> "6502" CpuType.CPU6502 -> "6502"
CpuType.CPU65c02 -> "65c02" CpuType.CPU65c02 -> "65c02"
else -> "unsupported" else -> "unsupported"
@ -113,16 +106,16 @@ internal class AsmGen(private val program: Program,
program.actualLoadAddress = program.definedLoadAddress program.actualLoadAddress = program.definedLoadAddress
if (program.actualLoadAddress == 0) // fix load address if (program.actualLoadAddress == 0) // fix load address
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC) program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
CompilationTarget.instance.machine.BASIC_LOAD_ADDRESS else CompilationTarget.instance.machine.RAW_LOAD_ADDRESS compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS
// the global prog8 variables needed // the global prog8 variables needed
val zp = CompilationTarget.instance.machine.zeropage val zp = compTarget.machine.zeropage
out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}") out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}") out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word") out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word") out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
out("P8ESTACK_LO = ${CompilationTarget.instance.machine.ESTACK_LO.toHex()}") out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
out("P8ESTACK_HI = ${CompilationTarget.instance.machine.ESTACK_HI.toHex()}") out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
when { when {
options.launcher == LauncherType.BASIC -> { options.launcher == LauncherType.BASIC -> {
@ -136,13 +129,13 @@ internal class AsmGen(private val program: Program,
out("+\t.word 0") out("+\t.word 0")
out("_prog8_entrypoint\t; assembly code starts here\n") out("_prog8_entrypoint\t; assembly code starts here\n")
if(!options.noSysInit) if(!options.noSysInit)
out(" jsr ${CompilationTarget.instance.name}.init_system") out(" jsr ${compTarget.name}.init_system")
} }
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 ${CompilationTarget.instance.name}.init_system") out(" jsr ${compTarget.name}.init_system")
} }
options.output == OutputType.RAW -> { options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----") out("; ---- raw assembler program ----")
@ -174,7 +167,7 @@ internal class AsmGen(private val program: Program,
// the global list of all floating point constants for the whole program // the global list of all floating point constants for the whole program
out("; global float constants") out("; global float constants")
for (flt in globalFloatConsts) { for (flt in globalFloatConsts) {
val floatFill = CompilationTarget.instance.machine.getFloat(flt.key).makeFloatFillAsm() val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
val floatvalue = flt.key val floatvalue = flt.key
out("${flt.value}\t.byte $floatFill ; float $floatvalue") out("${flt.value}\t.byte $floatFill ; float $floatvalue")
} }
@ -185,9 +178,10 @@ internal class AsmGen(private val program: Program,
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")) out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
if(block.address!=null) { val addr = block.address
out(".cerror * > ${block.address.toHex()}, 'block address overlaps by ', *-${block.address.toHex()},' bytes'") if(addr!=null) {
out("* = ${block.address.toHex()}") out(".cerror * > ${addr.toHex()}, 'block address overlaps by ', *-${addr.toHex()},' bytes'")
out("* = ${addr.toHex()}")
} }
outputSourceLine(block) outputSourceLine(block)
@ -344,7 +338,7 @@ internal class AsmGen(private val program: Program,
} }
val floatFills = array.map { val floatFills = array.map {
val number = (it as NumericLiteralValue).number val number = (it as NumericLiteralValue).number
CompilationTarget.instance.machine.getFloat(number).makeFloatFillAsm() compTarget.machine.getFloat(number).makeFloatFillAsm()
} }
out(name) out(name)
for (f in array.zip(floatFills)) for (f in array.zip(floatFills))
@ -364,10 +358,11 @@ internal class AsmGen(private val program: Program,
} }
val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine } val asmSubs = statements.filterIsInstance<Subroutine>().filter { it.isAsmSubroutine }
for(sub in asmSubs) { for(sub in asmSubs) {
if(sub.asmAddress!=null) { val addr = sub.asmAddress
if(addr!=null) {
if(sub.statements.isNotEmpty()) if(sub.statements.isNotEmpty())
throw AssemblyError("kernel subroutine cannot have statements") throw AssemblyError("kernel subroutine cannot have statements")
out(" ${sub.name} = ${sub.asmAddress.toHex()}") out(" ${sub.name} = ${addr.toHex()}")
} }
} }
} }
@ -433,10 +428,10 @@ internal class AsmGen(private val program: Program,
"$" + it.number.toInt().toString(16).padStart(4, '0') "$" + it.number.toInt().toString(16).padStart(4, '0')
} }
is AddressOf -> { is AddressOf -> {
it.identifier.firstStructVarName(program.namespace) ?: asmSymbolName(it.identifier) it.identifier.firstStructVarName(program) ?: asmSymbolName(it.identifier)
} }
is IdentifierReference -> { is IdentifierReference -> {
it.firstStructVarName(program.namespace) ?: asmSymbolName(it) it.firstStructVarName(program) ?: asmSymbolName(it)
} }
else -> throw AssemblyError("weird array elt dt") else -> throw AssemblyError("weird array elt dt")
} }
@ -498,17 +493,23 @@ internal class AsmGen(private val program: Program,
} }
internal fun asmSymbolName(identifier: IdentifierReference): String { internal fun asmSymbolName(identifier: IdentifierReference): String {
return if(identifier.memberOfStruct(program.namespace)!=null) { return if(identifier.memberOfStruct(program)!=null) {
val name = identifier.targetVarDecl(program.namespace)!!.name val name = identifier.targetVarDecl(program)!!.name
fixNameSymbols(name) fixNameSymbols(name)
} else { } else {
fixNameSymbols(identifier.nameInSource.joinToString(".")) fixNameSymbols(identifier.nameInSource.joinToString("."))
} }
} }
internal fun asmSymbolName(regs: RegisterOrPair): String =
if(regs in Cx16VirtualRegisters)
"cx16." + regs.toString().toLowerCase()
else
throw AssemblyError("no symbol name for register $regs")
internal fun asmVariableName(identifier: IdentifierReference): String { internal fun asmVariableName(identifier: IdentifierReference): String {
return if(identifier.memberOfStruct(program.namespace)!=null) { return if(identifier.memberOfStruct(program)!=null) {
val name = identifier.targetVarDecl(program.namespace)!!.name val name = identifier.targetVarDecl(program)!!.name
fixNameSymbols(name) fixNameSymbols(name)
} else { } else {
fixNameSymbols(identifier.nameInSource.joinToString(".")) fixNameSymbols(identifier.nameInSource.joinToString("."))
@ -524,9 +525,9 @@ internal class AsmGen(private val program: Program,
internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> { internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> {
// returns if the pointer is already on the ZP itself or not (in the latter case SCRATCH_W1 is used as intermediary) // returns if the pointer is already on the ZP itself or not (in the latter case SCRATCH_W1 is used as intermediary)
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!! val vardecl = pointervar.targetVarDecl(program)!!
val scopedName = vardecl.makeScopedName(vardecl.name) val scopedName = vardecl.makeScopedName(vardecl.name)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if (compTarget.machine.cpu == 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)")
@ -561,7 +562,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if (compTarget.machine.cpu == 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")
@ -590,7 +591,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx") if (compTarget.machine.cpu == 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")
@ -599,7 +600,7 @@ internal class AsmGen(private val program: Program,
} }
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy") if (compTarget.machine.cpu == 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")
@ -611,7 +612,7 @@ internal class AsmGen(private val program: Program,
} }
internal fun restoreRegisterLocal(register: CpuRegister) { internal fun restoreRegisterLocal(register: CpuRegister) {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if (compTarget.machine.cpu == 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")
@ -636,7 +637,7 @@ internal class AsmGen(private val program: Program,
out(" pla") out(" pla")
} }
CpuRegister.X -> { CpuRegister.X -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx") if (compTarget.machine.cpu == 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")
@ -645,7 +646,7 @@ internal class AsmGen(private val program: Program,
} }
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply") if (compTarget.machine.cpu == 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")
@ -705,7 +706,7 @@ internal class AsmGen(private val program: Program,
val reg = register.toString().toLowerCase() val reg = register.toString().toLowerCase()
val indexnum = expr.indexer.constIndex() val indexnum = expr.indexer.constIndex()
if(indexnum!=null) { if(indexnum!=null) {
val indexValue = indexnum * elementDt.memorySize() + if(addOneExtra) 1 else 0 val indexValue = indexnum * compTarget.memorySize(elementDt) + if(addOneExtra) 1 else 0
out(" ld$reg #$indexValue") out(" ld$reg #$indexValue")
return return
} }
@ -731,7 +732,7 @@ internal class AsmGen(private val program: Program,
} }
} }
DataType.FLOAT -> { DataType.FLOAT -> {
require(DataType.FLOAT.memorySize()==5) require(compTarget.memorySize(DataType.FLOAT)==5)
out(""" out("""
lda $indexName lda $indexName
asl a asl a
@ -758,7 +759,7 @@ internal class AsmGen(private val program: Program,
} }
} }
DataType.FLOAT -> { DataType.FLOAT -> {
require(DataType.FLOAT.memorySize()==5) require(compTarget.memorySize(DataType.FLOAT)==5)
out(""" out("""
lda $indexName lda $indexName
asl a asl a
@ -782,8 +783,8 @@ internal class AsmGen(private val program: Program,
internal fun translateExpression(indexer: ArrayIndex) = internal fun translateExpression(indexer: ArrayIndex) =
expressionsAsmGen.translateExpression(indexer) expressionsAsmGen.translateExpression(indexer)
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean) = internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack) builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack, resultRegister)
internal fun translateFunctionCall(functionCall: FunctionCall) = internal fun translateFunctionCall(functionCall: FunctionCall) =
functioncallAsmGen.translateFunctionCall(functionCall) functioncallAsmGen.translateFunctionCall(functionCall)
@ -962,16 +963,14 @@ internal class AsmGen(private val program: Program,
} }
} }
is IdentifierReference -> { is IdentifierReference -> {
val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program.namespace) as VarDecl val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program) as VarDecl
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 -> {
out(" lda $name") repeatByteCountVar(name, repeatLabel, endLabel, stmt.body)
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
out(" lda $name | ldy $name+1") repeatWordCountVar(name, repeatLabel, endLabel, stmt.body)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
} }
else -> throw AssemblyError("invalid loop variable datatype $vardecl") else -> throw AssemblyError("invalid loop variable datatype $vardecl")
} }
@ -1001,6 +1000,7 @@ internal class AsmGen(private val program: Program,
if(constIterations==0) if(constIterations==0)
return return
// note: A/Y must have been loaded with the number of iterations already! // note: A/Y must have been loaded with the number of iterations already!
// TODO can be even more optimized by iterating over pages
val counterVar = makeLabel("repeatcounter") val counterVar = makeLabel("repeatcounter")
out(""" out("""
sta $counterVar sta $counterVar
@ -1035,22 +1035,54 @@ $counterVar .word 0""")
val counterVar = makeLabel("repeatcounter") val counterVar = makeLabel("repeatcounter")
if(constIterations==null) if(constIterations==null)
out(" beq $endLabel") out(" beq $endLabel")
out(""" out(" sta $counterVar")
sta $counterVar out(repeatLabel)
$repeatLabel""")
translate(body) translate(body)
out(""" out("""
dec $counterVar dec $counterVar
bne $repeatLabel bne $repeatLabel
beq $endLabel""") beq $endLabel
if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
out("""$counterVar = $zpAddr ; auto zp UBYTE""")
} else {
out("""
$counterVar .byte 0""") $counterVar .byte 0""")
out(endLabel)
} }
private fun repeatByteCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: cannot use original counter variable because it should retain its original value
val counterVar = makeLabel("repeatcounter")
out(" lda $repeatCountVar | beq $endLabel | sta $counterVar")
out(repeatLabel)
translate(body)
out(" dec $counterVar | bne $repeatLabel")
// inline countervar:
out("""
beq $endLabel
$counterVar .byte 0""")
out(endLabel)
}
private fun repeatWordCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// TODO can be even more optimized by iterating over pages
// note: cannot use original counter variable because it should retain its original value
val counterVar = makeLabel("repeatcounter")
out("""
lda $repeatCountVar
sta $counterVar
ora $repeatCountVar+1
beq $endLabel
lda $repeatCountVar+1
sta $counterVar+1""")
out(repeatLabel)
translate(body)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel
beq $endLabel
$counterVar .word 0""")
out(endLabel) out(endLabel)
} }
@ -1239,17 +1271,20 @@ $label nop""")
} }
private fun getJumpTarget(jmp: Jump): String { private fun getJumpTarget(jmp: Jump): String {
val ident = jmp.identifier
val label = jmp.generatedLabel
val addr = jmp.address
return when { return when {
jmp.identifier!=null -> { ident!=null -> {
val target = jmp.identifier.targetStatement(program.namespace) val target = ident.targetStatement(program)
val asmName = asmSymbolName(jmp.identifier) val asmName = asmSymbolName(ident)
if(target is Label) if(target is Label)
"_$asmName" // prefix with underscore to jump to local label "_$asmName" // prefix with underscore to jump to local label
else else
asmName asmName
} }
jmp.generatedLabel!=null -> jmp.generatedLabel label!=null -> label
jmp.address!=null -> jmp.address.toHex() addr!=null -> addr.toHex()
else -> "????" else -> "????"
} }
} }
@ -1264,12 +1299,12 @@ $label nop""")
when (returnType) { when (returnType) {
in NumericDatatypes -> { in NumericDatatypes -> {
assignExpressionToRegister(returnvalue, returnReg.registerOrPair) assignExpressionToRegister(returnvalue, returnReg.registerOrPair!!)
} }
else -> { else -> {
// all else take its address and assign that also to AY register pair // all else take its address and assign that also to AY register pair
val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position) val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position)
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair) assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!)
} }
} }
} }
@ -1294,7 +1329,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(compTarget.machine.cpu == 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")
@ -1308,7 +1343,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(compTarget.machine.cpu == CpuType.CPU65c02)
out(" stz $asmvar+1") out(" stz $asmvar+1")
else else
out(" lda #0 | sta $asmvar+1") out(" lda #0 | sta $asmvar+1")
@ -1328,7 +1363,7 @@ $label nop""")
internal fun isZpVar(scopedName: String): Boolean = scopedName in allocatedZeropageVariables internal fun isZpVar(scopedName: String): Boolean = scopedName in allocatedZeropageVariables
internal fun isZpVar(variable: IdentifierReference): Boolean { internal fun isZpVar(variable: IdentifierReference): Boolean {
val vardecl = variable.targetVarDecl(program.namespace)!! val vardecl = variable.targetVarDecl(program)!!
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
} }
@ -1411,5 +1446,4 @@ $label nop""")
} }
return false return false
} }
} }

View File

@ -6,27 +6,24 @@ 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.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource import prog8.compiler.functions.FSignature
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment import prog8.compiler.target.c64.codegen.assignment.*
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.target.subroutineFloatEvalResultVar2 import prog8.compiler.target.subroutineFloatEvalResultVar2
import prog8.compiler.toHex
import prog8.functions.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean) { internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack) translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
} }
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) { internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) {
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false) translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
} }
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean) { private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if (discardResult && func.pure) if (discardResult && func.pure)
return // can just ignore the whole function call altogether return // can just ignore the whole function call altogether
@ -34,41 +31,46 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("cannot both discard the result AND put it onto stack") throw AssemblyError("cannot both discard the result AND put it onto stack")
val sscope = (fcall as Node).definingSubroutine() val sscope = (fcall as Node).definingSubroutine()
when (func.name) { when (func.name) {
"msb" -> funcMsb(fcall, resultToStack) "msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack) "lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack) "mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"abs" -> funcAbs(fcall, func, resultToStack, sscope) "abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
"swap" -> funcSwap(fcall) "swap" -> funcSwap(fcall)
"min", "max" -> funcMinMax(fcall, func, resultToStack) "min", "max" -> funcMinMax(fcall, func, resultToStack, resultRegister, sscope)
"sum" -> funcSum(fcall, resultToStack) "sum" -> funcSum(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, func, resultToStack) "any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
"sin8", "sin8u", "sin16", "sin16u", "sin8", "sin8u", "sin16", "sin16u",
"cos8", "cos8u", "cos16", "cos16u" -> funcSinCosInt(fcall, func, resultToStack, sscope) "cos8", "cos8u", "cos16", "cos16u" -> funcSinCosInt(fcall, func, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, func, resultToStack, sscope) "sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
"sin", "cos", "tan", "atan", "sin", "cos", "tan", "atan",
"ln", "log2", "sqrt", "rad", "ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil", "deg", "round", "floor", "ceil",
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, sscope) "rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack) "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, sscope) "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall) "ror" -> funcRor(fcall)
"ror2" -> funcRor2(fcall) "ror2" -> funcRor2(fcall)
"sort" -> funcSort(fcall) "sort" -> funcSort(fcall)
"reverse" -> funcReverse(fcall) "reverse" -> funcReverse(fcall)
"memory" -> funcMemory(fcall, discardResult, resultToStack) "memory" -> funcMemory(fcall, discardResult, resultToStack, resultRegister)
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
"pokew" -> funcPokeW(fcall)
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
else -> TODO("missing asmgen for builtin func ${func.name}") else -> TODO("missing asmgen for builtin func ${func.name}")
} }
} }
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean) { 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 scope = fcall.definingScope()
val nameRef = fcall.args[0] as IdentifierReference val nameRef = fcall.args[0] as IdentifierReference
val name = (nameRef.targetVarDecl(program.namespace)!!.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()
val existingSize = asmgen.slabs[name] val existingSize = asmgen.slabs[name]
@ -82,7 +84,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if(resultToStack) if(resultToStack)
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null) AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
else else
AsmAssignTarget.fromRegisters(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, fcall.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
@ -92,29 +94,37 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.slabs[name] = size asmgen.slabs[name] = size
} }
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
} }
private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_${func.name}_stack") asmgen.out(" jsr prog8_lib.func_${func.name}_stack")
else else
when(func.name) { when(func.name) {
"sin8", "sin8u", "cos8", "cos8u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_A") "sin8", "sin8u", "cos8", "cos8u" -> {
"sin16", "sin16u", "cos16", "cos16u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY") asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
"sin16", "sin16u", "cos16", "cos16u" -> {
asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
} }
} }
private fun funcReverse(fcall: IFunctionCall) { private fun funcReverse(fcall: IFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is IdentifierReference) { if (variable is IdentifierReference) {
val decl = variable.targetVarDecl(program.namespace)!! val decl = variable.targetVarDecl(program)!!
val varName = asmgen.asmVariableName(variable) val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraysize!!.constIndex() val numElements = decl.arraysize!!.constIndex()
when (decl.datatype) { when (decl.datatype) {
@ -153,7 +163,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcSort(fcall: IFunctionCall) { private fun funcSort(fcall: IFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is IdentifierReference) { if (variable is IdentifierReference) {
val decl = variable.targetVarDecl(program.namespace)!! val decl = variable.targetVarDecl(program)!!
val varName = asmgen.asmVariableName(variable) val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraysize!!.constIndex() val numElements = decl.arraysize!!.constIndex()
when (decl.datatype) { when (decl.datatype) {
@ -390,15 +400,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.assignExpressionToVariable(indexerExpr, "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, scope: Subroutine?) { private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
if(resultToStack) if(resultToStack)
asmgen.out(" jsr floats.func_${func.name}_stack") asmgen.out(" jsr floats.func_${func.name}_stack")
else else {
asmgen.out(" jsr floats.func_${func.name}_fac1") asmgen.out(" jsr floats.func_${func.name}_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
}
} }
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().inferType(program)
if(resultToStack) { if(resultToStack) {
@ -419,10 +431,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A") DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
} }
} }
private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) { private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0]) outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().inferType(program)
if(resultToStack) { if(resultToStack) {
@ -439,10 +452,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A") DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
} }
} }
private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) { private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0]) outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().inferType(program)
if(resultToStack) { if(resultToStack) {
@ -456,17 +470,32 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} else { } else {
when (dt.typeOrElse(DataType.STRUCT)) { when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A") DataType.ARRAY_UB, DataType.STR -> {
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A") asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY") assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY") }
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_fac1") DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
}
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
} }
} }
private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean) { private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0]) outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program) val dt = fcall.args.single().inferType(program)
if(resultToStack) { if(resultToStack) {
@ -480,11 +509,26 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} else { } else {
when (dt.typeOrElse(DataType.STRUCT)) { when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY") DataType.ARRAY_UB, DataType.STR -> {
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_into_AY") asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY") assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_into_AY") }
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_fac1") DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_sum_f_fac1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
}
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
} }
@ -639,8 +683,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() * elementDt.memorySize() val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
val index2 = indexValue2.number.toInt() * elementDt.memorySize() val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(""" asmgen.out("""
@ -753,7 +797,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() * elementDt.memorySize() val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
val idxAsmName2 = asmgen.asmVariableName(indexName2) val idxAsmName2 = asmgen.asmVariableName(indexName2)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
@ -812,7 +856,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() * elementDt.memorySize() val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(""" asmgen.out("""
@ -868,7 +912,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} }
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine?) { private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
translateArguments(fcall.args, func, scope) translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT) val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT)
if(resultToStack) { if(resultToStack) {
@ -880,40 +924,221 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} else { } else {
when (dt) { when (dt) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_into_A") in ByteDatatypes -> {
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_into_AY") asmgen.out(" jsr prog8_lib.abs_b_into_A")
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_fac1") assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A)
}
in WordDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.abs_f_fac1")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1)
}
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
} }
private fun funcRnd(func: FSignature, resultToStack: Boolean) { private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) { when(func.name) {
"rnd" -> { "rnd" -> {
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rnd_stack") asmgen.out(" jsr prog8_lib.func_rnd_stack")
else else {
asmgen.out(" jsr math.randbyte") asmgen.out(" jsr math.randbyte")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
} }
"rndw" -> { "rndw" -> {
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rndw_stack") asmgen.out(" jsr prog8_lib.func_rndw_stack")
else else {
asmgen.out(" jsr math.randword") asmgen.out(" jsr math.randword")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
} }
else -> throw AssemblyError("wrong func") else -> throw AssemblyError("wrong func")
} }
} }
private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean) { private fun funcPokeW(fcall: IFunctionCall) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb when(val addrExpr = fcall.args[0]) {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb is NumericLiteralValue -> {
if(resultToStack) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex") val addr = addrExpr.number.toHex()
asmgen.out(" sta $addr | sty ${addr}+1")
return
}
is IdentifierReference -> {
val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
asmgen.out("""
sta ($varname)
txa
ldy #1
sta ($varname),y""")
} else {
asmgen.out("""
ldy #0
sta ($varname),y
txa
iny
sta ($varname),y""")
}
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
is BinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
asmgen.out("""
ldy #$index
sta ($varname),y
txa
iny
sta ($varname),y""")
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
} }
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean) { asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD, null)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_pokew")
}
private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
when(val addrExpr = fcall.args[0]) {
is NumericLiteralValue -> {
val addr = addrExpr.number.toHex()
asmgen.out(" lda $addr | ldy ${addr}+1")
}
is IdentifierReference -> {
val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
asmgen.out("""
ldy #1
lda ($varname),y
tay
lda ($varname)""")
} else {
asmgen.out("""
ldy #0
lda ($varname),y
pha
iny
lda ($varname),y
tay
pla""")
}
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
is BinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
asmgen.out("""
ldy #$index
lda ($varname),y
pha
iny
lda ($varname),y
tay
pla""")
} else {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
else -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
}
}
if(resultToStack){
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
when(resultRegister ?: RegisterOrPair.AY) {
RegisterOrPair.AY -> {}
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
RegisterOrPair.XY -> asmgen.out(" tax")
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().toLowerCase()} | sty cx16.${resultRegister.toString().toLowerCase()}+1")
else -> throw AssemblyError("invalid reg")
}
}
}
private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteralValue || fcall.args[0] is IdentifierReference)
if(!needAsave) {
val mr0 = fcall.args[0] as? DirectMemoryRead
val mr1 = fcall.args[1] as? DirectMemoryRead
if (mr0 != null)
needAsave = mr0.addressExpression !is NumericLiteralValue && mr0.addressExpression !is IdentifierReference
if (mr1 != null)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteralValue && mr1.addressExpression !is IdentifierReference)
}
when(reg) {
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
if(needAsave)
asmgen.out(" pla")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
asmgen.out(" tax")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}+1")
}
else -> throw AssemblyError("invalid mkword target reg")
}
}
}
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single() val arg = fcall.args.single()
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes) if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
throw AssemblyError("msb required word argument") throw AssemblyError("msb required word argument")
@ -921,19 +1146,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("msb(const) should have been const-folded away") throw AssemblyError("msb(const) should have been const-folded away")
if (arg is IdentifierReference) { if (arg is IdentifierReference) {
val sourceName = asmgen.asmVariableName(arg) val sourceName = asmgen.asmVariableName(arg)
asmgen.out(" lda $sourceName+1") if(resultToStack) {
if (resultToStack) asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else { } else {
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
else -> throw AssemblyError("invalid reg")
}
}
} else {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
if (resultToStack)
asmgen.out(" tya | sta P8ESTACK_LO,x | dex") asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
else } else {
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tya") asmgen.out(" tya")
} }
RegisterOrPair.X -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" pla")
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" pla")
}
else -> throw AssemblyError("invalid reg")
}
}
}
} }
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean) { private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single() val arg = fcall.args.single()
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes) if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
throw AssemblyError("lsb required word argument") throw AssemblyError("lsb required word argument")
@ -942,16 +1191,45 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if (arg is IdentifierReference) { if (arg is IdentifierReference) {
val sourceName = asmgen.asmVariableName(arg) val sourceName = asmgen.asmVariableName(arg)
asmgen.out(" lda $sourceName") if(resultToStack) {
if (resultToStack) asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else { } else {
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
else -> throw AssemblyError("invalid reg")
}
}
} else {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction. // NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
// this is required because the compiler assumes the status bits are set according to what A is (lsb) // this is required because the compiler assumes the status bits are set according to what A is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression. // and will not generate another cmp when lsb() is directly used inside a comparison expression.
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex") asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.X -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | pla | cpy #0")
}
else -> throw AssemblyError("invalid reg")
}
}
} }
} }
@ -959,7 +1237,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// address in P8ZP_SCRATCH_W1, number of elements in A // address in P8ZP_SCRATCH_W1, number of elements in A
arg as IdentifierReference arg as IdentifierReference
val identifierName = asmgen.asmVariableName(arg) val identifierName = asmgen.asmVariableName(arg)
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.constIndex()!! val size = arg.targetVarDecl(program)!!.arraysize!!.constIndex()!!
asmgen.out(""" asmgen.out("""
lda #<$identifierName lda #<$identifierName
ldy #>$identifierName ldy #>$identifierName

View File

@ -6,12 +6,11 @@ import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.subroutineFloatEvalResultVar1 import prog8.compiler.target.subroutineFloatEvalResultVar1
import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -1327,10 +1326,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
private fun translateFunctionCallResultOntoStack(call: FunctionCall) { private fun translateFunctionCallResultOntoStack(call: FunctionCall) {
// only for use in nested expression evaluation // only for use in nested expression evaluation
val sub = call.target.targetStatement(program.namespace) val sub = call.target.targetStatement(program)
if(sub is BuiltinFunctionStatementPlaceholder) { if(sub is BuiltinFunctionStatementPlaceholder) {
val builtinFunc = BuiltinFunctions.getValue(sub.name) val builtinFunc = BuiltinFunctions.getValue(sub.name)
asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true) asmgen.translateBuiltinFunctionCallExpression(call, builtinFunc, true, null)
} else { } else {
sub as Subroutine sub as Subroutine
asmgen.saveXbeforeCall(call) asmgen.saveXbeforeCall(call)
@ -1394,7 +1393,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
when(typecast.type) { when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {} DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==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")
@ -1460,7 +1459,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
fun assignViaExprEval() { fun assignViaExprEval() {
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
if (pushResultOnEstack) { if (pushResultOnEstack) {
asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x") asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
} else { } else {
@ -1674,7 +1673,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
} }
DataType.UWORD -> { DataType.UWORD -> {
if(amount>=16) { if(amount>=16) {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x") asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x") asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
@ -1814,7 +1813,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x") DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
DataType.BYTE -> asmgen.out(" asl P8ESTACK_LO+1,x | ror P8ESTACK_LO+1,x") DataType.BYTE -> asmgen.out(" asl P8ESTACK_LO+1,x | ror P8ESTACK_LO+1,x")
DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
DataType.WORD -> asmgen.out(" asl P8ESTACK_HI+1,x | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") DataType.WORD -> asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
else -> throw AssemblyError("wrong dt") else -> throw AssemblyError("wrong dt")
} }
return return
@ -1889,7 +1888,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
val elementDt = elementIDt.typeOrElse(DataType.STRUCT) val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar) val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
if(arrayExpr.indexer.indexNum!=null) { if(arrayExpr.indexer.indexNum!=null) {
val indexValue = arrayExpr.indexer.constIndex()!! * elementDt.memorySize() val indexValue = arrayExpr.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt)
when(elementDt) { when(elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex") asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")

View File

@ -7,8 +7,8 @@ import prog8.ast.base.RegisterOrPair
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpr import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.toHex
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -239,7 +239,7 @@ $endLabel""")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident) val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program.namespace)!! val decl = ident.targetVarDecl(program)!!
when(iterableDt) { when(iterableDt) {
DataType.STR -> { DataType.STR -> {
asmgen.out(""" asmgen.out("""
@ -369,7 +369,7 @@ $loopLabel""")
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt") throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
} }
2 -> { 2 -> {
if(range.last==255) { if(range.last==255 || range.last==254) {
asmgen.out(""" asmgen.out("""
inc $varname inc $varname
beq $endLabel beq $endLabel
@ -377,12 +377,11 @@ $loopLabel""")
bne $loopLabel""") bne $loopLabel""")
} else { } else {
asmgen.out(""" asmgen.out("""
inc $varname
inc $varname
lda $varname lda $varname
cmp #${range.last} cmp #${range.last+2}
beq $endLabel bne $loopLabel""")
inc $varname
inc $varname
jmp $loopLabel""")
} }
} }
-2 -> { -2 -> {
@ -399,12 +398,11 @@ $loopLabel""")
dec $varname dec $varname
bne $loopLabel""") bne $loopLabel""")
else -> asmgen.out(""" else -> asmgen.out("""
dec $varname
dec $varname
lda $varname lda $varname
cmp #${range.last} cmp #${range.last-2}
beq $endLabel bne $loopLabel""")
dec $varname
dec $varname
jmp $loopLabel""")
} }
} }
else -> { else -> {
@ -480,11 +478,10 @@ $loopLabel""")
$endLabel""") $endLabel""")
} else { } else {
asmgen.out(""" asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
inc $varname inc $varname
jmp $loopLabel lda $varname
cmp #${range.last+1}
bne $loopLabel
$endLabel""") $endLabel""")
} }
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
@ -512,16 +509,15 @@ $endLabel""")
1 -> { 1 -> {
asmgen.out(""" asmgen.out("""
dec $varname dec $varname
jmp $loopLabel bne $loopLabel
$endLabel""") $endLabel""")
} }
else -> { else -> {
asmgen.out(""" asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
dec $varname dec $varname
jmp $loopLabel lda $varname
cmp #${range.last-1}
bne $loopLabel
$endLabel""") $endLabel""")
} }
} }
@ -546,7 +542,6 @@ $loopLabel""")
bne + bne +
lda $varname+1 lda $varname+1
cmp #>${range.last} cmp #>${range.last}
bne +
beq $endLabel beq $endLabel
+ inc $varname + inc $varname
bne $loopLabel bne $loopLabel
@ -574,7 +569,6 @@ $loopLabel""")
bne + bne +
lda $varname+1 lda $varname+1
cmp #>${range.last} cmp #>${range.last}
bne +
beq $endLabel beq $endLabel
+ lda $varname + lda $varname
bne + bne +

View File

@ -7,9 +7,11 @@ 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.CompilationTarget
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.* import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.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) {
@ -22,7 +24,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
internal fun saveXbeforeCall(stmt: IFunctionCall) { internal fun saveXbeforeCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) { if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA() val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
@ -34,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
internal fun restoreXafterCall(stmt: IFunctionCall) { internal fun restoreXafterCall(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
if(sub.shouldSaveX()) { if(sub.shouldSaveX()) {
val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls val regSaveOnStack = sub.asmAddress==null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA() val (keepAonEntry: Boolean, keepAonReturn: Boolean) = sub.shouldKeepA()
@ -52,9 +54,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!! // NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this) // (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}") val sub = stmt.target.targetSubroutine(program) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val subName = asmgen.asmSymbolName(stmt.target) val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) { if(stmt.args.isNotEmpty()) {
if(sub.asmParameterRegisters.isEmpty()) { if(sub.asmParameterRegisters.isEmpty()) {
// via variables // via variables
for(arg in sub.parameters.withIndex().zip(stmt.args)) { for(arg in sub.parameters.withIndex().zip(stmt.args)) {
@ -66,16 +69,31 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// just a single parameter, no risk of clobbering registers // just a single parameter, no risk of clobbering registers
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0]) argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
} else { } else {
fun isNoClobberRisk(expr: Expression): Boolean {
if(expr is AddressOf ||
expr is NumericLiteralValue ||
expr is StringLiteralValue ||
expr is ArrayLiteralValue ||
expr is IdentifierReference)
return true
if(expr is FunctionCall) {
if(expr.target.nameInSource==listOf("lsb") || expr.target.nameInSource==listOf("msb"))
return isNoClobberRisk(expr.args[0])
if(expr.target.nameInSource==listOf("mkword"))
return isNoClobberRisk(expr.args[0]) && isNoClobberRisk(expr.args[1])
}
return false
}
when { when {
stmt.args.all {it is AddressOf || stmt.args.all {isNoClobberRisk(it)} -> {
it is NumericLiteralValue ||
it is StringLiteralValue ||
it is ArrayLiteralValue ||
it is IdentifierReference} -> {
// 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.
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters) val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
val (vregsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters } val (cx16virtualRegsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
for(arg in vregsArgsInfo) for(arg in cx16virtualRegsArgsInfo)
argumentViaRegister(sub, arg.first.first, arg.first.second) argumentViaRegister(sub, arg.first.first, arg.first.second)
for(arg in otherRegsArgsInfo) for(arg in otherRegsArgsInfo)
argumentViaRegister(sub, arg.first.first, arg.first.second) argumentViaRegister(sub, arg.first.first, arg.first.second)
@ -150,7 +168,7 @@ 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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (asmgen.compTarget.machine.cpu == 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")
@ -240,8 +258,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
if(valueDt largerThan requiredDt) if(valueDt largerThan requiredDt)
throw AssemblyError("can only convert byte values to word param types") throw AssemblyError("can only convert byte values to word param types")
} }
when { if (statusflag!=null) {
statusflag!=null -> {
if(requiredDt!=valueDt) if(requiredDt!=valueDt)
throw AssemblyError("for statusflag, byte value is required") throw AssemblyError("for statusflag, byte value is required")
if (statusflag == Statusflag.Pc) { if (statusflag == Statusflag.Pc) {
@ -260,9 +277,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
beq + beq +
sec sec
bcs ++ bcs ++
+ clc + clc
+ pla + pla
""") """)
} }
else -> { else -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
@ -270,14 +287,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
beq + beq +
sec sec
bcs ++ bcs ++
+ clc + clc
+""") +""")
} }
} }
} else throw AssemblyError("can only use Carry as status flag parameter")
} }
else throw AssemblyError("can only use Carry as status flag parameter") else {
}
else -> {
// via register or register pair // via register or register pair
register!! register!!
if(requiredDt largerThan valueDt) { if(requiredDt largerThan valueDt) {
@ -286,8 +302,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub) asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub)
asmgen.signExtendVariableLsb(scratchVar, valueDt) asmgen.signExtendVariableLsb(scratchVar, valueDt)
asmgen.assignVariableToRegister(scratchVar, register) asmgen.assignVariableToRegister(scratchVar, register)
} } else {
else {
val target: AsmAssignTarget = val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register) AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
@ -307,7 +322,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} }
} }
} }
}
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType) if(argType isAssignableTo paramType)

View File

@ -5,8 +5,8 @@ import prog8.ast.base.*
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.PostIncrDecr import prog8.ast.statements.PostIncrDecr
import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.toHex
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -67,7 +67,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
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) { if(targetArrayIdx.indexer.indexNum!=null) {
val indexValue = targetArrayIdx.indexer.constIndex()!! * elementDt.memorySize() val indexValue = targetArrayIdx.indexer.constIndex()!! * asmgen.compTarget.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 -> {

View File

@ -5,6 +5,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
@ -104,7 +105,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
private val variableAsmName: String? = null, private val variableAsmName: String? = null,
val array: ArrayIndexedExpression? = null, val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryRead? = null, val memory: DirectMemoryRead? = null,
val register: CpuRegister? = null, val register: RegisterOrPair? = null,
val number: NumericLiteralValue? = null, val number: NumericLiteralValue? = null,
val expression: Expression? = null val expression: Expression? = null
) )
@ -138,7 +139,15 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation") is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is IdentifierReference -> { is IdentifierReference -> {
val dt = value.inferType(program).typeOrElse(DataType.STRUCT) val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value)) val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(dt==DataType.UWORD && varName.toLowerCase().startsWith("cx16.r")) {
val regStr = varName.toLowerCase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.toUpperCase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
} else {
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
}
} }
is DirectMemoryRead -> { is DirectMemoryRead -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value) AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
@ -148,7 +157,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value) AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
} }
is FunctionCall -> { is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) { when (val sub = value.target.targetStatement(program)) {
is Subroutine -> { is Subroutine -> {
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
?: throw AssemblyError("can't translate zero return values in assignment") ?: throw AssemblyError("can't translate zero return values in assignment")
@ -203,7 +212,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
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(source.datatype.memorySize() <= target.datatype.memorySize()) { require(ICompilationTarget.instance.memorySize(source.datatype) <= ICompilationTarget.instance.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"
} }
} }

View File

@ -4,17 +4,17 @@ 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.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget import prog8.compiler.functions.BuiltinFunctions
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.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions
import prog8.functions.builtinFunctionReturnType
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen, private val exprAsmgen: ExpressionsAsmGen) { internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
private val exprAsmgen: ExpressionsAsmGen) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen) private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
@ -66,7 +66,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val arrayVarName = asmgen.asmVariableName(value.arrayvar) val arrayVarName = asmgen.asmVariableName(value.arrayvar)
if (value.indexer.indexNum!=null) { if (value.indexer.indexNum!=null) {
// constant array index value // constant array index value
val indexValue = value.indexer.constIndex()!! * elementDt.memorySize() val indexValue = value.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt)
when (elementDt) { when (elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue") asmgen.out(" lda $arrayVarName+$indexValue")
@ -114,7 +114,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" lda (P8ZP_SCRATCH_W2)") asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y") asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
@ -143,7 +143,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
when(val value = assign.source.expression!!) { when(val value = assign.source.expression!!) {
is AddressOf -> { is AddressOf -> {
val sourceName = value.identifier.firstStructVarName(program.namespace) ?: asmgen.asmVariableName(value.identifier) val sourceName = value.identifier.firstStructVarName(program) ?: asmgen.asmVariableName(value.identifier)
assignAddressOf(assign.target, sourceName) assignAddressOf(assign.target, sourceName)
} }
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber") is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
@ -152,7 +152,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory") is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value) is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
is FunctionCall -> { is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) { when (val sub = value.target.targetStatement(program)) {
is Subroutine -> { is Subroutine -> {
asmgen.saveXbeforeCall(value) asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value) asmgen.translateFunctionCall(value)
@ -209,7 +209,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
is BuiltinFunctionStatementPlaceholder -> { is BuiltinFunctionStatementPlaceholder -> {
val signature = BuiltinFunctions.getValue(sub.name) val signature = BuiltinFunctions.getValue(sub.name)
asmgen.translateBuiltinFunctionCallExpression(value, signature, false) asmgen.translateBuiltinFunctionCallExpression(value, signature, false, assign.target.register)
if(assign.target.register==null) {
// still need to assign the result to the target variable/etc.
val returntype = builtinFunctionReturnType(sub.name, value.args, program) val returntype = builtinFunctionReturnType(sub.name, value.args, program)
if(!returntype.isKnown) if(!returntype.isKnown)
throw AssemblyError("unknown dt") throw AssemblyError("unknown dt")
@ -239,6 +241,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("weird result type") else -> throw AssemblyError("weird result type")
} }
} }
}
else -> { else -> {
throw AssemblyError("weird func call") throw AssemblyError("weird func call")
} }
@ -260,7 +263,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
} }
SourceStorageKind.REGISTER -> { SourceStorageKind.REGISTER -> {
assignRegisterByte(assign.target, assign.source.register!!) when(assign.source.datatype) {
DataType.UBYTE -> assignRegisterByte(assign.target, assign.source.register!!.asCpuRegister())
DataType.UWORD -> assignRegisterpairWord(assign.target, assign.source.register!!)
else -> throw AssemblyError("invalid register dt")
}
} }
SourceStorageKind.STACK -> { SourceStorageKind.STACK -> {
assignStackValue(assign.target) assignStackValue(assign.target)
@ -312,7 +319,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (asmgen.compTarget.machine.cpu == 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")
@ -466,7 +473,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(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==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")
@ -489,7 +496,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(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==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")
@ -585,7 +592,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(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==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")
@ -613,7 +620,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(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu==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")
@ -755,7 +762,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!! * target.datatype.memorySize() val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.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")
@ -961,7 +968,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!! * target.datatype.memorySize() val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.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")
@ -1184,7 +1191,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!! * target.datatype.memorySize() val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
else { else {
@ -1288,7 +1295,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1297,7 +1304,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1322,7 +1329,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1431,7 +1438,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname} | stx ${target.asmVarname}+1") RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname} | stx ${target.asmVarname}+1")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname} | sty ${target.asmVarname}+1") RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname} | sty ${target.asmVarname}+1")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname} | sty ${target.asmVarname}+1") RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname} | sty ${target.asmVarname}+1")
else -> throw AssemblyError("expected reg pair") in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}
lda $srcReg+1
sta ${target.asmVarname}+1""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
@ -1441,11 +1456,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1") RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
else -> throw AssemblyError("expected reg pair") in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}+$idx
lda $srcReg+1
sta ${target.asmVarname}+$idx+1""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} }
else { else {
when(regs) { if (regs !in Cx16VirtualRegisters) {
when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha") RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha") RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha") RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
@ -1458,6 +1482,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
dey dey
pla pla
sta ${target.asmVarname},y""") sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
lda $srcReg+1
sta ${target.asmVarname},y
dey
lda $srcReg
sta ${target.asmVarname},y""")
}
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
@ -1472,7 +1506,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
stx cx16.${target.register.toString().toLowerCase()}+1 stx cx16.${target.register.toString().toLowerCase()}+1
""") """)
} }
else -> throw AssemblyError("expected reg pair") else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
RegisterOrPair.AY -> when(target.register!!) { RegisterOrPair.AY -> when(target.register!!) {
RegisterOrPair.AY -> { } RegisterOrPair.AY -> { }
@ -1484,7 +1518,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sty cx16.${target.register.toString().toLowerCase()}+1 sty cx16.${target.register.toString().toLowerCase()}+1
""") """)
} }
else -> throw AssemblyError("expected reg pair") else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
RegisterOrPair.XY -> when(target.register!!) { RegisterOrPair.XY -> when(target.register!!) {
RegisterOrPair.AY -> { asmgen.out(" txa") } RegisterOrPair.AY -> { asmgen.out(" txa") }
@ -1496,16 +1530,40 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sty cx16.${target.register.toString().toLowerCase()}+1 sty cx16.${target.register.toString().toLowerCase()}+1
""") """)
} }
else -> throw AssemblyError("expected reg pair") else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
else -> throw AssemblyError("expected reg pair") in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
if(regs!=target.register) {
when(target.register) {
RegisterOrPair.AX -> asmgen.out(" lda $srcReg | ldx $srcReg+1")
RegisterOrPair.AY -> asmgen.out(" lda $srcReg | ldy $srcReg+1")
RegisterOrPair.XY -> asmgen.out(" ldx $srcReg | ldy $srcReg+1")
in Cx16VirtualRegisters -> {
val targetReg = asmgen.asmSymbolName(target.register!!)
asmgen.out(" lda $srcReg | sta $targetReg | lda $srcReg+1 | sta $targetReg+1")
}
else -> throw AssemblyError("invalid reg")
}
}
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
when(regs) { when(regs) {
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex") RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't use X here") RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't use X here")
else -> throw AssemblyError("expected reg pair") in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta P8ESTACK_LO,x
lda $srcReg+1
sta P8ESTACK_HI,x
dex""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} }
TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte") TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte")
@ -1513,7 +1571,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 && CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if(word==0 && asmgen.compTarget.machine.cpu == 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 -> {
@ -1608,7 +1666,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() && CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if(byte==0.toShort() && asmgen.compTarget.machine.cpu == 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 -> {
@ -1678,7 +1736,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1699,7 +1757,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(""" asmgen.out("""
stz ${target.asmVarname} stz ${target.asmVarname}
stz ${target.asmVarname}+1 stz ${target.asmVarname}+1
@ -1719,8 +1777,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if (target.array!!.indexer.indexNum!=null) { if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize() val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT)
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(""" asmgen.out("""
stz ${target.asmVarname}+$indexValue stz ${target.asmVarname}+$indexValue
stz ${target.asmVarname}+$indexValue+1 stz ${target.asmVarname}+$indexValue+1
@ -1784,7 +1842,7 @@ 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) { if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize() val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT)
asmgen.out(""" asmgen.out("""
lda $constFloat lda $constFloat
sta $arrayVarName+$indexValue sta $arrayVarName+$indexValue
@ -1917,7 +1975,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1933,7 +1991,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1945,7 +2003,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1965,7 +2023,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1983,7 +2041,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (asmgen.compTarget.machine.cpu == 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")
@ -1993,7 +2051,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 (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if (asmgen.compTarget.machine.cpu == 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")
@ -2003,9 +2061,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
fun storeAIntoPointerVar(pointervar: IdentifierReference) { fun storeAIntoPointerVar(pointervar: IdentifierReference) {
val sourceName = asmgen.asmVariableName(pointervar) val sourceName = asmgen.asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program.namespace)!! val vardecl = pointervar.targetVarDecl(program)!!
val scopedName = vardecl.makeScopedName(vardecl.name) val scopedName = vardecl.makeScopedName(vardecl.name)
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) { if (asmgen.compTarget.machine.cpu == 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)")

View File

@ -4,13 +4,12 @@ import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex
internal class AugmentableAssignmentAsmGen(private val program: Program, internal class AugmentableAssignmentAsmGen(private val program: Program,
private val assignmentAsmGen: AssignmentAsmGen, private val assignmentAsmGen: AssignmentAsmGen,
@ -202,7 +201,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
with(target.array!!.indexer) { with(target.array!!.indexer) {
when { when {
indexNum!=null -> { indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*target.datatype.memorySize()}" val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*asmgen.compTarget.memorySize(target.datatype)}"
when(target.datatype) { when(target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
when { when {
@ -632,7 +631,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"<<" -> { "<<" -> {
if(value>=8) { if(value>=8) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -643,7 +642,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -858,14 +857,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"<<" -> { "<<" -> {
when { when {
value>=16 -> { value>=16 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -885,14 +884,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if(dt==DataType.UWORD) { if(dt==DataType.UWORD) {
when { when {
value>=16 -> { value>=16 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -941,13 +940,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> { "&", "and" -> {
when { when {
value == 0 -> { value == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -955,7 +954,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -986,7 +985,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) { private fun inplaceModification_word_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) {
val otherName = asmgen.asmVariableName(ident) val otherName = asmgen.asmVariableName(ident)
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype val valueDt = ident.targetVarDecl(program)!!.datatype
when (valueDt) { when (valueDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
// the other variable is a BYTE type so optimize for that // the other variable is a BYTE type so optimize for that
@ -1042,7 +1041,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1093,7 +1092,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1352,7 +1351,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1467,7 +1466,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) { private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) {
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype val valueDt = ident.targetVarDecl(program)!!.datatype
if(valueDt != DataType.FLOAT) if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected") throw AssemblyError("float variable expected")
@ -1475,7 +1474,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
if(CompilationTarget.instance is Cx16Target) { if(asmgen.compTarget is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT() // cx16 doesn't have FPWR() only FPWRT()
asmgen.out(""" asmgen.out("""
lda #<$name lda #<$name
@ -1553,7 +1552,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
if(CompilationTarget.instance is Cx16Target) { if(asmgen.compTarget is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT() // cx16 doesn't have FPWR() only FPWRT()
asmgen.out(""" asmgen.out("""
lda #<$name lda #<$name
@ -1645,7 +1644,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")
@ -1655,7 +1654,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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) if(asmgen.compTarget.machine.cpu == 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")

View File

@ -1,5 +1,6 @@
package prog8.compiler.target.cx16 package prog8.compiler.target.cx16
import prog8.ast.IStringEncoding
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
@ -28,9 +29,15 @@ internal object CX16MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num) override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) { override fun importLibs(
compilerOptions: CompilationOptions,
importer: ModuleImporter,
program: Program,
encoder: IStringEncoding,
compilationTargetName: String)
{
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
importer.importLibraryModule(program, "syslib") importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
} }
override fun launchEmulator(programName: String) { override fun launchEmulator(programName: String) {

View File

@ -1,13 +1,13 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.target.ICompilationTarget
internal class BinExprSplitter(private val program: Program) : AstWalker() { internal class BinExprSplitter(private val program: Program) : AstWalker() {
@ -53,7 +53,7 @@ X = BinExpr X = LeftExpr
*/ */
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) { if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program)) {
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right) if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
return noModifications return noModifications
@ -78,9 +78,9 @@ X = BinExpr X = LeftExpr
private fun isSimpleExpression(expr: Expression) = private fun isSimpleExpression(expr: Expression) =
expr is IdentifierReference || expr is NumericLiteralValue || expr is AddressOf || expr is DirectMemoryRead || expr is StringLiteralValue || expr is ArrayLiteralValue || expr is RangeExpr expr is IdentifierReference || expr is NumericLiteralValue || expr is AddressOf || expr is DirectMemoryRead || expr is StringLiteralValue || expr is ArrayLiteralValue || expr is RangeExpr
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope) = private fun isSimpleTarget(target: AssignTarget, program: Program) =
if (target.identifier!=null || target.memoryAddress!=null) if (target.identifier!=null || target.memoryAddress!=null)
target.isInRegularRAM(namespace) ICompilationTarget.instance.isInRegularRAM(target, program)
else else
false false

View File

@ -1,14 +1,17 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.* import prog8.ast.INameScope
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.ast.base.ErrorReporter 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.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.compiler.loadAsmIncludeFile import prog8.compiler.loadAsmIncludeFile
private val alwaysKeepSubroutines = setOf( private val alwaysKeepSubroutines = setOf(
@ -89,7 +92,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
override fun visit(identifier: IdentifierReference) { override fun visit(identifier: IdentifierReference) {
// track symbol usage // track symbol usage
val target = identifier.targetStatement(this.program.namespace) val target = identifier.targetStatement(program)
if (target != null) { if (target != null) {
addNodeAndParentScopes(target) addNodeAndParentScopes(target)
} }
@ -126,7 +129,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
override fun visit(functionCall: FunctionCall) { override fun visit(functionCall: FunctionCall) {
val otherSub = functionCall.target.targetSubroutine(program.namespace) val otherSub = functionCall.target.targetSubroutine(program)
if (otherSub != null) { if (otherSub != null) {
functionCall.definingSubroutine()?.let { thisSub -> functionCall.definingSubroutine()?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub).plus(otherSub) calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
@ -137,7 +140,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
override fun visit(functionCallStatement: FunctionCallStatement) { override fun visit(functionCallStatement: FunctionCallStatement) {
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace) val otherSub = functionCallStatement.target.targetSubroutine(program)
if (otherSub != null) { if (otherSub != null) {
functionCallStatement.definingSubroutine()?.let { thisSub -> functionCallStatement.definingSubroutine()?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub).plus(otherSub) calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
@ -148,7 +151,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
override fun visit(jump: Jump) { override fun visit(jump: Jump) {
val otherSub = jump.identifier?.targetSubroutine(program.namespace) val otherSub = jump.identifier?.targetSubroutine(program)
if (otherSub != null) { if (otherSub != null) {
jump.definingSubroutine()?.let { thisSub -> jump.definingSubroutine()?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub).plus(otherSub) calls[thisSub] = calls.getValue(thisSub).plus(otherSub)

View File

@ -4,9 +4,12 @@ import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.statements.Assignment
import prog8.ast.processing.IAstModification import prog8.ast.statements.ForLoop
import prog8.ast.statements.* import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.target.ICompilationTarget
import kotlin.math.pow import kotlin.math.pow
@ -221,7 +224,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
range.step range.step
} }
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position) return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, ICompilationTarget.instance, 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.
@ -230,7 +233,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
val rangeTo = iterableRange.to as? NumericLiteralValue val rangeTo = iterableRange.to as? NumericLiteralValue
if(rangeFrom==null || rangeTo==null) return noModifications if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program.namespace) ?: throw UndefinedSymbolError(forLoop.loopVar) val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
val stepLiteral = iterableRange.step as? NumericLiteralValue val stepLiteral = iterableRange.step as? NumericLiteralValue
when(loopvar.datatype) { when(loopvar.datatype) {

View File

@ -4,13 +4,14 @@ import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.ArrayIndex import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.compiler.target.CompilationTarget import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter
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: ErrorReporter) : AstWalker() {
@ -191,7 +192,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 < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE) if (fillvalue < ICompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > ICompilationTarget.instance.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.

View File

@ -2,11 +2,14 @@ package prog8.optimizer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.base.IntegerDatatypes
import prog8.ast.base.NumericDatatypes
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow import kotlin.math.pow

View File

@ -1,7 +1,8 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.ErrorReporter import prog8.compiler.ErrorReporter
internal fun Program.constantFold(errors: ErrorReporter) { internal fun Program.constantFold(errors: ErrorReporter) {
@ -38,8 +39,8 @@ internal fun Program.constantFold(errors: ErrorReporter) {
} }
internal fun Program.optimizeStatements(errors: ErrorReporter): Int { internal fun Program.optimizeStatements(errors: ErrorReporter, functions: IBuiltinFunctions): Int {
val optimizer = StatementOptimizer(this, errors) val optimizer = StatementOptimizer(this, errors, functions)
optimizer.visit(this) optimizer.visit(this)
val optimizationCount = optimizer.applyModifications() val optimizationCount = optimizer.applyModifications()

View File

@ -1,25 +1,27 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.IBuiltinFunctions
import prog8.ast.INameScope import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.target.CompilationTarget import prog8.ast.walk.AstWalker
import prog8.functions.BuiltinFunctions import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.compiler.ErrorReporter
import prog8.compiler.target.ICompilationTarget
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) : AstWalker() { private val errors: ErrorReporter,
private val functions: IBuiltinFunctions
) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
private val callgraph = CallGraph(program) private val callgraph = CallGraph(program)
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
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()) {
@ -70,9 +72,9 @@ internal class StatementOptimizer(private val program: Program,
} }
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) { if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in functions.names) {
val functionName = functionCallStatement.target.nameInSource[0] val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in pureBuiltinFunctions) { if (functionName in functions.purefunctionNames) {
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position) errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope())) return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope()))
} }
@ -89,12 +91,12 @@ internal class StatementOptimizer(private val program: Program,
arg as? IdentifierReference arg as? IdentifierReference
} }
if(stringVar!=null) { if(stringVar!=null) {
val vardecl = stringVar.targetVarDecl(program.namespace)!! val vardecl = stringVar.targetVarDecl(program)!!
val string = vardecl.value as? StringLiteralValue val string = vardecl.value as? StringLiteralValue
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 = CompilationTarget.instance.encodeString(string.value, string.altEncoding)[0] val firstCharEncoded = ICompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
val chrout = FunctionCallStatement( val chrout = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos), IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)), mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
@ -102,7 +104,7 @@ internal class StatementOptimizer(private val program: Program,
) )
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 = CompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding) val firstTwoCharsEncoded = ICompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
val chrout1 = FunctionCallStatement( val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos), IdentifierReference(listOf("c64", "CHROUT"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)), mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
@ -123,7 +125,7 @@ internal class StatementOptimizer(private val program: Program,
} }
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter // if the first instruction in the called subroutine is a return statement, remove the jump altogeter
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace) val subroutine = functionCallStatement.target.targetSubroutine(program)
if(subroutine!=null) { if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull() val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return) if(first is Return)
@ -135,7 +137,7 @@ internal class StatementOptimizer(private val program: Program,
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> { override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
// if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value // if the first instruction in the called subroutine is a return statement with constant value, replace with the constant value
val subroutine = functionCall.target.targetSubroutine(program.namespace) val subroutine = functionCall.target.targetSubroutine(program)
if(subroutine!=null) { if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull() val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Return && first.value!=null) { if(first is Return && first.value!=null) {
@ -203,14 +205,14 @@ internal class StatementOptimizer(private val program: Program,
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
} }
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program.namespace) val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
if(iterable!=null) { if(iterable!=null) {
if(iterable.datatype==DataType.STR) { if(iterable.datatype==DataType.STR) {
val sv = iterable.value as StringLiteralValue val sv = iterable.value as StringLiteralValue
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 = CompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0] val character = ICompilationTarget.instance.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))
@ -306,7 +308,7 @@ internal class StatementOptimizer(private val program: Program,
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()
val label = jump.identifier?.targetStatement(scope) val label = jump.identifier?.targetStatement(program)
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1) if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
return listOf(IAstModification.Remove(jump, jump.definingScope())) return listOf(IAstModification.Remove(jump, jump.definingScope()))
@ -390,7 +392,7 @@ internal class StatementOptimizer(private val program: Program,
// assignments of the form: X = X <operator> <expr> // assignments of the form: X = X <operator> <expr>
// remove assignments that have no effect (such as X=X+0) // remove assignments that have no effect (such as X=X+0)
// optimize/rewrite some other expressions // optimize/rewrite some other expressions
val vardeclDt = (assignment.target.identifier?.targetVarDecl(program.namespace))?.type val vardeclDt = (assignment.target.identifier?.targetVarDecl(program))?.type
when (bexpr.operator) { when (bexpr.operator) {
"+" -> { "+" -> {
if (rightCv == 0.0) { if (rightCv == 0.0) {

View File

@ -3,11 +3,15 @@ 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.ast.base.ErrorReporter import prog8.compiler.ErrorReporter
import prog8.ast.expressions.* import prog8.ast.expressions.BinaryExpression
import prog8.ast.processing.AstWalker import prog8.ast.expressions.FunctionCall
import prog8.ast.processing.IAstModification import prog8.ast.expressions.PrefixExpression
import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.target.ICompilationTarget
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() { internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() {
@ -92,7 +96,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) && assign1.target.isInRegularRAM(program.namespace)) { if (assign1.target.isSameAs(assign2.target, program) && ICompilationTarget.instance.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) {

View File

@ -5,12 +5,15 @@ 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.Module
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.toHex
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
@ -18,6 +21,7 @@ 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
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.file.Path
import kotlin.test.* import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ -50,8 +54,8 @@ class TestCompiler {
assertEquals("-\$c382", (-50050).toHex()) assertEquals("-\$c382", (-50050).toHex())
assertEquals("-\$ffff", (-65535).toHex()) assertEquals("-\$ffff", (-65535).toHex())
assertEquals("-\$ffff", (-65535L).toHex()) assertEquals("-\$ffff", (-65535L).toHex())
assertFailsWith<CompilerException> { 65536.toHex() } assertFailsWith<IllegalArgumentException> { 65536.toHex() }
assertFailsWith<CompilerException> { 65536L.toHex() } assertFailsWith<IllegalArgumentException> { 65536L.toHex() }
} }
@Test @Test
@ -401,74 +405,73 @@ class TestPetscii {
class TestMemory { class TestMemory {
private class DummyFunctions: IBuiltinFunctions {
override val names: Set<String> = emptySet()
override val purefunctionNames: Set<String> = emptySet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? = null
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
}
@Test @Test
fun testInValidRamC64_memory_addresses() { fun testInValidRamC64_memory_addresses() {
CompilationTarget.instance = C64Target
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)
var scope = AnonymousScope(mutableListOf(), Position.DUMMY) val program = Program("test", mutableListOf(), DummyFunctions())
assertTrue(target.isInRegularRAM(scope)) assertTrue(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAM(scope))
} }
@Test @Test
fun testNotInValidRamC64_memory_addresses() { fun testNotInValidRamC64_memory_addresses() {
CompilationTarget.instance = C64Target
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)
var scope = AnonymousScope(mutableListOf(), Position.DUMMY) val program = Program("test", mutableListOf(), DummyFunctions())
assertFalse(target.isInRegularRAM(scope)) assertFalse(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAM(scope))
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
scope = AnonymousScope(mutableListOf(), Position.DUMMY) assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAM(scope))
} }
@Test @Test
fun testInValidRamC64_memory_identifiers() { fun testInValidRamC64_memory_identifiers() {
CompilationTarget.instance = C64Target
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR) var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
assertTrue(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(), DummyFunctions())
assertTrue(C64Target.isInRegularRAM(target, program))
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR) target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
assertFalse(target.isInRegularRAM(target.definingScope())) assertFalse(C64Target.isInRegularRAM(target, program))
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.CONST) target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.CONST)
assertTrue(target.isInRegularRAM(target.definingScope())) assertTrue(C64Target.isInRegularRAM(target, program))
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.CONST) target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.CONST)
assertFalse(target.isInRegularRAM(target.definingScope())) assertFalse(C64Target.isInRegularRAM(target, program))
target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.MEMORY) target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.MEMORY)
assertFalse(target.isInRegularRAM(target.definingScope())) assertFalse(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
@ -478,89 +481,95 @@ class TestMemory {
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
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)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
module.linkParents(ParentSentinel)
return target return target
} }
@Test @Test
fun testInValidRamC64_memory_expression() { fun testInValidRamC64_memory_expression() {
CompilationTarget.instance = C64Target
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 scope = AnonymousScope(mutableListOf(), Position.DUMMY) val program = Program("test", mutableListOf(), DummyFunctions())
assertFalse(target.isInRegularRAM(scope)) assertFalse(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testInValidRamC64_variable() { fun testInValidRamC64_variable() {
CompilationTarget.instance = C64Target
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertTrue(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testInValidRamC64_memmap_variable() { fun testInValidRamC64_memmap_variable() {
CompilationTarget.instance = C64Target
val address = 0x1000 val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertTrue(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testNotInValidRamC64_memmap_variable() { fun testNotInValidRamC64_memmap_variable() {
CompilationTarget.instance = C64Target
val address = 0xd020 val address = 0xd020
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY) val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertFalse(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertFalse(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testInValidRamC64_array() { fun testInValidRamC64_array() {
CompilationTarget.instance = C64Target
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertTrue(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testInValidRamC64_array_memmapped() { fun testInValidRamC64_array_memmapped() {
CompilationTarget.instance = C64Target
val address = 0x1000 val address = 0x1000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertTrue(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program))
} }
@Test @Test
fun testNotValidRamC64_array_memmapped() { fun testNotValidRamC64_array_memmapped() {
CompilationTarget.instance = C64Target
val address = 0xe000 val address = 0xe000
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY) val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY) val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY) val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
subroutine.linkParents(ParentSentinel) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
assertFalse(target.isInRegularRAM(target.definingScope())) val program = Program("test", mutableListOf(module), DummyFunctions())
module.linkParents(ParentSentinel)
assertFalse(C64Target.isInRegularRAM(target, program))
} }
} }

65
compilerAst/build.gradle Normal file
View File

@ -0,0 +1,65 @@
plugins {
id 'antlr'
id 'java'
id "org.jetbrains.kotlin.jvm" version "1.4.30"
}
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenCentral()
}
configurations {
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
// this avoids linking in the complete antlr binary jar
compile {
extendsFrom = extendsFrom.findAll { it != configurations.antlr }
}
}
dependencies {
antlr 'org.antlr:antlr4:4.9'
implementation 'org.antlr:antlr4-runtime:4.9'
implementation project(':parser')
// antlr('org.antlr:antlr4:4.9') {
// exclude group: 'com.ibm.icu', module: 'icu4j'
// }
}
compileKotlin {
kotlinOptions {
jvmTarget = "11"
// verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "11"
}
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
test {
java {
srcDirs = ["${project.projectDir}/test"]
}
}
}
task wrapper(type: Wrapper) {
gradleVersion = '6.7'
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="parser" />
<orderEntry type="library" name="antlr-runtime-4.9" level="project" />
</component>
</module>

View File

@ -5,9 +5,9 @@ import prog8.ast.base.DataType
import prog8.ast.base.NumericDatatypes import prog8.ast.base.NumericDatatypes
import prog8.ast.base.VarDeclType import prog8.ast.base.VarDeclType
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.toHex import prog8.ast.walk.IAstVisitor
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor { class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
private var scopelevel = 0 private var scopelevel = 0
@ -413,10 +413,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
outputlni("}}") outputlni("}}")
} }
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
output(builtinFunctionStatementPlaceholder.name)
}
override fun visit(whenStatement: WhenStatement) { override fun visit(whenStatement: WhenStatement) {
output("when ") output("when ")
whenStatement.condition.accept(this) whenStatement.condition.accept(this)

View File

@ -3,12 +3,18 @@ package prog8.ast
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.processing.AstWalker import prog8.ast.expressions.InferredTypes
import prog8.ast.processing.IAstVisitor import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
import java.nio.file.Path import java.nio.file.Path
import kotlin.math.abs
interface IStringEncoding {
fun encodeString(str: String, altEncoding: Boolean): List<Short>
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
}
interface Node { interface Node {
val position: Position val position: Position
@ -238,11 +244,19 @@ interface IAssignable {
// just a tag for now // just a tag for now
} }
interface IBuiltinFunctions {
val names: Set<String>
val purefunctionNames: Set<String>
fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue?
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
}
/*********** Everything starts from here, the Program; zero or more modules *************/ /*********** Everything starts from here, the Program; zero or more modules *************/
class Program(val name: String, val modules: MutableList<Module>): Node {
val namespace = GlobalNamespace(modules)
class Program(val name: String, val modules: MutableList<Module>, val builtinFunctions: IBuiltinFunctions): Node {
val namespace = GlobalNamespace(modules, builtinFunctions.names)
val definedLoadAddress: Int val definedLoadAddress: Int
get() = modules.first().loadAddress get() = modules.first().loadAddress
@ -317,7 +331,7 @@ class Module(override val name: String,
} }
class GlobalNamespace(val modules: List<Module>): Node, INameScope { class GlobalNamespace(val modules: List<Module>, private val builtinFunctionNames: Set<String>): Node, INameScope {
override val name = "<<<global>>>" override val name = "<<<global>>>"
override val position = Position("<<<global>>>", 0, 0, 0) override val position = Position("<<<global>>>", 0, 0, 0)
override val statements = mutableListOf<Statement>() // not used override val statements = mutableListOf<Statement>() // not used
@ -332,7 +346,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
} }
override fun lookup(scopedName: List<String>, localContext: Node): Statement? { override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) { if (scopedName.size == 1 && scopedName[0] in builtinFunctionNames) {
// builtin functions always exist, return a dummy localContext for them // builtin functions always exist, return a dummy localContext for them
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position) val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
builtinPlaceholder.parent = ParentSentinel builtinPlaceholder.parent = ParentSentinel
@ -379,4 +393,21 @@ object BuiltinFunctionScopePlaceholder : INameScope {
// prefix for struct member variables // prefix for struct member variables
internal fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName" fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"
fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
return '-' + abs(integer).toHex()
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
}
}

View File

@ -3,11 +3,11 @@ package prog8.ast.antlr
import org.antlr.v4.runtime.IntStream import org.antlr.v4.runtime.IntStream
import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.TerminalNode import org.antlr.v4.runtime.tree.TerminalNode
import prog8.ast.IStringEncoding
import prog8.ast.Module import prog8.ast.Module
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.target.CompilationTarget
import prog8.parser.CustomLexer import prog8.parser.CustomLexer
import prog8.parser.prog8Parser import prog8.parser.prog8Parser
import java.io.CharConversionException import java.io.CharConversionException
@ -19,10 +19,10 @@ import java.nio.file.Path
private data class NumericLiteral(val number: Number, val datatype: DataType) private data class NumericLiteral(val number: Number, val datatype: DataType)
internal fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module { internal fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path, encoding: IStringEncoding) : Module {
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
val directives = this.directive().map { it.toAst() } val directives = this.directive().map { it.toAst() }
val blocks = this.block().map { it.toAst(isLibrary) } val blocks = this.block().map { it.toAst(isLibrary, encoding) }
return Module(nameWithoutSuffix, (directives + blocks).toMutableList(), toPosition(), isLibrary, source) return Module(nameWithoutSuffix, (directives + blocks).toMutableList(), toPosition(), isLibrary, source)
} }
@ -38,11 +38,11 @@ private fun ParserRuleContext.toPosition() : Position {
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length) return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
} }
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement { private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean, encoding: IStringEncoding) : Statement {
val blockstatements = block_statement().map { val blockstatements = block_statement().map {
when { when {
it.variabledeclaration()!=null -> it.variabledeclaration().toAst() it.variabledeclaration()!=null -> it.variabledeclaration().toAst(encoding)
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst() it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst(encoding)
it.directive()!=null -> it.directive().toAst() it.directive()!=null -> it.directive().toAst()
it.inlineasm()!=null -> it.inlineasm().toAst() it.inlineasm()!=null -> it.inlineasm().toAst()
else -> throw FatalAstException("weird block statement $it") else -> throw FatalAstException("weird block statement $it")
@ -51,11 +51,11 @@ private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement {
return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition()) return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
} }
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<Statement> = private fun prog8Parser.Statement_blockContext.toAst(encoding: IStringEncoding): MutableList<Statement> =
statement().asSequence().map { it.toAst() }.toMutableList() statement().asSequence().map { it.toAst(encoding) }.toMutableList()
private fun prog8Parser.VariabledeclarationContext.toAst() : Statement { private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncoding) : Statement {
vardecl()?.let { return it.toAst() } vardecl()?.let { return it.toAst(encoding) }
varinitializer()?.let { varinitializer()?.let {
val vd = it.vardecl() val vd = it.vardecl()
@ -63,10 +63,10 @@ private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
VarDeclType.VAR, VarDeclType.VAR,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE, if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(encoding),
vd.varname.text, vd.varname.text,
null, null,
it.expression().toAst(), it.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null, vd.ARRAYSIG() != null || vd.arrayindex() != null,
false, false,
it.toPosition() it.toPosition()
@ -82,7 +82,7 @@ private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
null, null,
vd.varname.text, vd.varname.text,
vd.structname.text, vd.structname.text,
it.expression().toAst(), it.expression().toAst(encoding),
isArray = false, isArray = false,
autogeneratedDontRemove = false, autogeneratedDontRemove = false,
position = it.toPosition() position = it.toPosition()
@ -111,10 +111,10 @@ private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
VarDeclType.CONST, VarDeclType.CONST,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE, if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(encoding),
vd.varname.text, vd.varname.text,
null, null,
cvarinit.expression().toAst(), cvarinit.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null, vd.ARRAYSIG() != null || vd.arrayindex() != null,
false, false,
cvarinit.toPosition() cvarinit.toPosition()
@ -128,10 +128,10 @@ private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
VarDeclType.MEMORY, VarDeclType.MEMORY,
vd.datatype()?.toAst() ?: DataType.STRUCT, vd.datatype()?.toAst() ?: DataType.STRUCT,
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE, if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
vd.arrayindex()?.toAst(), vd.arrayindex()?.toAst(encoding),
vd.varname.text, vd.varname.text,
null, null,
mvarinit.expression().toAst(), mvarinit.expression().toAst(encoding),
vd.ARRAYSIG() != null || vd.arrayindex() != null, vd.ARRAYSIG() != null || vd.arrayindex() != null,
false, false,
mvarinit.toPosition() mvarinit.toPosition()
@ -140,40 +140,40 @@ private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
structdecl()?.let { structdecl()?.let {
return StructDecl(it.identifier().text, return StructDecl(it.identifier().text,
it.vardecl().map { vd->vd.toAst() }.toMutableList(), it.vardecl().map { vd->vd.toAst(encoding) }.toMutableList(),
toPosition()) toPosition())
} }
throw FatalAstException("weird variable decl $this") throw FatalAstException("weird variable decl $this")
} }
private fun prog8Parser.SubroutinedeclarationContext.toAst() : Subroutine { private fun prog8Parser.SubroutinedeclarationContext.toAst(encoding: IStringEncoding) : Subroutine {
return when { return when {
subroutine()!=null -> subroutine().toAst() subroutine()!=null -> subroutine().toAst(encoding)
asmsubroutine()!=null -> asmsubroutine().toAst() asmsubroutine()!=null -> asmsubroutine().toAst(encoding)
romsubroutine()!=null -> romsubroutine().toAst() romsubroutine()!=null -> romsubroutine().toAst()
else -> throw FatalAstException("weird subroutine decl $this") else -> throw FatalAstException("weird subroutine decl $this")
} }
} }
private fun prog8Parser.StatementContext.toAst() : Statement { private fun prog8Parser.StatementContext.toAst(encoding: IStringEncoding) : Statement {
val vardecl = variabledeclaration()?.toAst() val vardecl = variabledeclaration()?.toAst(encoding)
if(vardecl!=null) return vardecl if(vardecl!=null) return vardecl
assignment()?.let { assignment()?.let {
return Assignment(it.assign_target().toAst(), it.expression().toAst(), it.toPosition()) return Assignment(it.assign_target().toAst(encoding), it.expression().toAst(encoding), it.toPosition())
} }
augassignment()?.let { augassignment()?.let {
// replace A += X with A = A + X // replace A += X with A = A + X
val target = it.assign_target().toAst() val target = it.assign_target().toAst(encoding)
val oper = it.operator.text.substringBefore('=') val oper = it.operator.text.substringBefore('=')
val expression = BinaryExpression(target.toExpression(), oper, it.expression().toAst(), it.expression().toPosition()) val expression = BinaryExpression(target.toExpression(), oper, it.expression().toAst(encoding), it.expression().toPosition())
return Assignment(it.assign_target().toAst(), expression, it.toPosition()) return Assignment(it.assign_target().toAst(encoding), expression, it.toPosition())
} }
postincrdecr()?.let { postincrdecr()?.let {
return PostIncrDecr(it.assign_target().toAst(), it.operator.text, it.toPosition()) return PostIncrDecr(it.assign_target().toAst(encoding), it.operator.text, it.toPosition())
} }
val directive = directive()?.toAst() val directive = directive()?.toAst()
@ -185,49 +185,49 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
val jump = unconditionaljump()?.toAst() val jump = unconditionaljump()?.toAst()
if(jump!=null) return jump if(jump!=null) return jump
val fcall = functioncall_stmt()?.toAst() val fcall = functioncall_stmt()?.toAst(encoding)
if(fcall!=null) return fcall if(fcall!=null) return fcall
val ifstmt = if_stmt()?.toAst() val ifstmt = if_stmt()?.toAst(encoding)
if(ifstmt!=null) return ifstmt if(ifstmt!=null) return ifstmt
val returnstmt = returnstmt()?.toAst() val returnstmt = returnstmt()?.toAst(encoding)
if(returnstmt!=null) return returnstmt if(returnstmt!=null) return returnstmt
val subroutine = subroutinedeclaration()?.toAst() val subroutine = subroutinedeclaration()?.toAst(encoding)
if(subroutine!=null) return subroutine if(subroutine!=null) return subroutine
val asm = inlineasm()?.toAst() val asm = inlineasm()?.toAst()
if(asm!=null) return asm if(asm!=null) return asm
val branchstmt = branch_stmt()?.toAst() val branchstmt = branch_stmt()?.toAst(encoding)
if(branchstmt!=null) return branchstmt if(branchstmt!=null) return branchstmt
val forloop = forloop()?.toAst() val forloop = forloop()?.toAst(encoding)
if(forloop!=null) return forloop if(forloop!=null) return forloop
val untilloop = untilloop()?.toAst() val untilloop = untilloop()?.toAst(encoding)
if(untilloop!=null) return untilloop if(untilloop!=null) return untilloop
val whileloop = whileloop()?.toAst() val whileloop = whileloop()?.toAst(encoding)
if(whileloop!=null) return whileloop if(whileloop!=null) return whileloop
val repeatloop = repeatloop()?.toAst() val repeatloop = repeatloop()?.toAst(encoding)
if(repeatloop!=null) return repeatloop if(repeatloop!=null) return repeatloop
val breakstmt = breakstmt()?.toAst() val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt if(breakstmt!=null) return breakstmt
val whenstmt = whenstmt()?.toAst() val whenstmt = whenstmt()?.toAst(encoding)
if(whenstmt!=null) return whenstmt if(whenstmt!=null) return whenstmt
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text") throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
} }
private fun prog8Parser.AsmsubroutineContext.toAst(): Subroutine { private fun prog8Parser.AsmsubroutineContext.toAst(encoding: IStringEncoding): Subroutine {
val inline = this.inline()!=null val inline = this.inline()!=null
val subdecl = asmsub_decl().toAst() val subdecl = asmsub_decl().toAst()
val statements = statement_block()?.toAst() ?: mutableListOf() val statements = statement_block()?.toAst(encoding) ?: mutableListOf()
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes, return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters, subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
subdecl.asmClobbers, null, true, inline, statements, toPosition()) subdecl.asmClobbers, null, true, inline, statements, toPosition())
@ -308,28 +308,28 @@ private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParamete
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, toPosition()) AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, toPosition())
} }
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement { private fun prog8Parser.Functioncall_stmtContext.toAst(encoding: IStringEncoding): Statement {
val void = this.VOID() != null val void = this.VOID() != null
val location = scoped_identifier().toAst() val location = scoped_identifier().toAst()
return if(expression_list() == null) return if(expression_list() == null)
FunctionCallStatement(location, mutableListOf(), void, toPosition()) FunctionCallStatement(location, mutableListOf(), void, toPosition())
else else
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition()) FunctionCallStatement(location, expression_list().toAst(encoding).toMutableList(), void, toPosition())
} }
private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall { private fun prog8Parser.FunctioncallContext.toAst(encoding: IStringEncoding): FunctionCall {
val location = scoped_identifier().toAst() val location = scoped_identifier().toAst()
return if(expression_list() == null) return if(expression_list() == null)
FunctionCall(location, mutableListOf(), toPosition()) FunctionCall(location, mutableListOf(), toPosition())
else else
FunctionCall(location, expression_list().toAst().toMutableList(), toPosition()) FunctionCall(location, expression_list().toAst(encoding).toMutableList(), toPosition())
} }
private fun prog8Parser.InlineasmContext.toAst() = private fun prog8Parser.InlineasmContext.toAst() =
InlineAssembly(INLINEASMBLOCK().text, toPosition()) InlineAssembly(INLINEASMBLOCK().text, toPosition())
private fun prog8Parser.ReturnstmtContext.toAst() : Return { private fun prog8Parser.ReturnstmtContext.toAst(encoding: IStringEncoding) : Return {
return Return(expression()?.toAst(), toPosition()) return Return(expression()?.toAst(encoding), toPosition())
} }
private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump { private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
@ -341,14 +341,14 @@ private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
private fun prog8Parser.LabeldefContext.toAst(): Statement = private fun prog8Parser.LabeldefContext.toAst(): Statement =
Label(children[0].text, toPosition()) Label(children[0].text, toPosition())
private fun prog8Parser.SubroutineContext.toAst() : Subroutine { private fun prog8Parser.SubroutineContext.toAst(encoding: IStringEncoding) : Subroutine {
// non-asm subroutine // non-asm subroutine
val inline = inline()!=null val inline = inline()!=null
val returntypes = sub_return_part()?.toAst() ?: emptyList() val returntypes = sub_return_part()?.toAst() ?: emptyList()
return Subroutine(identifier().text, return Subroutine(identifier().text,
sub_params()?.toAst() ?: emptyList(), sub_params()?.toAst() ?: emptyList(),
returntypes, returntypes,
statement_block()?.toAst() ?: mutableListOf(), statement_block()?.toAst(encoding) ?: mutableListOf(),
inline, inline,
toPosition()) toPosition())
} }
@ -364,12 +364,12 @@ private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
SubroutineParameter(it.varname.text, datatype, it.toPosition()) SubroutineParameter(it.varname.text, datatype, it.toPosition())
} }
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget { private fun prog8Parser.Assign_targetContext.toAst(encoding: IStringEncoding) : AssignTarget {
val identifier = scoped_identifier() val identifier = scoped_identifier()
return when { return when {
identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition()) identifier!=null -> AssignTarget(identifier.toAst(), null, null, toPosition())
arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(), null, toPosition()) arrayindexed()!=null -> AssignTarget(null, arrayindexed().toAst(encoding), null, toPosition())
directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition()) directmemory()!=null -> AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(encoding), toPosition()), toPosition())
else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition()) else -> AssignTarget(scoped_identifier()?.toAst(), null, null, toPosition())
} }
} }
@ -381,8 +381,8 @@ private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase()) private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex = private fun prog8Parser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
ArrayIndex(expression().toAst(), toPosition()) ArrayIndex(expression().toAst(encoding), toPosition())
private fun prog8Parser.DirectiveContext.toAst() : Directive = private fun prog8Parser.DirectiveContext.toAst() : Directive =
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition()) Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
@ -446,7 +446,7 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
} }
} }
private fun prog8Parser.ExpressionContext.toAst() : Expression { private fun prog8Parser.ExpressionContext.toAst(encoding: IStringEncoding) : Expression {
val litval = literalvalue() val litval = literalvalue()
if(litval!=null) { if(litval!=null) {
@ -469,7 +469,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
litval.stringliteral()!=null -> litval.stringliteral().toAst() litval.stringliteral()!=null -> litval.stringliteral().toAst()
litval.charliteral()!=null -> { litval.charliteral()!=null -> {
try { try {
NumericLiteralValue(DataType.UBYTE, CompilationTarget.instance.encodeString( NumericLiteralValue(DataType.UBYTE, encoding.encodeString(
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()), unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition()) litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
} catch (ce: CharConversionException) { } catch (ce: CharConversionException) {
@ -477,7 +477,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
} }
} }
litval.arrayliteral()!=null -> { litval.arrayliteral()!=null -> {
val array = litval.arrayliteral().toAst() val array = litval.arrayliteral().toAst(encoding)
// the actual type of the arraysize can not yet be determined here (missing namespace & heap) // the actual type of the arraysize can not yet be determined here (missing namespace & heap)
// the ConstantFold takes care of that and converts the type if needed. // the ConstantFold takes care of that and converts the type if needed.
ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition()) ArrayLiteralValue(InferredTypes.InferredType.unknown(), array, position = litval.toPosition())
@ -491,31 +491,31 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
return scoped_identifier().toAst() return scoped_identifier().toAst()
if(bop!=null) if(bop!=null)
return BinaryExpression(left.toAst(), bop.text, right.toAst(), toPosition()) return BinaryExpression(left.toAst(encoding), bop.text, right.toAst(encoding), toPosition())
if(prefix!=null) if(prefix!=null)
return PrefixExpression(prefix.text, expression(0).toAst(), toPosition()) return PrefixExpression(prefix.text, expression(0).toAst(encoding), toPosition())
val funcall = functioncall()?.toAst() val funcall = functioncall()?.toAst(encoding)
if(funcall!=null) return funcall if(funcall!=null) return funcall
if (rangefrom!=null && rangeto!=null) { if (rangefrom!=null && rangeto!=null) {
val defaultstep = if(rto.text == "to") 1 else -1 val defaultstep = if(rto.text == "to") 1 else -1
val step = rangestep?.toAst() ?: NumericLiteralValue(DataType.UBYTE, defaultstep, toPosition()) val step = rangestep?.toAst(encoding) ?: NumericLiteralValue(DataType.UBYTE, defaultstep, toPosition())
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition()) return RangeExpr(rangefrom.toAst(encoding), rangeto.toAst(encoding), step, encoding, toPosition())
} }
if(childCount==3 && children[0].text=="(" && children[2].text==")") if(childCount==3 && children[0].text=="(" && children[2].text==")")
return expression(0).toAst() // expression within ( ) return expression(0).toAst(encoding) // expression within ( )
if(arrayindexed()!=null) if(arrayindexed()!=null)
return arrayindexed().toAst() return arrayindexed().toAst(encoding)
if(typecast()!=null) if(typecast()!=null)
return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition()) return TypecastExpression(expression(0).toAst(encoding), typecast().datatype().toAst(), false, toPosition())
if(directmemory()!=null) if(directmemory()!=null)
return DirectMemoryRead(directmemory().expression().toAst(), toPosition()) return DirectMemoryRead(directmemory().expression().toAst(encoding), toPosition())
if(addressof()!=null) if(addressof()!=null)
return AddressOf(addressof().scoped_identifier().toAst(), toPosition()) return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
@ -526,13 +526,13 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
private fun prog8Parser.StringliteralContext.toAst(): StringLiteralValue = private fun prog8Parser.StringliteralContext.toAst(): StringLiteralValue =
StringLiteralValue(unescape(this.STRING().text, toPosition()), ALT_STRING_ENCODING()!=null, toPosition()) StringLiteralValue(unescape(this.STRING().text, toPosition()), ALT_STRING_ENCODING()!=null, toPosition())
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression { private fun prog8Parser.ArrayindexedContext.toAst(encoding: IStringEncoding): ArrayIndexedExpression {
return ArrayIndexedExpression(scoped_identifier().toAst(), return ArrayIndexedExpression(scoped_identifier().toAst(),
arrayindex().toAst(), arrayindex().toAst(encoding),
toPosition()) toPosition())
} }
private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() } private fun prog8Parser.Expression_listContext.toAst(encoding: IStringEncoding) = expression().map{ it.toAst(encoding) }
private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference = private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
IdentifierReference(listOf(text), toPosition()) IdentifierReference(listOf(text), toPosition())
@ -548,27 +548,27 @@ private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
else -> throw FatalAstException(text) else -> throw FatalAstException(text)
} }
private fun prog8Parser.ArrayliteralContext.toAst() : Array<Expression> = private fun prog8Parser.ArrayliteralContext.toAst(encoding: IStringEncoding) : Array<Expression> =
expression().map { it.toAst() }.toTypedArray() expression().map { it.toAst(encoding) }.toTypedArray()
private fun prog8Parser.If_stmtContext.toAst(): IfStatement { private fun prog8Parser.If_stmtContext.toAst(encoding: IStringEncoding): IfStatement {
val condition = expression().toAst() val condition = expression().toAst(encoding)
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val trueStatements = statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
val elseStatements = else_part()?.toAst() ?: mutableListOf() val elseStatements = else_part()?.toAst(encoding) ?: mutableListOf()
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition()) val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
return IfStatement(condition, trueScope, elseScope, toPosition()) return IfStatement(condition, trueScope, elseScope, toPosition())
} }
private fun prog8Parser.Else_partContext.toAst(): MutableList<Statement> { private fun prog8Parser.Else_partContext.toAst(encoding: IStringEncoding): MutableList<Statement> {
return statement_block()?.toAst() ?: mutableListOf(statement().toAst()) return statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
} }
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement { private fun prog8Parser.Branch_stmtContext.toAst(encoding: IStringEncoding): BranchStatement {
val branchcondition = branchcondition().toAst() val branchcondition = branchcondition().toAst()
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val trueStatements = statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
val elseStatements = else_part()?.toAst() ?: mutableListOf() val elseStatements = else_part()?.toAst(encoding) ?: mutableListOf()
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition() val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition()) val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
@ -577,65 +577,65 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase()) private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
private fun prog8Parser.ForloopContext.toAst(): ForLoop { private fun prog8Parser.ForloopContext.toAst(encoding: IStringEncoding): ForLoop {
val loopvar = identifier().toAst() val loopvar = identifier().toAst()
val iterable = expression()!!.toAst() val iterable = expression()!!.toAst(encoding)
val scope = val scope =
if(statement()!=null) if(statement()!=null)
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition()) AnonymousScope(mutableListOf(statement().toAst(encoding)), statement().toPosition())
else else
AnonymousScope(statement_block().toAst(), statement_block().toPosition()) AnonymousScope(statement_block().toAst(encoding), statement_block().toPosition())
return ForLoop(loopvar, iterable, scope, toPosition()) return ForLoop(loopvar, iterable, scope, toPosition())
} }
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition()) private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop { private fun prog8Parser.WhileloopContext.toAst(encoding: IStringEncoding): WhileLoop {
val condition = expression().toAst() val condition = expression().toAst(encoding)
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val statements = statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
val scope = AnonymousScope(statements, statement_block()?.toPosition() val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
return WhileLoop(condition, scope, toPosition()) return WhileLoop(condition, scope, toPosition())
} }
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop { private fun prog8Parser.RepeatloopContext.toAst(encoding: IStringEncoding): RepeatLoop {
val iterations = expression()?.toAst() val iterations = expression()?.toAst(encoding)
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val statements = statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
val scope = AnonymousScope(statements, statement_block()?.toPosition() val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
return RepeatLoop(iterations, scope, toPosition()) return RepeatLoop(iterations, scope, toPosition())
} }
private fun prog8Parser.UntilloopContext.toAst(): UntilLoop { private fun prog8Parser.UntilloopContext.toAst(encoding: IStringEncoding): UntilLoop {
val untilCondition = expression().toAst() val untilCondition = expression().toAst(encoding)
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst()) val statements = statement_block()?.toAst(encoding) ?: mutableListOf(statement().toAst(encoding))
val scope = AnonymousScope(statements, statement_block()?.toPosition() val scope = AnonymousScope(statements, statement_block()?.toPosition()
?: statement().toPosition()) ?: statement().toPosition())
return UntilLoop(scope, untilCondition, toPosition()) return UntilLoop(scope, untilCondition, toPosition())
} }
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement { private fun prog8Parser.WhenstmtContext.toAst(encoding: IStringEncoding): WhenStatement {
val condition = expression().toAst() val condition = expression().toAst(encoding)
val choices = this.when_choice()?.map { it.toAst() }?.toMutableList() ?: mutableListOf() val choices = this.when_choice()?.map { it.toAst(encoding) }?.toMutableList() ?: mutableListOf()
return WhenStatement(condition, choices, toPosition()) return WhenStatement(condition, choices, toPosition())
} }
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice { private fun prog8Parser.When_choiceContext.toAst(encoding: IStringEncoding): WhenChoice {
val values = expression_list()?.toAst() val values = expression_list()?.toAst(encoding)
val stmt = statement()?.toAst() val stmt = statement()?.toAst(encoding)
val stmtBlock = statement_block()?.toAst()?.toMutableList() ?: mutableListOf() val stmtBlock = statement_block()?.toAst(encoding)?.toMutableList() ?: mutableListOf()
if(stmt!=null) if(stmt!=null)
stmtBlock.add(stmt) stmtBlock.add(stmt)
val scope = AnonymousScope(stmtBlock, toPosition()) val scope = AnonymousScope(stmtBlock, toPosition())
return WhenChoice(values?.toMutableList(), scope, toPosition()) return WhenChoice(values?.toMutableList(), scope, toPosition())
} }
private fun prog8Parser.VardeclContext.toAst(): VarDecl { private fun prog8Parser.VardeclContext.toAst(encoding: IStringEncoding): VarDecl {
return VarDecl( return VarDecl(
VarDeclType.VAR, VarDeclType.VAR,
datatype()?.toAst() ?: DataType.STRUCT, datatype()?.toAst() ?: DataType.STRUCT,
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE, if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
arrayindex()?.toAst(), arrayindex()?.toAst(encoding),
varname.text, varname.text,
null, null,
null, null,
@ -644,48 +644,3 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
toPosition() toPosition()
) )
} }
internal fun escape(str: String): String {
val es = str.map {
when(it) {
'\t' -> "\\t"
'\n' -> "\\n"
'\r' -> "\\r"
'"' -> "\\\""
in '\u8000'..'\u80ff' -> "\\x" + (it.toInt() - 0x8000).toString(16).padStart(2, '0')
in '\u0000'..'\u00ff' -> it.toString()
else -> "\\u" + it.toInt().toString(16).padStart(4, '0')
}
}
return es.joinToString("")
}
internal fun unescape(str: String, position: Position): String {
val result = mutableListOf<Char>()
val iter = str.iterator()
while(iter.hasNext()) {
val c = iter.nextChar()
if(c=='\\') {
val ec = iter.nextChar()
result.add(when(ec) {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
'"' -> '"'
'\'' -> '\''
'u' -> {
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
}
'x' -> {
// special hack 0x8000..0x80ff will be outputted verbatim without encoding
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
(0x8000 + hex).toChar()
}
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
})
} else {
result.add(c)
}
}
return result.joinToString("")
}

View File

@ -0,0 +1,49 @@
package prog8.ast.antlr
import prog8.ast.base.Position
import prog8.ast.base.SyntaxError
fun escape(str: String): String {
val es = str.map {
when(it) {
'\t' -> "\\t"
'\n' -> "\\n"
'\r' -> "\\r"
'"' -> "\\\""
in '\u8000'..'\u80ff' -> "\\x" + (it.toInt() - 0x8000).toString(16).padStart(2, '0')
in '\u0000'..'\u00ff' -> it.toString()
else -> "\\u" + it.toInt().toString(16).padStart(4, '0')
}
}
return es.joinToString("")
}
fun unescape(str: String, position: Position): String {
val result = mutableListOf<Char>()
val iter = str.iterator()
while(iter.hasNext()) {
val c = iter.nextChar()
if(c=='\\') {
val ec = iter.nextChar()
result.add(when(ec) {
'\\' -> '\\'
'n' -> '\n'
'r' -> '\r'
'"' -> '"'
'\'' -> '\''
'u' -> {
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
}
'x' -> {
// special hack 0x8000..0x80ff will be outputted verbatim without encoding
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
(0x8000 + hex).toChar()
}
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
})
} else {
result.add(c)
}
}
return result.joinToString("")
}

View File

@ -1,7 +1,6 @@
package prog8.ast.base package prog8.ast.base
import prog8.ast.Node import prog8.ast.Node
import prog8.compiler.target.CompilationTarget
/**************************** AST Data classes ****************************/ /**************************** AST Data classes ****************************/
@ -44,7 +43,7 @@ enum class DataType {
this == other -> false this == other -> false
this in ByteDatatypes -> false this in ByteDatatypes -> false
this in WordDatatypes -> other in ByteDatatypes this in WordDatatypes -> other in ByteDatatypes
this==STR && other==UWORD || this==UWORD && other==STR -> false this== STR && other== UWORD || this== UWORD && other== STR -> false
else -> true else -> true
} }
@ -53,19 +52,9 @@ enum class DataType {
this == other -> true this == other -> true
this in ByteDatatypes -> other in ByteDatatypes this in ByteDatatypes -> other in ByteDatatypes
this in WordDatatypes -> other in WordDatatypes this in WordDatatypes -> other in WordDatatypes
this==STR && other==UWORD || this==UWORD && other==STR -> true this== STR && other== UWORD || this== UWORD && other== STR -> true
else -> false else -> false
} }
fun memorySize(): Int {
return when(this) {
in ByteDatatypes -> 1
in WordDatatypes -> 2
FLOAT -> CompilationTarget.instance.machine.FLOAT_MEM_SIZE
in PassByReferenceDatatypes -> CompilationTarget.instance.machine.POINTER_MEM_SIZE
else -> -9999999
}
}
} }
enum class CpuRegister { enum class CpuRegister {
@ -91,6 +80,13 @@ enum class RegisterOrPair {
val names by lazy { values().map { it.toString()} } val names by lazy { values().map { it.toString()} }
} }
fun asCpuRegister(): CpuRegister = when(this) {
A -> CpuRegister.A
X -> CpuRegister.X
Y -> CpuRegister.Y
else -> throw IllegalArgumentException("no cpu hardware register for $this")
}
} // only used in parameter and return value specs in asm subroutines } // only used in parameter and return value specs in asm subroutines
enum class Statusflag { enum class Statusflag {
@ -135,7 +131,8 @@ val IterableDatatypes = setOf(
DataType.STR, DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_F) DataType.ARRAY_F
)
val PassByValueDatatypes = NumericDatatypes val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT) val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
val ArrayElementTypes = mapOf( val ArrayElementTypes = mapOf(
@ -144,7 +141,8 @@ val ArrayElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE, DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD, DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD, DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT) DataType.ARRAY_F to DataType.FLOAT
)
val ElementArrayTypes = mapOf( val ElementArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B, DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB, DataType.UBYTE to DataType.ARRAY_UB,
@ -152,10 +150,12 @@ val ElementArrayTypes = mapOf(
DataType.UWORD to DataType.ARRAY_UW, DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F DataType.FLOAT to DataType.ARRAY_F
) )
val Cx16VirtualRegisters = listOf(RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, val Cx16VirtualRegisters = listOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11, RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11,
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15) RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
)
// find the parent node of a specific type or interface // find the parent node of a specific type or interface

View File

@ -3,14 +3,9 @@ package prog8.ast.expressions
import prog8.ast.* import prog8.ast.*
import prog8.ast.antlr.escape import prog8.ast.antlr.escape
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstVisitor
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.target.CompilationTarget import prog8.ast.walk.AstWalker
import prog8.functions.BuiltinFunctions import prog8.ast.walk.IAstVisitor
import prog8.functions.CannotEvaluateException
import prog8.functions.NotConstArgumentException
import prog8.functions.builtinFunctionReturnType
import java.util.* import java.util.*
import kotlin.math.abs import kotlin.math.abs
@ -152,10 +147,13 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
InferredTypes.unknown() InferredTypes.unknown()
else { else {
try { try {
InferredTypes.knownFor(commonDatatype( InferredTypes.knownFor(
commonDatatype(
leftDt.typeOrElse(DataType.BYTE), leftDt.typeOrElse(DataType.BYTE),
rightDt.typeOrElse(DataType.BYTE), rightDt.typeOrElse(DataType.BYTE),
null, null).first) null, null
).first
)
} catch (x: FatalAstException) { } catch (x: FatalAstException) {
InferredTypes.unknown() InferredTypes.unknown()
} }
@ -257,7 +255,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
override fun referencesIdentifier(vararg scopedName: String) = arrayvar.referencesIdentifier(*scopedName) override fun referencesIdentifier(vararg scopedName: String) = arrayvar.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
val target = arrayvar.targetStatement(program.namespace) val target = arrayvar.targetStatement(program)
if (target is VarDecl) { if (target is VarDecl) {
return when (target.datatype) { return when (target.datatype) {
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE) DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
@ -612,6 +610,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
class RangeExpr(var from: Expression, class RangeExpr(var from: Expression,
var to: Expression, var to: Expression,
var step: Expression, var step: Expression,
private val encoding: IStringEncoding,
override val position: Position) : Expression() { override val position: Position) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
@ -677,8 +676,8 @@ class RangeExpr(var from: Expression,
val toString = to as? StringLiteralValue val toString = to as? StringLiteralValue
if(fromString!=null && toString!=null ) { if(fromString!=null && toString!=null ) {
// string range -> int range over character values // string range -> int range over character values
fromVal = CompilationTarget.instance.encodeString(fromString.value, fromString.altEncoding)[0].toInt() fromVal = encoding.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
toVal = CompilationTarget.instance.encodeString(toString.value, fromString.altEncoding)[0].toInt() toVal = encoding.encodeString(toString.value, fromString.altEncoding)[0].toInt()
} else { } else {
val fromLv = from as? NumericLiteralValue val fromLv = from as? NumericLiteralValue
val toLv = to as? NumericLiteralValue val toLv = to as? NumericLiteralValue
@ -708,17 +707,18 @@ internal fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
} }
} }
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(), IAssignable { data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression(),
IAssignable {
override lateinit var parent: Node override lateinit var parent: Node
fun targetStatement(namespace: INameScope) = fun targetStatement(program: Program) =
if(nameInSource.size==1 && nameInSource[0] in BuiltinFunctions) if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
BuiltinFunctionStatementPlaceholder(nameInSource[0], position) BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
else else
namespace.lookup(nameInSource, this) program.namespace.lookup(nameInSource, this)
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource override fun equals(other: Any?) = other is IdentifierReference && other.nameInSource==nameInSource
override fun hashCode() = nameInSource.hashCode() override fun hashCode() = nameInSource.hashCode()
@ -754,14 +754,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
nameInSource.size==scopedName.size && nameInSource.toTypedArray().contentEquals(scopedName) nameInSource.size==scopedName.size && nameInSource.toTypedArray().contentEquals(scopedName)
override fun inferType(program: Program): InferredTypes.InferredType { override fun inferType(program: Program): InferredTypes.InferredType {
return when (val targetStmt = targetStatement(program.namespace)) { return when (val targetStmt = targetStatement(program)) {
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype) is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT) is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
else -> InferredTypes.InferredType.unknown() else -> InferredTypes.InferredType.unknown()
} }
} }
fun memberOfStruct(namespace: INameScope) = this.targetVarDecl(namespace)?.struct fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
fun heapId(namespace: INameScope): Int { fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this) val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
@ -774,11 +774,11 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
} }
} }
fun firstStructVarName(namespace: INameScope): 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.
val struct = memberOfStruct(namespace) ?: return null val struct = memberOfStruct(program) ?: return null
val decl = targetVarDecl(namespace)!! val decl = targetVarDecl(program)!!
if(decl.datatype!=DataType.STRUCT) if(decl.datatype!=DataType.STRUCT)
return null return null
@ -816,18 +816,9 @@ class FunctionCall(override var target: IdentifierReference,
private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? { private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? {
// if the function is a built-in function and the args are consts, should try to const-evaluate! // if the function is a built-in function and the args are consts, should try to const-evaluate!
// 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) return null if(target.nameInSource.size>1)
try { return null
var resultValue: NumericLiteralValue? = null val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
val func = BuiltinFunctions[target.nameInSource[0]]
if(func!=null) {
val exprfunc = func.constExpressionFunc
if(exprfunc!=null)
resultValue = exprfunc(args, position, program)
else if(func.known_returntype==null)
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
}
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)
@ -837,15 +828,6 @@ class FunctionCall(override var target: IdentifierReference,
return resultValue return resultValue
} }
} }
catch(x: NotConstArgumentException) {
// const-evaluating the builtin function call failed.
return null
}
catch(x: CannotEvaluateException) {
// const-evaluating the builtin function call failed.
return null
}
}
override fun toString(): String { override fun toString(): String {
return "FunctionCall(target=$target, pos=$position)" return "FunctionCall(target=$target, pos=$position)"
@ -860,14 +842,14 @@ class FunctionCall(override var target: IdentifierReference,
val constVal = constValue(program ,false) val constVal = constValue(program ,false)
if(constVal!=null) if(constVal!=null)
return InferredTypes.knownFor(constVal.type) return InferredTypes.knownFor(constVal.type)
val stmt = target.targetStatement(program.namespace) ?: return InferredTypes.unknown() val stmt = target.targetStatement(program) ?: return InferredTypes.unknown()
when (stmt) { when (stmt) {
is BuiltinFunctionStatementPlaceholder -> { is BuiltinFunctionStatementPlaceholder -> {
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" || if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") { target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
return InferredTypes.void() // these have no return value return InferredTypes.void() // these have no return value
} }
return builtinFunctionReturnType(target.nameInSource[0], this.args, program) return program.builtinFunctions.returnType(target.nameInSource[0], this.args)
} }
is Subroutine -> { is Subroutine -> {
if(stmt.returntypes.isEmpty()) if(stmt.returntypes.isEmpty())

View File

@ -3,10 +3,8 @@ package prog8.ast.statements
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.processing.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.CompilerException
import prog8.compiler.target.CompilationTarget
sealed class Statement : Node { sealed class Statement : Node {
@ -36,8 +34,8 @@ sealed class Statement : Node {
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() { class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
override var parent: Node = ParentSentinel override var parent: Node = ParentSentinel
override fun linkParents(parent: Node) {} override fun linkParents(parent: Node) {}
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = throw FatalAstException("should not iterate over this node")
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = throw FatalAstException("should not iterate over this node")
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
replacement.parent = this replacement.parent = this
@ -252,7 +250,7 @@ open class VarDecl(val type: VarDeclType,
if(allowInitializeWithZero) if(allowInitializeWithZero)
return defaultZero(declaredDatatype, position) return defaultZero(declaredDatatype, position)
else else
throw CompilerException("attempt to get zero value for vardecl that shouldn't get it") throw IllegalArgumentException("attempt to get zero value for vardecl that shouldn't get it")
} }
fun flattenStructMembers(): MutableList<Statement> { fun flattenStructMembers(): MutableList<Statement> {
@ -518,44 +516,6 @@ data class AssignTarget(var identifier: IdentifierReference?,
} }
return false return false
} }
fun isInRegularRAM(namespace: INameScope): Boolean {
when {
this.memoryAddress != null -> {
return when (this.memoryAddress.addressExpression) {
is NumericLiteralValue -> {
CompilationTarget.instance.machine.isRegularRAMaddress((this.memoryAddress.addressExpression as NumericLiteralValue).number.toInt())
}
is IdentifierReference -> {
val decl = (this.memoryAddress.addressExpression as IdentifierReference).targetVarDecl(namespace)
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
false
}
else -> false
}
}
this.arrayindexed != null -> {
val targetStmt = this.arrayindexed!!.arrayvar.targetVarDecl(namespace)
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteralValue
if (addr != null)
CompilationTarget.instance.machine.isRegularRAMaddress(addr.number.toInt())
else
false
} else true
}
this.identifier != null -> {
val decl = this.identifier!!.targetVarDecl(namespace)!!
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
CompilationTarget.instance.machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
true
}
else -> return true
}
}
} }

View File

@ -1,4 +1,4 @@
package prog8.ast.processing package prog8.ast.walk
import prog8.ast.* import prog8.ast.*
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
@ -16,7 +16,8 @@ interface IAstModification {
} }
} }
class SetExpression(private val setter: (newExpr: Expression) -> Unit, private val newExpr: Expression, private val parent: Node) : IAstModification { class SetExpression(private val setter: (newExpr: Expression) -> Unit, private val newExpr: Expression, private val parent: Node) :
IAstModification {
override fun perform() { override fun perform() {
setter(newExpr) setter(newExpr)
newExpr.linkParents(parent) newExpr.linkParents(parent)
@ -37,7 +38,8 @@ interface IAstModification {
} }
} }
class InsertAfter(private val after: Statement, private val stmt: Statement, private val parent: INameScope) : IAstModification { class InsertAfter(private val after: Statement, private val stmt: Statement, private val parent: INameScope) :
IAstModification {
override fun perform() { override fun perform() {
val idx = parent.statements.indexOfFirst { it===after } + 1 val idx = parent.statements.indexOfFirst { it===after } + 1
parent.statements.add(idx, stmt) parent.statements.add(idx, stmt)
@ -45,7 +47,8 @@ interface IAstModification {
} }
} }
class InsertBefore(private val before: Statement, private val stmt: Statement, private val parent: INameScope) : IAstModification { class InsertBefore(private val before: Statement, private val stmt: Statement, private val parent: INameScope) :
IAstModification {
override fun perform() { override fun perform() {
val idx = parent.statements.indexOfFirst { it===before } val idx = parent.statements.indexOfFirst { it===before }
parent.statements.add(idx, stmt) parent.statements.add(idx, stmt)
@ -53,7 +56,8 @@ interface IAstModification {
} }
} }
class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) : IAstModification { class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) :
IAstModification {
override fun perform() { override fun perform() {
parent.replaceChildNode(node, replacement) parent.replaceChildNode(node, replacement)
replacement.linkParents(parent) replacement.linkParents(parent)
@ -80,7 +84,6 @@ abstract class AstWalker {
open fun before(block: Block, parent: Node): Iterable<IAstModification> = emptyList() open fun before(block: Block, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList() open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList() open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
@ -391,11 +394,6 @@ abstract class AstWalker {
track(after(inlineAssembly, parent), inlineAssembly, parent) track(after(inlineAssembly, parent), inlineAssembly, parent)
} }
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
}
fun visit(nopStatement: NopStatement, parent: Node) { fun visit(nopStatement: NopStatement, parent: Node) {
track(before(nopStatement, parent), nopStatement, parent) track(before(nopStatement, parent), nopStatement, parent)
track(after(nopStatement, parent), nopStatement, parent) track(after(nopStatement, parent), nopStatement, parent)

View File

@ -1,4 +1,4 @@
package prog8.ast.processing package prog8.ast.walk
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
@ -158,9 +158,6 @@ interface IAstVisitor {
fun visit(inlineAssembly: InlineAssembly) { fun visit(inlineAssembly: InlineAssembly) {
} }
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
}
fun visit(nopStatement: NopStatement) { fun visit(nopStatement: NopStatement) {
} }

View File

@ -3,7 +3,6 @@ package prog8.parser
import org.antlr.v4.runtime.CommonTokenStream import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.Lexer import org.antlr.v4.runtime.Lexer
internal class CommentHandlingTokenStream(lexer: Lexer) : CommonTokenStream(lexer) { internal class CommentHandlingTokenStream(lexer: Lexer) : CommonTokenStream(lexer) {
data class Comment(val type: String, val line: Int, val comment: String) data class Comment(val type: String, val line: Int, val comment: String)

View File

@ -1,34 +1,33 @@
package prog8.parser package prog8.parser
import org.antlr.v4.runtime.* import org.antlr.v4.runtime.*
import prog8.ast.IStringEncoding
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.antlr.toAst import prog8.ast.antlr.toAst
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.base.SyntaxError import prog8.ast.base.SyntaxError
import prog8.ast.base.checkImportedValid
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.ast.statements.DirectiveArg import prog8.ast.statements.DirectiveArg
import prog8.compiler.target.CompilationTarget
import prog8.pathFrom
import java.io.InputStream import java.io.InputStream
import java.nio.file.FileSystems
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
internal class ParsingFailedError(override var message: String) : Exception(message) class ParsingFailedError(override var message: String) : Exception(message)
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input) internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.') internal fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
internal class ModuleImporter { class ModuleImporter {
internal fun importModule(program: Program, filePath: Path): Module { fun importModule(program: Program, filePath: Path, encoder: IStringEncoding, compilationTargetName: String): Module {
print("importing '${moduleName(filePath.fileName)}'") print("importing '${moduleName(filePath.fileName)}'")
if(filePath.parent!=null) { if(filePath.parent!=null) {
var importloc = filePath.toString() var importloc = filePath.toString()
@ -43,14 +42,15 @@ internal class ModuleImporter {
throw ParsingFailedError("No such file: $filePath") throw ParsingFailedError("No such file: $filePath")
val input = CharStreams.fromPath(filePath) val input = CharStreams.fromPath(filePath)
return importModule(program, input, filePath, false) return importModule(program, input, filePath, false, encoder, compilationTargetName)
} }
internal fun importLibraryModule(program: Program, name: String): Module? { fun importLibraryModule(program: Program, name: String,
encoder: IStringEncoding, compilationTargetName: String): Module? {
val import = Directive("%import", listOf( val import = Directive("%import", listOf(
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0)) DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0))
), Position("<<<implicit-import>>>", 0, 0, 0)) ), Position("<<<implicit-import>>>", 0, 0, 0))
return executeImportDirective(program, import, Paths.get("")) return executeImportDirective(program, import, Paths.get(""), encoder, compilationTargetName)
} }
private class MyErrorListener: ConsoleErrorListener() { private class MyErrorListener: ConsoleErrorListener() {
@ -65,7 +65,8 @@ internal class ModuleImporter {
} }
} }
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module { private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean,
encoder: IStringEncoding, compilationTargetName: String): Module {
val moduleName = moduleName(modulePath.fileName) val moduleName = moduleName(modulePath.fileName)
val lexer = CustomLexer(modulePath, stream) val lexer = CustomLexer(modulePath, stream)
lexer.removeErrorListeners() lexer.removeErrorListeners()
@ -84,7 +85,7 @@ internal class ModuleImporter {
// tokens.commentTokens().forEach { println(it) } // tokens.commentTokens().forEach { println(it) }
// convert to Ast // convert to Ast
val moduleAst = parseTree.toAst(moduleName, isLibrary, modulePath) val moduleAst = parseTree.toAst(moduleName, isLibrary, modulePath, encoder)
moduleAst.program = program moduleAst.program = program
moduleAst.linkParents(program.namespace) moduleAst.linkParents(program.namespace)
program.modules.add(moduleAst) program.modules.add(moduleAst)
@ -94,13 +95,14 @@ internal class ModuleImporter {
lines.asSequence() lines.asSequence()
.mapIndexed { i, it -> i to it } .mapIndexed { i, it -> i to it }
.filter { (it.second as? Directive)?.directive == "%import" } .filter { (it.second as? Directive)?.directive == "%import" }
.forEach { executeImportDirective(program, it.second as Directive, modulePath) } .forEach { executeImportDirective(program, it.second as Directive, modulePath, encoder, compilationTargetName) }
moduleAst.statements = lines moduleAst.statements = lines
return moduleAst return moduleAst
} }
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? { private fun executeImportDirective(program: Program, import: Directive, source: Path,
encoder: IStringEncoding, compilationTargetName: String): Module? {
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null) if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
throw SyntaxError("invalid import directive", import.position) throw SyntaxError("invalid import directive", import.position)
val moduleName = import.args[0].name!! val moduleName = import.args[0].name!!
@ -111,27 +113,36 @@ internal class ModuleImporter {
if(existing!=null) if(existing!=null)
return null return null
val rsc = tryGetModuleFromResource("$moduleName.p8") val rsc = tryGetModuleFromResource("$moduleName.p8", compilationTargetName)
val importedModule = val importedModule =
if(rsc!=null) { if(rsc!=null) {
// load the module from the embedded resource // load the module from the embedded resource
val (resource, resourcePath) = rsc val (resource, resourcePath) = rsc
resource.use { resource.use {
println("importing '$moduleName' (library)") println("importing '$moduleName' (library)")
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$resourcePath"), true) importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$resourcePath"),
true, encoder, compilationTargetName)
} }
} else { } else {
val modulePath = tryGetModuleFromFile(moduleName, source, import.position) val modulePath = tryGetModuleFromFile(moduleName, source, import.position)
importModule(program, modulePath) importModule(program, modulePath, encoder, compilationTargetName)
} }
importedModule.checkImportedValid() removeDirectivesFromImportedModule(importedModule)
return importedModule return importedModule
} }
private fun tryGetModuleFromResource(name: String): Pair<InputStream, String>? { private fun removeDirectivesFromImportedModule(importedModule: Module) {
val target = CompilationTarget.instance.name // Most global directives don't apply for imported modules, so remove them
val targetSpecificPath = "/prog8lib/$target/$name" val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%target")
var directives = importedModule.statements.filterIsInstance<Directive>()
importedModule.statements.removeAll(directives)
directives = directives.filter{ it.directive !in moduleLevelDirectives }
importedModule.statements.addAll(0, directives)
}
private fun tryGetModuleFromResource(name: String, compilationTargetName: String): Pair<InputStream, String>? {
val targetSpecificPath = "/prog8lib/$compilationTargetName/$name"
val targetSpecificResource = object{}.javaClass.getResourceAsStream(targetSpecificPath) val targetSpecificResource = object{}.javaClass.getResourceAsStream(targetSpecificPath)
if(targetSpecificResource!=null) if(targetSpecificResource!=null)
return Pair(targetSpecificResource, targetSpecificPath) return Pair(targetSpecificResource, targetSpecificPath)

View File

@ -2,7 +2,7 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" version "1.4.21" id "org.jetbrains.kotlin.jvm" version "1.4.30"
id 'com.github.johnrengelman.shadow' version '6.1.0' id 'com.github.johnrengelman.shadow' version '6.1.0'
} }

View File

@ -803,6 +803,17 @@ mkword(msb, lsb)
Don't get confused by how the system actually stores this 16-bit word value in memory (which is Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb) in little-endian format, so lsb first then msb)
peek(address)
same as @(address) - reads the byte at the given address in memory.
peekw(address)
reads the word value at the given address in memory. Word is read as usual little-endian lsb/msb byte order.
poke(address, value)
same as @(address)=value - writes the byte value at the given address in memory.
pokew(address, value)
writes the word value at the given address in memory, in usual little-endian lsb/msb byte order.
rnd() rnd()
returns a pseudo-random byte from 0..255 returns a pseudo-random byte from 0..255

View File

@ -2,16 +2,14 @@
TODO TODO
==== ====
- add any2(), all2(), max2(), min2(), reverse2(), sum2(), sort2() that take (array, startindex, length) arguments - refactor the asmgen into their own submodule?
- optimize for loop iterations better to allow proper inx, cpx #value, bne loop instructions (like repeat loop) - refactor the compiler optimizers into their own submodule?
- why is there a beq _prog8_label_2_repeatend at the end of repeat loops? seems unused
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2)) - optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- implement the linked_list millfork benchmark - optimize several inner loops in gfx2 (highres 4 color mode)
- use the 65c02 bit clear/set/test instructions for single-bit operations - 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')
- implement highres 4 color mode in gfx2 - add a flood fill routine to gfx2?
- make a retro amiga workbench 1.3 and/or 2.0 workbench "simulator" using that new gfx mode - add sound to the cx16 tehtriz
- can we get rid of the --longOptionName command line options and only keep the short versions? https://github.com/Kotlin/kotlinx-cli/issues/50
- add a f_seek() routine for the Cx16 that uses its seek dos api? - add a f_seek() routine for the Cx16 that uses its seek dos api?
- 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

207
examples/cx16/amiga.p8 Normal file
View File

@ -0,0 +1,207 @@
%import textio
%import palette
%import string
%import gfx2
; Mockup of a classic Amiga Workbench screen.
main {
sub start() {
gfx2.screen_mode(6) ; select 640*480 mode, 4 colors
uword[4] amigacolors = [$aaa, $000, $fff, $68c] ; gray, black, white, lightblue
palette.set_rgb(amigacolors, len(amigacolors))
cx16.VERA_DC_VSCALE = 64 ; have the vertical resolution so it is 640*240 - more or less Amiga's default non interlaced mode
gfx2.text_charset(3)
screen_titlebar()
window_workbench()
window_system()
window_shell()
gfx2.text(280, 210, 1, @"640x480(240) 4 colors")
gfx2.text(280, 220, 1, @"Mockup drawn using Prog8 gfx2 library")
repeat {
}
}
sub screen_titlebar() {
gfx2.fillrect(0, 0, gfx2.width, 10, 2)
gfx2.text(8,1, 1, @"AmigaOS 3.1 2,002,448 graphics mem 16,504,384 other mem")
gfx2.horizontal_line(0, 10, gfx2.width, 1)
widget.window_order_icon(gfx2.width-widget.window_order_icon.width, 0, false)
}
sub window_workbench() {
const uword win_x = 10
const uword win_y = 16
const uword width = 600
const uword height = 220
widget.window_titlebar(win_x, win_y, width, @"Workbench", false)
; gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, false)
vector_v(win_x+width - 390, win_y+height-20)
vector_v(win_x+width - 390 -14, win_y+height-20)
widget.icon(45,40, @"Ram Disk")
widget.icon(45,90, @"Workbench3.1")
}
sub vector_v(uword x, uword y) {
gfx2.horizontal_line(x, y, 12, 1)
gfx2.horizontal_line(x+16, y+16, 12,1)
gfx2.line(x,y,x+16,y+16,1)
gfx2.line(x+11,y,x+16+6,y+10,1)
gfx2.line(x+16+6,y+10,x+48,y-16,1)
gfx2.line(x+16+11,y+16,x+48+11,y-16,1)
}
sub window_system() {
const uword width = 300
const uword height = 120
const uword win_x = 320
const uword win_y = 40
widget.window_titlebar(win_x, win_y, width, @"System", false)
gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2, 0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, false)
widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, false)
widget.icon(win_x+16, win_y+14, @"FixFonts")
widget.icon(win_x+16+80, win_y+14, @"NoFastMem")
widget.icon(win_x+16, win_y+56, @"Format")
widget.icon(win_x+16+80, win_y+56, @"RexxMast")
widget.icon(win_x+16+160, win_y+56, @"Shell")
}
sub window_shell() {
const uword win_x = 64-4
const uword win_y = 140
const uword width = 500
const uword height = 65
widget.window_titlebar(win_x, win_y, width, @"AmigaShell", true)
gfx2.fillrect(win_x+3, win_y+11, width-4, height-11-2,0) ; clear window pane
widget.window_leftborder(win_x, win_y, height, true)
widget.window_bottomborder(win_x, win_y, width, height)
widget.window_rightborder(win_x, win_y, width, height, true)
gfx2.text(win_x+5, win_y+12, 1, @"New Shell process 3")
gfx2.text(win_x+5, win_y+12+8, 1, @"3.Workbench3.1:>")
gfx2.fillrect(win_x+5+17*8, win_y+12+8, 8, 8, 1) ; cursor
}
}
widget {
sub highlightedrect(uword x, uword y, uword width, uword height, ubyte fill, ubyte active) {
gfx2.horizontal_line(x, y, width, 2)
gfx2.vertical_line(x, y+1, height-1, 2)
gfx2.vertical_line(x+width-1, y+1, height-1, 1)
gfx2.horizontal_line(x+1, y+height-1, width-2, 1)
if fill {
if active
gfx2.fillrect(x+1,y+1,width-2,height-2, 3)
else
gfx2.fillrect(x+1,y+1,width-2,height-2, 0)
}
}
sub icon(uword x, uword y, uword caption) {
const ubyte width = 56
const ubyte height = 28
highlightedrect(x, y, width, height, false, false)
uword middlex = x+width/2+1
ubyte halfstring = string.length(caption) * 4
gfx2.text(middlex-halfstring,y+height+1,1,caption)
gfx2.disc(x+width/4+4, y+height/2, height/2-3, 1)
gfx2.fillrect(x+20,y+12,width/2,height/2-4,3)
}
sub window_titlebar(uword x, uword y, uword width, uword titlestr, ubyte active) {
const ubyte height = 11
widget.highlightedrect(x+widget.window_close_icon.width, y, width-64, height, true, active)
gfx2.plot(x+widget.window_close_icon.width, y+height-1, 1) ; correct bottom left corner
gfx2.text(x+32, y+1, 1, titlestr)
widget.window_close_icon(x, y, active)
widget.window_order_icon(x+width-22, y, active)
widget.window_flipsize_icon(x+width-44, y, active)
}
sub window_flipsize_icon(uword x, uword y, ubyte active) {
const uword width = 22
const uword height = 11
highlightedrect(x, y, width, height, true, active)
gfx2.plot(x, y+height-1, 1) ; correct bottom left corner
gfx2.rect(x+5, y+2, width-9, height-4, 1)
gfx2.rect(x+5, y+2, 7, 4, 1)
gfx2.fillrect(x+6, y+3, 5, 2, 2)
}
sub window_order_icon(uword x, uword y, ubyte active) {
const uword width = 22
const uword height = 11
highlightedrect(x, y, width, height, true, active)
gfx2.plot(x, y+height-1, 1) ; correct bottom left corner
gfx2.rect(x+4, y+2, 10, 5, 1) ; back
gfx2.fillrect(x+9, y+5, 8, 3, 2) ; white front
gfx2.rect(x+8, y+4, 10, 5, 1) ; front
}
sub window_close_icon(uword x, uword y, ubyte active) {
const uword width = 20
const uword height = 11
highlightedrect(x, y, width, height, true, active)
gfx2.plot(x, y+height-1, 1) ; correct bottom left corner
gfx2.rect(x+7, y+3, 5, 5, 1)
gfx2.fillrect(x+8, y+4, 3, 3, 2)
}
sub window_leftborder(uword x, uword y, uword height, ubyte active) {
gfx2.vertical_line(x, y, height, 2)
ubyte color = 0
if active
color = 3
gfx2.vertical_line(x+1, y+11, height-11, color)
gfx2.vertical_line(x+2, y+11, height-11, 1)
}
sub window_bottomborder(uword x, uword y, uword width, uword height) {
gfx2.horizontal_line(x+3, y+height-2, width-3, 2)
gfx2.horizontal_line(x, y+height-1, width, 1)
}
sub window_rightborder(uword x, uword y, uword width, uword height, ubyte active) {
gfx2.vertical_line(x+width-1-16, y+11, height-13,2)
gfx2.vertical_line(x+width-1, y+11, height-11,1)
ubyte color = 0
if active
color = 3
gfx2.fillrect(x+width-1-15, y+11, 15, height-12, color)
gfx2.horizontal_line(x+width-1-13, y+height-3, 11, 1)
gfx2.vertical_line(x+width-1-3, y+height-3-5, 5, 1)
gfx2.line(x+width-1-13,y+height-3, x+width-1-3, y+height-3-5, 1)
gfx2.horizontal_line(x+width-1-16, y+height-10, 16, 2)
highlightedrect(x+width-13, y+12, 10, height-43, false, false)
gfx2.horizontal_line(x+width-1-16, y+height-11, 16, 1)
gfx2.horizontal_line(x+width-1-16, y+height-20, 16, 2)
gfx2.horizontal_line(x+width-1-16, y+height-21, 16, 1)
gfx2.horizontal_line(x+width-1-16, y+height-30, 16, 2)
gfx2.line(x+width-1-12, y+height-23, x+width-8, y+height-28, 1)
gfx2.line(x+width-1-3, y+height-23, x+width-9, y+height-28, 1)
gfx2.line(x+width-1-13, y+height-18, x+width-8, y+height-13, 1)
gfx2.line(x+width-1-3, y+height-18, x+width-9, y+height-13, 1)
}
}

View File

@ -11,12 +11,12 @@ main {
test_monochrome() test_monochrome()
gfx2.screen_mode(255) gfx2.screen_mode(0)
txt.print("done!\n") txt.print("done!\n")
} }
sub test_monochrome() { sub test_monochrome() {
gfx2.screen_mode(128) gfx2.screen_mode(5)
uword yy uword yy
uword xx uword xx
word ww word ww

642
examples/cx16/tehtriz.p8 Normal file
View File

@ -0,0 +1,642 @@
; TehTriz - a Tetris clone. Commander X16 version.
;
; features:
; holding area
; wall kick rotations
; shows next piece
; staged speed increase
; TODO: replicate the simple sound effects from the C64 version
%target cx16
%import syslib
%import textio
%import test_stack
main {
const ubyte boardOffsetX = 14
const ubyte boardOffsetY = 3
const ubyte boardWidth = 10
const ubyte boardHeight = 20
const ubyte startXpos = boardOffsetX + 3
const ubyte startYpos = boardOffsetY - 2
uword lines
uword score
ubyte xpos
ubyte ypos
ubyte nextBlock
ubyte speedlevel
ubyte holding
ubyte holdingAllowed
sub start() {
cx16.screen_set_mode(0) ; low res
sound.init()
newGame()
drawBoard()
gameOver()
newgame:
newGame()
drawBoard()
spawnNextBlock()
waitkey:
ubyte time_lo = lsb(c64.RDTIM16())
if time_lo>=(60-4*speedlevel) {
c64.SETTIM(0,0,0)
drawBlock(xpos, ypos, 32) ; hide block
if blocklogic.noCollision(xpos, ypos+1) {
; slowly move the block down
ypos++
drawBlock(xpos, ypos, 160) ; show block on new position
} else {
; block can't move further down!
; check if the game area is full, if not, spawn the next block at the top.
if blocklogic.isGameOver(xpos, ypos) {
gameOver()
goto newgame
} else {
sound.blockrotate()
checkForLines()
spawnNextBlock()
score++
}
}
drawScore()
; txt.plot(0,0)
; test_stack.test()
}
ubyte key=c64.GETIN()
if key==0 goto waitkey
keypress(key)
goto waitkey
}
sub move_left() {
drawBlock(xpos, ypos, 32)
if blocklogic.noCollision(xpos-1, ypos) {
xpos--
}
drawBlock(xpos, ypos, 160)
}
sub move_right() {
drawBlock(xpos, ypos, 32)
if blocklogic.noCollision(xpos+1, ypos) {
xpos++
}
drawBlock(xpos, ypos, 160)
}
sub move_down_faster() {
drawBlock(xpos, ypos, 32)
if blocklogic.noCollision(xpos, ypos+1) {
ypos++
}
drawBlock(xpos, ypos, 160)
}
sub drop_down_immediately() {
drawBlock(xpos, ypos, 32)
ubyte dropypos
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
if not blocklogic.noCollision(xpos, dropypos) {
dropypos-- ; the furthest down that still fits
break
}
}
if dropypos>ypos {
ypos = dropypos
sound.blockdrop()
drawBlock(xpos, ypos, 160)
checkForLines()
spawnNextBlock()
score++
drawScore()
}
}
sub keypress(ubyte key) {
when key {
157, ',' -> move_left()
29, '/' -> move_right()
17, '.' -> move_down_faster()
145, ' ' -> drop_down_immediately()
'z' -> {
; no joystick equivalent (there is only 1 fire button)
; rotate counter clockwise
drawBlock(xpos, ypos, 32)
if blocklogic.canRotateCCW(xpos, ypos) {
blocklogic.rotateCCW()
sound.blockrotate()
}
else if blocklogic.canRotateCCW(xpos-1, ypos) {
xpos--
blocklogic.rotateCCW()
sound.blockrotate()
}
else if blocklogic.canRotateCCW(xpos+1, ypos) {
xpos++
blocklogic.rotateCCW()
sound.blockrotate()
}
drawBlock(xpos, ypos, 160)
}
'x' -> {
; rotate clockwise
drawBlock(xpos, ypos, 32)
if blocklogic.canRotateCW(xpos, ypos) {
blocklogic.rotateCW()
sound.blockrotate()
}
else if blocklogic.canRotateCW(xpos-1, ypos) {
xpos--
blocklogic.rotateCW()
sound.blockrotate()
}
else if blocklogic.canRotateCW(xpos+1, ypos) {
xpos++
blocklogic.rotateCW()
sound.blockrotate()
}
drawBlock(xpos, ypos, 160)
}
'c' -> {
; hold
if holdingAllowed {
sound.swapping()
if holding<7 {
drawBlock(xpos, ypos, 32)
ubyte newholding = blocklogic.currentBlockNum
swapBlock(holding)
holding = newholding
holdingAllowed = false
} else {
holding = blocklogic.currentBlockNum
drawBlock(xpos, ypos, 32)
spawnNextBlock()
}
drawHoldBlock()
}
}
}
}
sub checkForLines() {
; check if line(s) are full -> flash/clear line(s) + add score + move rest down
ubyte[boardHeight] complete_lines
ubyte num_lines=0
ubyte linepos
sys.memset(complete_lines, len(complete_lines), 0)
for linepos in boardOffsetY to boardOffsetY+boardHeight-1 {
if blocklogic.isLineFull(linepos) {
complete_lines[num_lines]=linepos
num_lines++
ubyte x
for x in boardOffsetX to boardOffsetX+boardWidth-1
txt.setcc(x, linepos, 160, 1)
}
}
if num_lines {
if num_lines>3
sound.lineclear_big()
else
sound.lineclear()
sys.wait(20) ; slight delay to flash the line
for linepos in complete_lines
if linepos and blocklogic.isLineFull(linepos)
blocklogic.collapse(linepos)
lines += num_lines
uword[] scores = [10, 25, 50, 100] ; can never clear more than 4 lines at once
score += scores[num_lines-1]
speedlevel = 1+lsb(lines/10)
drawScore()
}
}
sub gameOver() {
sound.gameover()
txt.plot(7, 7)
c64.CHROUT('U')
txt.print("────────────────────────")
c64.CHROUT('I')
txt.plot(7, 8)
txt.print("│*** g a m e o v e r ***│")
txt.plot(7, 9)
c64.CHROUT('J')
txt.print("────────────────────────")
c64.CHROUT('K')
txt.plot(7, 18)
c64.CHROUT('U')
txt.print("────────────────────────")
c64.CHROUT('I')
txt.plot(7, 19)
txt.print("│ f1 for new game │")
txt.plot(7, 20)
c64.CHROUT('J')
txt.print("────────────────────────")
c64.CHROUT('K')
ubyte key = 0
while key!=133 {
; endless loop until user presses F1 to restart the game
key = c64.GETIN()
}
}
sub newGame() {
lines = 0
score = 0
xpos = startXpos
ypos = startYpos
speedlevel = 1
nextBlock = rnd() % 7
holding = 255
holdingAllowed = true
}
sub swapBlock(ubyte newblock) {
blocklogic.newCurrentBlock(newblock)
xpos = startXpos
ypos = startYpos
drawBlock(xpos, ypos, 160)
}
sub spawnNextBlock() {
swapBlock(nextBlock)
nextBlock = (rnd() + lsb(c64.RDTIM16())) % 7
drawNextBlock()
holdingAllowed = true
}
sub drawBoard() {
txt.clear_screen()
txt.color(7)
txt.plot(1,1)
txt.print("* tehtriz *")
txt.color(5)
txt.plot(6,4)
txt.print("hold:")
txt.plot(2,22)
txt.print("speed: ")
txt.plot(28,3)
txt.print("next:")
txt.plot(28,10)
txt.print("lines:")
txt.plot(28,14)
txt.print("score:")
txt.color(12)
txt.plot(27,18)
txt.print("controls:")
txt.color(11)
txt.plot(28,19)
txt.print(",/ move")
txt.plot(28,20)
txt.print("zx rotate")
txt.plot(29,21)
txt.print(". descend")
txt.plot(27,22)
txt.print("spc drop")
txt.plot(29,23)
txt.print("c hold")
txt.setcc(boardOffsetX-1, boardOffsetY-2, 255, 0) ; invisible barrier
txt.setcc(boardOffsetX-1, boardOffsetY-3, 255, 0) ; invisible barrier
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-2, 255, 0) ; invisible barrier
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-3, 255, 0) ; invisible barrier
txt.setcc(boardOffsetX-1, boardOffsetY-1, 108, 12)
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
txt.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
txt.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12)
txt.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12)
ubyte i
for i in boardOffsetX+boardWidth-1 downto boardOffsetX {
txt.setcc(i, boardOffsetY-3, 255, 0) ; invisible barrier
txt.setcc(i, boardOffsetY+boardHeight, 69, 11)
}
for i in boardOffsetY+boardHeight-1 downto boardOffsetY {
txt.setcc(boardOffsetX-1, i, 89, 11)
txt.setcc(boardOffsetX+boardWidth, i, 84, 11)
}
ubyte[] colors = [6,8,7,5,4]
for i in len(colors)-1 downto 0 {
ubyte x
for x in 5 downto 0 {
txt.setcc(6+x-i, 11+2*i, 102, colors[i])
}
}
drawScore()
}
sub drawScore() {
txt.color(1)
txt.plot(30,11)
txt.print_uw(lines)
txt.plot(30,15)
txt.print_uw(score)
txt.plot(9,22)
txt.print_ub(speedlevel)
}
sub drawNextBlock() {
const ubyte nextBlockXpos = 29
const ubyte nextBlockYpos = 5
ubyte x
for x in nextBlockXpos+3 downto nextBlockXpos {
txt.setcc(x, nextBlockYpos, ' ', 0)
txt.setcc(x, nextBlockYpos+1, ' ', 0)
}
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
ubyte prev = blocklogic.currentBlockNum
blocklogic.newCurrentBlock(nextBlock)
drawBlock(nextBlockXpos, nextBlockYpos, 160)
blocklogic.newCurrentBlock(prev)
}
sub drawHoldBlock() {
const ubyte holdBlockXpos = 7
const ubyte holdBlockYpos = 6
ubyte x
for x in holdBlockXpos+3 downto holdBlockXpos {
txt.setcc(x, holdBlockYpos, '@', 0)
txt.setcc(x, holdBlockYpos+1, '@', 0)
}
if holding < 7 {
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
ubyte prev = blocklogic.currentBlockNum
blocklogic.newCurrentBlock(holding)
drawBlock(holdBlockXpos, holdBlockYpos, 160)
blocklogic.newCurrentBlock(prev)
}
}
sub drawBlock(ubyte x, ubyte y, ubyte character) {
ubyte @zp i
for i in 15 downto 0 {
ubyte @zp c=blocklogic.currentBlock[i]
if c
txt.setcc((i&3)+x, (i/4)+y, character, c)
}
}
}
blocklogic {
ubyte currentBlockNum
ubyte[16] currentBlock
ubyte[16] rotated
; the 7 tetrominos
ubyte[] blockI = [0,0,0,0, ; cyan ; note: special rotation (around matrix center)
3,3,3,3,
0,0,0,0,
0,0,0,0]
ubyte[] blockJ = [6,0,0,0, ; blue
6,6,6,0,
0,0,0,0,
0,0,0,0]
ubyte[] blockL = [0,0,8,0, ; orange
8,8,8,0,
0,0,0,0,
0,0,0,0]
ubyte[] blockO = [0,7,7,0, ; yellow ; note: no rotation (square)
0,7,7,0,
0,0,0,0,
0,0,0,0]
ubyte[] blockS = [0,5,5,0, ; green
5,5,0,0,
0,0,0,0,
0,0,0,0]
ubyte[] blockT = [0,4,0,0, ; purple
4,4,4,0,
0,0,0,0,
0,0,0,0]
ubyte[] blockZ = [2,2,0,0, ; red
0,2,2,0,
0,0,0,0,
0,0,0,0]
uword[] blocks = [&blockI, &blockJ, &blockL, &blockO, &blockS, &blockT, &blockZ]
sub newCurrentBlock(ubyte block) {
currentBlockNum = block
sys.memcopy(blocks[block], currentBlock, len(currentBlock))
}
sub rotateCW() {
; rotates the current block clockwise.
if currentBlockNum==0 {
; the 'I' block rotates a 4x4 matrix around the center
rotated[0] = currentBlock[12]
rotated[1] = currentBlock[8]
rotated[2] = currentBlock[4]
rotated[3] = currentBlock[0]
rotated[4] = currentBlock[13]
rotated[5] = currentBlock[9]
rotated[6] = currentBlock[5]
rotated[7] = currentBlock[1]
rotated[8] = currentBlock[14]
rotated[9] = currentBlock[10]
rotated[10] = currentBlock[6]
rotated[11] = currentBlock[2]
rotated[12] = currentBlock[15]
rotated[13] = currentBlock[11]
rotated[14] = currentBlock[7]
rotated[15] = currentBlock[3]
sys.memcopy(rotated, currentBlock, len(currentBlock))
}
else if currentBlockNum!=3 {
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
sys.memset(rotated, len(rotated), 0)
rotated[0] = currentBlock[8]
rotated[1] = currentBlock[4]
rotated[2] = currentBlock[0]
rotated[4] = currentBlock[9]
rotated[5] = currentBlock[5]
rotated[6] = currentBlock[1]
rotated[8] = currentBlock[10]
rotated[9] = currentBlock[6]
rotated[10] = currentBlock[2]
sys.memcopy(rotated, currentBlock, len(currentBlock))
}
}
sub rotateCCW() {
; rotates the current block counterclockwise.
if currentBlockNum==0 {
; the 'I' block rotates a 4x4 matrix around the center
rotated[0] = currentBlock[3]
rotated[1] = currentBlock[7]
rotated[2] = currentBlock[11]
rotated[3] = currentBlock[15]
rotated[4] = currentBlock[2]
rotated[5] = currentBlock[6]
rotated[6] = currentBlock[10]
rotated[7] = currentBlock[14]
rotated[8] = currentBlock[1]
rotated[9] = currentBlock[5]
rotated[10] = currentBlock[9]
rotated[11] = currentBlock[13]
rotated[12] = currentBlock[0]
rotated[13] = currentBlock[4]
rotated[14] = currentBlock[8]
rotated[15] = currentBlock[12]
sys.memcopy(rotated, currentBlock, len(currentBlock))
}
else if currentBlockNum!=3 {
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
sys.memset(rotated, len(rotated), 0)
rotated[0] = currentBlock[2]
rotated[1] = currentBlock[6]
rotated[2] = currentBlock[10]
rotated[4] = currentBlock[1]
rotated[5] = currentBlock[5]
rotated[6] = currentBlock[9]
rotated[8] = currentBlock[0]
rotated[9] = currentBlock[4]
rotated[10] = currentBlock[8]
sys.memcopy(rotated, currentBlock, len(currentBlock))
}
}
; For movement checking it is not needed to clamp the x/y coordinates,
; because we have to check for brick collisions anyway.
; The full play area is bordered by (in)visible characters that will collide.
; Collision is determined by reading the screen data directly.
sub canRotateCW(ubyte xpos, ubyte ypos) -> ubyte {
rotateCW()
ubyte nocollision = noCollision(xpos, ypos)
rotateCCW()
return nocollision
}
sub canRotateCCW(ubyte xpos, ubyte ypos) -> ubyte {
rotateCCW()
ubyte nocollision = noCollision(xpos, ypos)
rotateCW()
return nocollision
}
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
ubyte @zp i
for i in 15 downto 0 {
if currentBlock[i] and txt.getchr(xpos + (i&3), ypos+i/4)!=32
return false
}
return true
}
sub isGameOver(ubyte xpos, ubyte ypos) -> ubyte {
main.drawBlock(xpos, ypos, 32)
ubyte result = ypos==main.startYpos and not noCollision(xpos, ypos+1)
main.drawBlock(xpos, ypos, 160)
return result
}
sub isLineFull(ubyte ypos) -> ubyte {
ubyte x
for x in main.boardOffsetX to main.boardOffsetX+main.boardWidth-1 {
if txt.getchr(x, ypos)==32
return false
}
return true
}
sub collapse(ubyte ypos) {
while ypos>main.startYpos+1 {
ubyte x
for x in main.boardOffsetX+main.boardWidth-1 downto main.boardOffsetX {
ubyte char = txt.getchr(x, ypos-1)
ubyte color = txt.getclr(x, ypos-1)
txt.setcc(x, ypos, char, color)
}
ypos--
}
}
}
sound {
; TODO stubs for now
sub init() {
; c64.MVOL = 15
}
sub blockrotate() {
; soft click
; c64.MVOL = 5
; c64.AD1 = %00100010
; c64.SR1 = %00000000
; c64.FREQ1 = 15600
; c64.CR1 = %10000000
; c64.CR1 = %10000001
}
sub blockdrop() {
; swish
; c64.MVOL = 5
; c64.AD1 = %01010111
; c64.SR1 = %00000000
; c64.FREQ1 = 4600
; c64.CR1 = %10000000
; c64.CR1 = %10000001
}
sub swapping() {
; beep
; c64.MVOL = 8
; c64.AD1 = %01010111
; c64.SR1 = %00000000
; c64.FREQ1 = 5500
; c64.CR1 = %00010000
; c64.CR1 = %00010001
}
sub lineclear() {
; explosion
; c64.MVOL = 15
; c64.AD1 = %01100110
; c64.SR1 = %00000000
; c64.FREQ1 = 1600
; c64.CR1 = %10000000
; c64.CR1 = %10000001
}
sub lineclear_big() {
; big explosion
; c64.MVOL = 15
; c64.AD1 = %01101010
; c64.SR1 = %00000000
; c64.FREQ1 = 2600
; c64.CR1 = %10000000
; c64.CR1 = %10000001
}
sub gameover() {
; buzz
; c64.MVOL = 15
; c64.FREQ2 = 600
; c64.AD2 = %00111010
; c64.SR2 = %00000000
; c64.CR2 = %00110000
; c64.CR2 = %00110001
}
}

View File

@ -7,8 +7,19 @@
main { main {
sub start() { sub start() {
gfx2.screen_mode(128) gfx2.screen_mode(5)
; demo1()
; sys.wait(3*60)
demo2()
gfx2.screen_mode(0)
txt.print("done!\n")
test_stack.test()
}
sub demo1() {
uword pixels = memory("pixels", 320) uword pixels = memory("pixels", 320)
uword yy = 10 uword yy = 10
uword xx uword xx
@ -124,21 +135,12 @@ main {
} }
xx+=4 xx+=4
} }
sys.wait(3*60)
demo2()
gfx2.screen_mode(255)
txt.print("done!\n")
test_stack.test()
} }
sub demo2 () { sub demo2 () {
gfx2.text_charset(3) gfx2.text_charset(3)
ubyte[] modes = [1, 0, 128] ubyte[] modes = [4, 1, 5]
ubyte mode ubyte mode
for mode in modes { for mode in modes {
gfx2.screen_mode(mode) gfx2.screen_mode(mode)

View File

@ -1,6 +1,5 @@
%import textio %import textio
%import syslib %import syslib
%import test_stack
%zeropage basicsafe %zeropage basicsafe
; Note: this program is compatible with C64 and CX16. ; Note: this program is compatible with C64 and CX16.
@ -8,7 +7,7 @@
main { main {
sub start() { sub start() {
txt.print("mid-point\ncircle\n and\nbresenham\nline\nalgorithms.\n") txt.print("rectangles\nand circle\ndrawing.\n")
ubyte r ubyte r
for r in 3 to 12 step 3 { for r in 3 to 12 step 3 {
@ -21,16 +20,6 @@ main {
txt.clear_screen() txt.clear_screen()
disc(20, 12, 12) disc(20, 12, 12)
txt.print("enter for lines:")
void c64.CHRIN()
c64.CHROUT('\n')
txt.clear_screen()
line(1, 10, 38, 24)
line(1, 20, 38, 2)
line(20, 4, 10, 24)
line(39, 16, 12, 0)
txt.print("enter for rectangles:") txt.print("enter for rectangles:")
void c64.CHRIN() void c64.CHRIN()
c64.CHROUT('\n') c64.CHROUT('\n')
@ -41,8 +30,6 @@ main {
rect(10, 10, 10, 10, false) rect(10, 10, 10, 10, false)
rect(6, 0, 16, 20, true) rect(6, 0, 16, 20, true)
test_stack.test()
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) { sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
ubyte x ubyte x
@ -67,46 +54,6 @@ main {
} }
} }
sub line(ubyte x1, ubyte y1, ubyte x2, ubyte y2) {
; Bresenham algorithm, not very optimized to keep clear code.
; For a better optimized version have a look in the graphics.p8 module.
byte d = 0
ubyte dx = abs(x2 - x1)
ubyte dy = abs(y2 - y1)
ubyte dx2 = 2 * dx
ubyte dy2 = 2 * dy
ubyte ix = sgn(x2 as byte - x1 as byte) as ubyte
ubyte iy = sgn(y2 as byte - y1 as byte) as ubyte
ubyte x = x1
ubyte y = y1
if dx >= dy {
repeat {
txt.setcc(x, y, 42, 5)
if x==x2
return
x += ix
d += dy2
if d > dx {
y += iy
d -= dx2
}
}
} else {
repeat {
txt.setcc(x, y, 42, 5)
if y == y2
return
y += iy
d += dx2
if d > dy {
x += ix
d -= dy2
}
}
}
}
sub circle(ubyte xcenter, ubyte ycenter, ubyte radius) { sub circle(ubyte xcenter, ubyte ycenter, ubyte radius) {
; Midpoint algorithm ; Midpoint algorithm
ubyte x = radius ubyte x = radius

View File

@ -214,11 +214,7 @@ waitkey:
sound.lineclear_big() sound.lineclear_big()
else else
sound.lineclear() sound.lineclear()
c64.TIME_LO=0 sys.wait(20) ; slight delay to flash the line
while c64.TIME_LO<20 {
; slight delay to flash the line
}
c64.TIME_LO=0
for linepos in complete_lines for linepos in complete_lines
if linepos and blocklogic.isLineFull(linepos) if linepos and blocklogic.isLineFull(linepos)
blocklogic.collapse(linepos) blocklogic.collapse(linepos)
@ -288,12 +284,10 @@ waitkey:
} }
sub drawBoard() { sub drawBoard() {
c64.CLEARSCR() txt.clear_screen()
txt.color(7) txt.color(7)
txt.plot(1,1) txt.plot(1,1)
txt.print("irmen's") txt.print("* tehtriz *")
txt.plot(2,2)
txt.print("teh▁triz")
txt.color(5) txt.color(5)
txt.plot(6,4) txt.plot(6,4)
txt.print("hold:") txt.print("hold:")

View File

@ -3,8 +3,24 @@
main { main {
sub start() { sub start() {
txt.print("hello\n")
uword ptr = $4000
uword ww
pokew($4000, $98cf)
ww = peekw($4000)
txt.print_uwhex(ww,1)
txt.nl()
pokew(ptr, $98cf)
ww = peekw(ptr)
txt.print_uwhex(ww,1)
txt.nl()
pokew(ptr+2, $1234)
ww = peekw(ptr+2)
txt.print_uwhex(ww,1)
txt.nl()
} }
} }

View File

@ -20,12 +20,12 @@ main {
sub start() { sub start() {
txt.lowercase() txt.lowercase()
txt.print("\u000c\n --- TextElite v1.1 ---\n") txt.print("\u000c\n --- TextElite v1.2 ---\n")
galaxy.travel_to(1, numforLave) galaxy.travel_to(1, numforLave)
market.init(0) ; Lave's market is seeded with 0 market.init(0) ; Lave's market is seeded with 0
ship.init() ship.init()
planet.display(false) planet.display(false, 0)
repeat { repeat {
; test_stack.test() ; test_stack.test()
@ -40,9 +40,9 @@ main {
when input[0] { when input[0] {
'?' -> { '?' -> {
txt.print("\nCommands are:\n"+ txt.print("\nCommands are:\n"+
"buy jump info cash >=save\n"+ "buy jump info map >=save\n"+
"sell teleport market hold <=load\n"+ "sell teleport market cash <=load\n"+
"fuel galhyp local quit\n") "fuel galhyp local hold quit\n")
} }
'q' -> break 'q' -> break
'b' -> trader.do_buy() 'b' -> trader.do_buy()
@ -52,7 +52,12 @@ main {
't' -> trader.do_teleport() 't' -> trader.do_teleport()
'g' -> trader.do_next_galaxy() 'g' -> trader.do_next_galaxy()
'i' -> trader.do_info() 'i' -> trader.do_info()
'm' -> trader.do_show_market() 'm' -> {
if input[1]=='a' and input[2]=='p'
trader.do_map()
else
trader.do_show_market()
}
'l' -> trader.do_local() 'l' -> trader.do_local()
'c' -> trader.do_cash() 'c' -> trader.do_cash()
'h' -> trader.do_hold() 'h' -> trader.do_hold()
@ -112,7 +117,7 @@ trader {
sys.memcopy(&savedata.cargo0, ship.cargohold, len(ship.cargohold)) sys.memcopy(&savedata.cargo0, ship.cargohold, len(ship.cargohold))
galaxy.travel_to(savedata.galaxy, savedata.planet) galaxy.travel_to(savedata.galaxy, savedata.planet)
planet.display(false) planet.display(false, 0)
} }
sub do_save() { sub do_save() {
@ -158,10 +163,10 @@ trader {
galaxy.init_market_for_planet() galaxy.init_market_for_planet()
ship.fuel -= distance ship.fuel -= distance
txt.print("\n\nHyperspace jump! Arrived at:\n") txt.print("\n\nHyperspace jump! Arrived at:\n")
planet.display(true) planet.display(true,0 )
return return
} }
txt.print("Insufficient fuel\n") txt.print("\nInsufficient fuel\n")
} else { } else {
txt.print(" Not found!\n") txt.print(" Not found!\n")
} }
@ -249,8 +254,9 @@ trader {
} }
sub do_next_galaxy() { sub do_next_galaxy() {
txt.print("\n>>>>>>>> Galaxy Hyperjump!\n")
galaxy.travel_to(galaxy.number+1, planet.number) galaxy.travel_to(galaxy.number+1, planet.number)
planet.display(false) planet.display(false, 0)
} }
sub do_info() { sub do_info() {
@ -258,14 +264,17 @@ trader {
num_chars = txt.input_chars(input) num_chars = txt.input_chars(input)
if num_chars { if num_chars {
ubyte current_planet = planet.number ubyte current_planet = planet.number
ubyte x = planet.x
ubyte y = planet.y
if galaxy.search_closest_planet(input) { if galaxy.search_closest_planet(input) {
planet.display(false) ubyte distance = planet.distance(x, y)
planet.display(false, distance)
} else { } else {
txt.print(" Not found!") txt.print(" Not found!")
} }
galaxy.travel_to(galaxy.number, current_planet) galaxy.travel_to(galaxy.number, current_planet)
} else { } else {
planet.display(false) planet.display(false, 0)
} }
} }
@ -273,6 +282,14 @@ trader {
galaxy.local_area() galaxy.local_area()
} }
sub do_map() {
txt.print("\n(l)ocal or (g)alaxy starmap? ")
num_chars = txt.input_chars(input)
if num_chars {
galaxy.starmap(input[0]=='l')
}
}
sub do_show_market() { sub do_show_market() {
market.display() market.display()
txt.print("\nFuel: ") txt.print("\nFuel: ")
@ -358,14 +375,15 @@ market {
util.print_right(13, names[ci]) util.print_right(13, names[ci])
txt.print(" ") txt.print(" ")
util.print_10s(current_price[ci]) util.print_10s(current_price[ci])
txt.print(" ") txt.column(24)
txt.print_ub(current_quantity[ci]) txt.print_ub(current_quantity[ci])
txt.chrout(' ')
when units[ci] { when units[ci] {
0 -> txt.chrout('t') 0 -> txt.chrout('t')
1 -> txt.print("kg") 1 -> txt.print("kg")
2 -> txt.chrout('g') 2 -> txt.chrout('g')
} }
txt.print(" ") txt.column(32)
txt.print_ub(ship.cargohold[ci]) txt.print_ub(ship.cargohold[ci])
txt.nl() txt.nl()
} }
@ -474,10 +492,7 @@ galaxy {
txt.chrout('-') txt.chrout('-')
txt.spc() txt.spc()
planet.name = make_current_planet_name() planet.name = make_current_planet_name()
planet.display(true) planet.display(true, distance)
txt.print(" (")
util.print_10s(distance)
txt.print(" LY)\n")
} }
pn++ pn++
} until pn==0 } until pn==0
@ -485,6 +500,72 @@ galaxy {
travel_to(number, current_planet) travel_to(number, current_planet)
} }
sub starmap(ubyte local) {
ubyte current_planet = planet.number
ubyte px = planet.x
ubyte py = planet.y
uword current_name = planet.name
ubyte pn = 0
uword scaling = 8
if local
scaling = 2
if txt.width() > 60
scaling /= 2
init(number)
txt.clear_screen()
txt.print("Galaxy #")
txt.print_ub(number)
if local
txt.print(" - local systems")
else
txt.print(" - galaxy")
txt.print(" starmap:\n")
ubyte max_distance = 255
if local
max_distance = ship.Max_fuel
do {
generate_next_planet()
ubyte distance = planet.distance(px, py)
if distance <= max_distance {
planet.name = make_current_planet_name()
planet.name[0] |= 32 ; uppercase first letter
uword tx = planet.x
uword ty = planet.y
if local {
tx = tx + 24 - px
ty = ty + 24 - py
}
tx /= scaling
ty /= scaling*2
ubyte sx = lsb(tx)
ubyte sy = lsb(ty)
ubyte char = '*'
if planet.number==current_planet
char = '%'
if local or planet.number==current_planet {
txt.plot(2+sx-2, 2+sy+1)
txt.print(current_name)
if distance {
txt.plot(2+sx-2, 2+sy+2)
util.print_10s(distance)
txt.print(" LY")
}
}
txt.setchr(2+sx, 2+sy, char)
}
pn++
} until pn==0
if txt.width() < 80
txt.plot(0,20)
else
txt.plot(0,36)
travel_to(number, current_planet)
}
ubyte pn_pair1 ubyte pn_pair1
ubyte pn_pair2 ubyte pn_pair2
ubyte pn_pair3 ubyte pn_pair3
@ -838,15 +919,21 @@ planet {
} }
} }
sub display(ubyte compressed) { sub display(ubyte compressed, ubyte distance) {
if compressed { if compressed {
print_name_uppercase() print_name_uppercase()
txt.print(" TL:") if distance {
txt.print(" (")
util.print_10s(distance)
txt.print(" LY)")
}
txt.print(" Tech level:")
txt.print_ub(techlevel+1) txt.print_ub(techlevel+1)
txt.spc() txt.print("\n ")
txt.print(econnames[economy]) txt.print(econnames[economy])
txt.spc() txt.spc()
txt.print(govnames[govtype]) txt.print(govnames[govtype])
txt.nl()
} else { } else {
txt.print("\n\nSystem: ") txt.print("\n\nSystem: ")
print_name_uppercase() print_name_uppercase()
@ -857,6 +944,11 @@ planet {
txt.spc() txt.spc()
txt.chrout('#') txt.chrout('#')
txt.print_ub(number) txt.print_ub(number)
if distance {
txt.print("\nDistance: ")
util.print_10s(distance)
txt.print(" LY")
}
txt.print("\nEconomy: ") txt.print("\nEconomy: ")
txt.print(econnames[economy]) txt.print(econnames[economy])
txt.print("\nGovernment: ") txt.print("\nGovernment: ")

View File

@ -2,7 +2,7 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" version "1.4.21" id "org.jetbrains.kotlin.jvm" version "1.4.30"
id 'com.github.johnrengelman.shadow' version '6.1.0' id 'com.github.johnrengelman.shadow' version '6.1.0'
} }

View File

@ -2,4 +2,5 @@
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out
rm -rf build out rm -rf build out
rm -rf compiler/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build

View File

@ -1,4 +1,5 @@
include ':parser' include ':parser'
include ':compilerAst'
include ':compiler' include ':compiler'
include ':dbusCompilerService' include ':dbusCompilerService'
include ':httpCompilerService' include ':httpCompilerService'

View File

@ -11,10 +11,10 @@
<option name="HAS_PARENS" value="true" /> <option name="HAS_PARENS" value="true" />
<option name="HAS_STRING_ESCAPES" value="true" /> <option name="HAS_STRING_ESCAPES" value="true" />
</options> </options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" /> <keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" /> <keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" /> <keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;offsetof;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" /> <keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;offsetof;peek;peekw;poke;pokew;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
</highlighting> </highlighting>
<extensionMap> <extensionMap>
<mapping ext="p8" /> <mapping ext="p8" />