mirror of
https://github.com/irmen/prog8.git
synced 2025-06-17 16:23:42 +00:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
53f0318187 | |||
5e6e711f33 | |||
78af2cd4dc | |||
02cb237623 | |||
cc0f19653e | |||
4fff150c7b | |||
f6136891cc | |||
1e22170302 | |||
bdda6f502a | |||
1bbd77fddb | |||
321fdd10d1 | |||
9867dfcdeb | |||
7c09ac632c | |||
3502f65332 | |||
628390c3b5 | |||
bc37097df2 | |||
7d98275763 | |||
d6ffb549f6 | |||
bcd0db984d | |||
d9244f22c2 | |||
c97d76dbf2 | |||
9e05e97d7f | |||
1070dedd7c | |||
ccd1516637 | |||
f1f51a01c6 | |||
be75b8dbe5 | |||
02fae0e722 | |||
e35b739579 | |||
34aa6cc8a2 | |||
d7a6b20028 | |||
eb2d5bb1f8 | |||
cefef3d1be | |||
cc96ab7a9b | |||
49ea31c0a4 | |||
f1478d776b | |||
40e4cfb686 | |||
76f459ee95 | |||
c478718019 | |||
c27248a58b | |||
51bc539468 | |||
2395863e7e | |||
69c459c8ac | |||
c8855b2b10 | |||
a910c0fddb | |||
fd55611cac | |||
52f6be2bb0 | |||
857f930dc2 | |||
dd2c436dc6 | |||
9f047ba752 |
7
.idea/inspectionProfiles/Project_Default.xml
generated
7
.idea/inspectionProfiles/Project_Default.xml
generated
@ -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
1
.idea/modules.xml
generated
@ -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" />
|
||||||
|
@ -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'
|
||||||
|
@ -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>
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
@ -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
|
||||||
|
@ -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++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -1071,3 +1071,4 @@ sign_extend_AY_byte .proc
|
|||||||
pla
|
pla
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
6.0
|
6.1
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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,22 +56,265 @@ 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 {
|
||||||
return if (filename.startsWith("library:")) {
|
return if (filename.startsWith("library:")) {
|
||||||
val resource = tryGetEmbeddedResource(filename.substring(8))
|
val resource = tryGetEmbeddedResource(filename.substring(8))
|
||||||
|
@ -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,
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
@ -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()) {
|
@ -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) {
|
@ -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() {
|
@ -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() {
|
@ -1,4 +1,4 @@
|
|||||||
package prog8.ast.processing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
@ -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
|
@ -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)
|
73
compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt
Normal file
73
compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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"
|
@ -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.*
|
||||||
|
|
||||||
|
|
||||||
@ -131,6 +132,10 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
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) {
|
@ -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)
|
|
||||||
}
|
|
123
compiler/src/prog8/compiler/target/ICompilationTarget.kt
Normal file
123
compiler/src/prog8/compiler/target/ICompilationTarget.kt
Normal 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)
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
|
@ -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 +
|
||||||
|
@ -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) {
|
||||||
@ -274,10 +291,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} 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)
|
||||||
|
@ -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 -> {
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,10 +1456,19 @@ 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 {
|
||||||
|
if (regs !in Cx16VirtualRegisters) {
|
||||||
when (regs) {
|
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")
|
||||||
@ -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)")
|
||||||
|
@ -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")
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
65
compilerAst/build.gradle
Normal 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'
|
||||||
|
}
|
14
compilerAst/compilerAst.iml
Normal file
14
compilerAst/compilerAst.iml
Normal 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>
|
@ -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)
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -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("")
|
|
||||||
}
|
|
49
compilerAst/src/prog8/ast/antlr/EscapeChars.kt
Normal file
49
compilerAst/src/prog8/ast/antlr/EscapeChars.kt
Normal 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("")
|
||||||
|
}
|
@ -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 ****************************/
|
||||||
@ -56,16 +55,6 @@ enum class DataType {
|
|||||||
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
|
@ -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())
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
@ -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)
|
@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
207
examples/cx16/amiga.p8
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
642
examples/cx16/tehtriz.p8
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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:")
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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: ")
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
include ':parser'
|
include ':parser'
|
||||||
|
include ':compilerAst'
|
||||||
include ':compiler'
|
include ':compiler'
|
||||||
include ':dbusCompilerService'
|
include ':dbusCompilerService'
|
||||||
include ':httpCompilerService'
|
include ':httpCompilerService'
|
||||||
|
@ -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="&;->;@;\$;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="&;->;@;\$;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" />
|
||||||
|
Reference in New Issue
Block a user