mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6079dfd71 | ||
|
|
2b435fe6a5 | ||
|
|
4e640b11fd | ||
|
|
8b1e1e68fa | ||
|
|
fd11927708 | ||
|
|
cd500fee8c | ||
|
|
1bd32c0f19 | ||
|
|
7aefca3de0 | ||
|
|
f275ed96ea | ||
|
|
d14dac3872 | ||
|
|
b0213b0565 | ||
|
|
c677f0a875 | ||
|
|
6e65cb2c0a | ||
|
|
e65c5402d7 | ||
|
|
334f86480a | ||
|
|
0e62f5b759 | ||
|
|
edf9a500d3 | ||
|
|
001d01fdaf | ||
|
|
a95677564e | ||
|
|
4aca8bb8df | ||
|
|
5540482888 | ||
|
|
00d735249b | ||
|
|
b5289511ba | ||
|
|
b6ded8501f | ||
|
|
781915d2cf | ||
|
|
f4cef3eaf2 | ||
|
|
d23c2eed86 | ||
|
|
15695a304e | ||
|
|
6319269976 | ||
|
|
0ed3d951a7 | ||
|
|
2aa39757b4 | ||
|
|
39d32a3600 | ||
|
|
219d17de34 | ||
|
|
9bb5b454e4 | ||
|
|
2412f8c531 | ||
|
|
8701d684e6 | ||
|
|
b543cc34cd | ||
|
|
791dbbab9b | ||
|
|
ac0b1da3fc | ||
|
|
2f97aedc3c | ||
|
|
ab544ee965 | ||
|
|
fa527f8624 | ||
|
|
92ee0aefee | ||
|
|
99759ae853 | ||
|
|
81930312ff | ||
|
|
194fbcdd91 | ||
|
|
1e3930aae2 | ||
|
|
62dda4d891 | ||
|
|
2b870fb9f7 |
No files matched your search
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Kotlin2JvmCompilerArguments">
|
||||
<option name="jvmTarget" value="1.8" />
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -52,7 +52,7 @@ What does Prog8 provide?
|
||||
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502)
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
|
||||
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ dependencies {
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
useIR = true
|
||||
// verbose = true
|
||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||
}
|
||||
@@ -42,6 +43,7 @@ compileKotlin {
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
useIR = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ c64 {
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
&ubyte STATUS = $90 ; kernel status variable for I/O
|
||||
&ubyte STATUS = $90 ; kernal status variable for I/O
|
||||
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
|
||||
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
|
||||
|
||||
@@ -178,7 +178,7 @@ c64 {
|
||||
|
||||
; ---- C64 ROM kernal routines ----
|
||||
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
@@ -294,6 +294,12 @@ asmsub init_system() {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
rts ; no phase 2 steps on the C64
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||
%asm {{
|
||||
lda #$80
|
||||
@@ -304,27 +310,13 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irqvec_excl() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irqvec() clobbers(A) {
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
@@ -333,9 +325,23 @@ asmsub set_irqvec() clobbers(A) {
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr _irq_handler_init
|
||||
jsr irq.irq
|
||||
_modified jsr $ffff ; modified
|
||||
jsr _irq_handler_end
|
||||
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
||||
lda _use_kernal
|
||||
bne +
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
@@ -388,7 +394,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_irqvec() clobbers(A) {
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
@@ -404,8 +410,15 @@ asmsub restore_irqvec() clobbers(A) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta set_irq._use_kernal
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
@@ -416,12 +429,21 @@ asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFRT
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda set_irq._use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
+ jmp c64.IRQDFRT ; continue with kernal irq routine
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
@@ -445,28 +467,6 @@ _setup_raster_irq
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
jsr set_rasterirq._setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C64 specific system utility routines ----
|
||||
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ _colormod sta $ffff ; modified
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
|
||||
@@ -257,7 +257,7 @@ asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1)
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp #'$'
|
||||
beq _hex
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
; Bitmap pixel graphics routines for the CommanderX16
|
||||
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
||||
; (These modes are not supported by the documented GRAPH_xxxx kernel routines)
|
||||
; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
|
||||
;
|
||||
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
||||
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
|
||||
@@ -15,13 +15,12 @@
|
||||
; 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 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
|
||||
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
|
||||
; mode 4 = bitmap 320 x 240 x 256c
|
||||
; mode 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)
|
||||
; mode 6 = bitmap 640 x 480 x 4c
|
||||
; higher color dephts in highres are not supported 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?
|
||||
|
||||
@@ -85,7 +84,6 @@ gfx2 {
|
||||
height = 480
|
||||
bpp = 2
|
||||
}
|
||||
; modes 7 and 8 not supported due to lack of VRAM
|
||||
else -> {
|
||||
; back to default text mode and colors
|
||||
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
||||
@@ -168,7 +166,7 @@ gfx2 {
|
||||
if separate_pixels as uword > length
|
||||
separate_pixels = lsb(length)
|
||||
repeat separate_pixels {
|
||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
@@ -210,7 +208,7 @@ _loop lda length
|
||||
_done
|
||||
}}
|
||||
repeat separate_pixels {
|
||||
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size
|
||||
; TODO optimize this by writing a masked byte in 1 go
|
||||
plot(x, y, color)
|
||||
x++
|
||||
}
|
||||
@@ -299,6 +297,7 @@ _done
|
||||
1, 5 -> {
|
||||
; monochrome, either resolution
|
||||
; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
||||
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
|
||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
if active_mode>=5
|
||||
@@ -326,7 +325,7 @@ _done
|
||||
}
|
||||
} else {
|
||||
; stippling.
|
||||
height = (height+1)/2
|
||||
height = (height+1)/2 ; TODO is the line sometimes 1 pixel too long now because of rounding?
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
@@ -375,7 +374,8 @@ _done
|
||||
lda cx16.VERA_ADDR_M
|
||||
adc #0
|
||||
sta cx16.VERA_ADDR_M
|
||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
||||
; the bitmap size is small enough to not have to deal with the _H part:
|
||||
; lda cx16.VERA_ADDR_H
|
||||
; adc #0
|
||||
; sta cx16.VERA_ADDR_H
|
||||
}}
|
||||
@@ -399,6 +399,7 @@ _done
|
||||
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.
|
||||
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
|
||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||
; 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)
|
||||
@@ -812,6 +813,7 @@ _done
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
||||
repeat 8 {
|
||||
; TODO rewrite this inner loop fully in assembly
|
||||
position(x,y)
|
||||
y++
|
||||
%asm {{
|
||||
@@ -840,7 +842,7 @@ _done
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
repeat 8 {
|
||||
; TODO rewrite this inner loop in assembly
|
||||
; TODO rewrite this inner loop fully in assembly
|
||||
ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
|
||||
repeat 8 {
|
||||
charbits <<= 1
|
||||
@@ -877,15 +879,31 @@ _done
|
||||
}}
|
||||
}
|
||||
|
||||
sub addr_mul_24_for_highres_4c(uword yy, uword xx) {
|
||||
; TODO turn into asmsub
|
||||
asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
|
||||
; yy * 160 + xx/4 (24 bits calculation)
|
||||
; 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)
|
||||
ldy #5
|
||||
- asl cx16.r2
|
||||
rol cx16.r2+1
|
||||
dey
|
||||
bne -
|
||||
lda cx16.r2
|
||||
sta cx16.r0
|
||||
lda cx16.r2+1
|
||||
sta cx16.r0+1
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
|
||||
; xx >>= 2 (xx=R3)
|
||||
lsr cx16.r3+1
|
||||
ror cx16.r3
|
||||
lsr cx16.r3+1
|
||||
ror cx16.r3
|
||||
|
||||
; add r2 and xx (r3) to r0 (24-bits)
|
||||
stz cx16.r1
|
||||
clc
|
||||
lda cx16.r0
|
||||
@@ -898,60 +916,61 @@ _done
|
||||
inc cx16.r1
|
||||
+ clc
|
||||
lda cx16.r0
|
||||
adc xx
|
||||
adc cx16.r3
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc xx+1
|
||||
adc cx16.r3+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
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 {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0+1
|
||||
sta cx16.r1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda cx16.r0
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
sta cx16.r0
|
||||
lda P8ZP_SCRATCH_B1
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ ; now add the value to this 24-bits number
|
||||
lda cx16.r0
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ lda cx16.r1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
; yy * 320 + xx (24 bits calculation)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0+1
|
||||
sta cx16.r1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda cx16.r0
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
sta cx16.r0
|
||||
lda P8ZP_SCRATCH_B1
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ ; now add the value to this 24-bits number
|
||||
lda cx16.r0
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W1
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc P8ZP_SCRATCH_W1+1
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+ lda cx16.r1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,8 +9,10 @@ palette {
|
||||
ubyte c
|
||||
|
||||
sub set_color(ubyte index, uword color) {
|
||||
cx16.vpoke(1, $fa00+index*2, lsb(color))
|
||||
cx16.vpoke(1, $fa01+index*2, msb(color))
|
||||
vera_palette_ptr = $fa00+index*2
|
||||
cx16.vpoke(1, vera_palette_ptr, lsb(color))
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||
}
|
||||
|
||||
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
||||
@@ -98,7 +100,7 @@ palette {
|
||||
$666, ; 12 = medium grey
|
||||
$9D8, ; 13 = light green
|
||||
$65B, ; 14 = light blue
|
||||
$999 ; 15 = light grey
|
||||
$999 ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||
@@ -117,7 +119,7 @@ palette {
|
||||
$777, ; 12 = medium grey
|
||||
$af9, ; 13 = light green
|
||||
$76e, ; 14 = light blue
|
||||
$bbb ; 15 = light grey
|
||||
$bbb ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] C64_colorpalette_light = [ ; this is a lighter palette
|
||||
|
||||
@@ -102,10 +102,11 @@ asmsub MEMTOP2() -> ubyte @A {
|
||||
|
||||
cx16 {
|
||||
|
||||
; 65c02 hardware vectors:
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
; irq and hardware vectors:
|
||||
&uword CINV = $0314 ; IRQ vector (in ram)
|
||||
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
|
||||
; the sixteen virtual 16-bit registers
|
||||
@@ -453,7 +454,7 @@ _loop ldy #0
|
||||
}
|
||||
|
||||
; ---- system stuff -----
|
||||
asmsub init_system() {
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
%asm {{
|
||||
@@ -461,7 +462,7 @@ asmsub init_system() {
|
||||
cld
|
||||
;stz $00
|
||||
;stz $01
|
||||
;stz d1prb ; select rom bank 0
|
||||
;stz d1prb ; select rom bank 0 (enable kernal)
|
||||
lda #$80
|
||||
sta VERA_CTRL
|
||||
jsr c64.IOINIT
|
||||
@@ -485,8 +486,177 @@ asmsub init_system() {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub init_system_phase2() {
|
||||
%asm {{
|
||||
sei
|
||||
lda cx16.CINV
|
||||
sta restore_irq._orig_irqvec
|
||||
lda cx16.CINV+1
|
||||
sta restore_irq._orig_irqvec+1
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda #0
|
||||
adc #0
|
||||
sta _use_kernal
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta cx16.CINV
|
||||
lda #>_irq_handler
|
||||
sta cx16.CINV+1
|
||||
lda cx16.VERA_IEN
|
||||
ora #%00000001 ; enable the vsync irq
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
rts
|
||||
|
||||
_irq_handler jsr _irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr _irq_handler_end
|
||||
lda _use_kernal
|
||||
bne +
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
lda cx16.VERA_ISR
|
||||
ora #1
|
||||
sta cx16.VERA_ISR ; clear Vera Vsync irq status
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
+ jmp (restore_irq._orig_irqvec) ; continue with normal kernal irq routine
|
||||
|
||||
_use_kernal .byte 0
|
||||
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
stx IRQ_X_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta IRQ_SCRATCH_ZPB1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta IRQ_SCRATCH_ZPREG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta IRQ_SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta IRQ_SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
cld
|
||||
rts
|
||||
|
||||
_irq_handler_end
|
||||
; restore all zp scratch registers and the X register
|
||||
lda IRQ_SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda IRQ_SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda IRQ_SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda IRQ_SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda IRQ_SCRATCH_ZPWORD2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda IRQ_SCRATCH_ZPWORD2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx IRQ_X_REG
|
||||
rts
|
||||
|
||||
IRQ_X_REG .byte 0
|
||||
IRQ_SCRATCH_ZPB1 .byte 0
|
||||
IRQ_SCRATCH_ZPREG .byte 0
|
||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda _orig_irqvec
|
||||
sta cx16.CINV
|
||||
lda _orig_irqvec+1
|
||||
sta cx16.CINV+1
|
||||
lda cx16.VERA_IEN
|
||||
and #%11110000 ; disable all Vera IRQs
|
||||
ora #%00000001 ; enable only the vsync Irq
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
rts
|
||||
_orig_irqvec .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
sei
|
||||
lda cx16.VERA_IEN
|
||||
and #%11110000 ; clear other IRQs
|
||||
ora #%00000010 ; enable the line (raster) irq
|
||||
sta cx16.VERA_IEN
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr set_rasterline
|
||||
lda #<_raster_irq_handler
|
||||
sta cx16.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cx16.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irq._irq_handler_init
|
||||
_modified jsr $ffff ; modified
|
||||
jsr set_irq._irq_handler_end
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
lda cx16.VERA_ISR
|
||||
ora #%00000010
|
||||
sta cx16.VERA_ISR ; clear Vera line irq status
|
||||
ply
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
%asm {{
|
||||
sta cx16.VERA_IRQ_LINE_L
|
||||
lda cx16.VERA_IEN
|
||||
and #%01111111
|
||||
sta cx16.VERA_IEN
|
||||
tya
|
||||
lsr a
|
||||
ror a
|
||||
and #%10000000
|
||||
ora cx16.VERA_IEN
|
||||
sta cx16.VERA_IEN
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
|
||||
@@ -626,6 +626,8 @@ asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
|
||||
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
|
||||
; ---- set the color in A on the screen matrix at the given position
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color!
|
||||
%asm {{
|
||||
pha
|
||||
txa
|
||||
@@ -657,6 +659,8 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: color handling is the same as on the C64: it only sets the foreground color.
|
||||
; use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors.
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
@@ -685,8 +689,35 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
|
||||
; use the high nybble in A to set the Bg color!
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda char
|
||||
sta cx16.VERA_DATA0
|
||||
inx
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda colors
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
; ---- safe wrapper around PLOT kernal routine, to save the X register.
|
||||
%asm {{
|
||||
phx
|
||||
tax
|
||||
|
||||
@@ -1 +1 @@
|
||||
6.1
|
||||
6.2
|
||||
@@ -8,7 +8,6 @@ import prog8.ast.base.AstException
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.compileProgram
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.parser.ParsingFailedError
|
||||
import java.nio.file.FileSystems
|
||||
@@ -117,7 +116,7 @@ private fun compileMain(args: Array<String>) {
|
||||
if (compilationResult.programName.isEmpty())
|
||||
println("\nCan't start emulator because no program was assembled.")
|
||||
else {
|
||||
ICompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
|
||||
compilationResult.compTarget.machine.launchEmulator(compilationResult.programName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
|
||||
|
||||
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
@@ -107,7 +107,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
}
|
||||
|
||||
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||
// and if an assembly block doesn't contain a rts/rti, and some other situations.
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val returnStmt = Return(null, subroutine.position)
|
||||
|
||||
@@ -2,6 +2,7 @@ package prog8.compiler
|
||||
|
||||
import prog8.ast.AstToSourceCode
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.IMemSizer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.base.Position
|
||||
@@ -48,7 +49,8 @@ data class CompilationOptions(val output: OutputType,
|
||||
val zeropage: ZeropageType,
|
||||
val zpReserved: List<IntRange>,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean) {
|
||||
val noSysInit: Boolean,
|
||||
val compTarget: ICompilationTarget) {
|
||||
var slowCodegenWarnings = false
|
||||
var optimize = false
|
||||
}
|
||||
@@ -59,6 +61,7 @@ class CompilerException(message: String?) : Exception(message)
|
||||
class CompilationResult(val success: Boolean,
|
||||
val programAst: Program,
|
||||
val programName: String,
|
||||
val compTarget: ICompilationTarget,
|
||||
val importedFiles: List<Path>)
|
||||
|
||||
|
||||
@@ -73,27 +76,28 @@ fun compileProgram(filepath: Path,
|
||||
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)
|
||||
val compTarget =
|
||||
when(compilationTarget) {
|
||||
C64Target.name -> C64Target
|
||||
Cx16Target.name -> 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)
|
||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget)
|
||||
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
||||
compilationOptions.optimize = optimize
|
||||
programAst = ast
|
||||
importedFiles = imported
|
||||
processAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
|
||||
processAst(programAst, errors, compilationOptions)
|
||||
if (compilationOptions.optimize)
|
||||
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions))
|
||||
postprocessAst(programAst, errors, compilationOptions, ICompilationTarget.instance)
|
||||
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
|
||||
postprocessAst(programAst, errors, compilationOptions)
|
||||
|
||||
// printAst(programAst)
|
||||
|
||||
@@ -103,7 +107,7 @@ fun compileProgram(filepath: Path,
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||
return CompilationResult(true, programAst, programName, importedFiles)
|
||||
return CompilationResult(true, programAst, programName, compTarget, importedFiles)
|
||||
|
||||
} catch (px: ParsingFailedError) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
@@ -127,8 +131,8 @@ fun compileProgram(filepath: Path,
|
||||
throw x
|
||||
}
|
||||
|
||||
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions))
|
||||
return CompilationResult(false, failedProgram, programName, emptyList())
|
||||
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
|
||||
return CompilationResult(false, failedProgram, programName, compTarget, emptyList())
|
||||
}
|
||||
|
||||
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
|
||||
@@ -137,13 +141,13 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
||||
override val names = functions.keys
|
||||
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
|
||||
|
||||
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? {
|
||||
override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? {
|
||||
val func = BuiltinFunctions[name]
|
||||
if(func!=null) {
|
||||
val exprfunc = func.constExpressionFunc
|
||||
if(exprfunc!=null) {
|
||||
return try {
|
||||
exprfunc(args, position, program)
|
||||
exprfunc(args, position, program, memsizer)
|
||||
} catch(x: NotConstArgumentException) {
|
||||
// const-evaluating the builtin function call failed.
|
||||
null
|
||||
@@ -161,34 +165,34 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
||||
builtinFunctionReturnType(name, args, program)
|
||||
}
|
||||
|
||||
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
||||
val compilationTargetName = ICompilationTarget.instance.name
|
||||
private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICompilationTarget): Triple<Program, CompilationOptions, List<Path>> {
|
||||
val compilationTargetName = compTarget.name
|
||||
println("Compiler target: $compilationTargetName. Parsing...")
|
||||
val importer = ModuleImporter()
|
||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf)
|
||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf, compTarget)
|
||||
bf.program = programAst
|
||||
importer.importModule(programAst, filepath, ICompilationTarget.instance, compilationTargetName)
|
||||
errors.handle()
|
||||
importer.importModule(programAst, filepath, compTarget, compilationTargetName)
|
||||
errors.report()
|
||||
|
||||
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
|
||||
|
||||
val compilerOptions = determineCompilationOptions(programAst)
|
||||
val compilerOptions = determineCompilationOptions(programAst, compTarget)
|
||||
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)
|
||||
for(lib in compTarget.machine.importLibs(compilerOptions, compilationTargetName))
|
||||
importer.importLibraryModule(programAst, lib, compTarget, compilationTargetName)
|
||||
|
||||
// always import prog8_lib and math
|
||||
importer.importLibraryModule(programAst, "math", ICompilationTarget.instance, compilationTargetName)
|
||||
importer.importLibraryModule(programAst, "prog8_lib", ICompilationTarget.instance, compilationTargetName)
|
||||
errors.handle()
|
||||
importer.importLibraryModule(programAst, "math", compTarget, compilationTargetName)
|
||||
importer.importLibraryModule(programAst, "prog8_lib", compTarget, compilationTargetName)
|
||||
errors.report()
|
||||
return Triple(programAst, compilerOptions, importedFiles)
|
||||
}
|
||||
|
||||
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
val mainModule = program.modules.first()
|
||||
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||
val mainModule = program.mainModule
|
||||
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" }
|
||||
@@ -209,7 +213,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
|
||||
if (zpType==ZeropageType.FLOATSAFE && ICompilationTarget.instance.name == Cx16Target.name) {
|
||||
if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
|
||||
zpType = ZeropageType.BASICSAFE
|
||||
}
|
||||
@@ -233,78 +237,81 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled, noSysInit
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
)
|
||||
}
|
||||
|
||||
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
|
||||
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
// perform initial syntax checks and processings
|
||||
println("Processing for target ${compTarget.name}...")
|
||||
programAst.checkIdentifiers(errors, compTarget)
|
||||
errors.handle()
|
||||
programAst.constantFold(errors)
|
||||
errors.handle()
|
||||
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.constantFold(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.reorderStatements(errors)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
programAst.addTypecasts(errors)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
programAst.variousCleanups()
|
||||
programAst.checkValid(compilerOptions, errors, compTarget)
|
||||
errors.handle()
|
||||
programAst.checkIdentifiers(errors, compTarget)
|
||||
errors.handle()
|
||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
}
|
||||
|
||||
private fun optimizeAst(programAst: Program, errors: ErrorReporter, functions: IBuiltinFunctions) {
|
||||
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||
// 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()
|
||||
val optsDone2 = programAst.splitBinaryExpressions(compTarget)
|
||||
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget, ::loadAsmIncludeFile)
|
||||
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
errors.report()
|
||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||
break
|
||||
}
|
||||
|
||||
val remover = UnusedCodeRemover(programAst, errors)
|
||||
val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
|
||||
remover.visit(programAst)
|
||||
remover.applyModifications()
|
||||
errors.handle()
|
||||
errors.report()
|
||||
}
|
||||
|
||||
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) {
|
||||
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
programAst.addTypecasts(errors)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
programAst.variousCleanups()
|
||||
programAst.checkValid(compilerOptions, errors, compTarget) // check if final tree is still valid
|
||||
errors.handle()
|
||||
val callGraph = CallGraph(programAst)
|
||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
||||
errors.report()
|
||||
val callGraph = CallGraph(programAst, ::loadAsmIncludeFile)
|
||||
callGraph.checkRecursiveCalls(errors)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
programAst.verifyFunctionArgTypes()
|
||||
programAst.moveMainAndStartToFirst()
|
||||
}
|
||||
|
||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||
private fun writeAssembly(programAst: Program,
|
||||
errors: IErrorReporter,
|
||||
outputDir: Path,
|
||||
compilerOptions: CompilationOptions): String {
|
||||
// asm generation directly from the Ast,
|
||||
programAst.processAstBeforeAsmGeneration(errors, ICompilationTarget.instance)
|
||||
errors.handle()
|
||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
|
||||
// printAst(programAst)
|
||||
|
||||
ICompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
||||
val assembly = asmGeneratorFor(ICompilationTarget.instance,
|
||||
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
||||
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
||||
programAst,
|
||||
errors,
|
||||
ICompilationTarget.instance.machine.zeropage,
|
||||
compilerOptions.compTarget.machine.zeropage,
|
||||
compilerOptions,
|
||||
outputDir).compileToAssembly()
|
||||
assembly.assemble(compilerOptions)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
return assembly.name
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,16 @@ package prog8.compiler
|
||||
import prog8.ast.base.Position
|
||||
import prog8.parser.ParsingFailedError
|
||||
|
||||
class ErrorReporter {
|
||||
|
||||
interface IErrorReporter {
|
||||
fun err(msg: String, position: Position)
|
||||
fun warn(msg: String, position: Position)
|
||||
fun isEmpty(): Boolean
|
||||
fun report()
|
||||
}
|
||||
|
||||
|
||||
internal class ErrorReporter: IErrorReporter {
|
||||
private enum class MessageSeverity {
|
||||
WARNING,
|
||||
ERROR
|
||||
@@ -13,10 +22,14 @@ class ErrorReporter {
|
||||
private val messages = mutableListOf<CompilerMessage>()
|
||||
private val alreadyReportedMessages = mutableSetOf<String>()
|
||||
|
||||
fun err(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
||||
fun warn(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
||||
override fun err(msg: String, position: Position) {
|
||||
messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
||||
}
|
||||
override fun warn(msg: String, position: Position) {
|
||||
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
||||
}
|
||||
|
||||
fun handle() {
|
||||
override fun report() {
|
||||
var numErrors = 0
|
||||
var numWarnings = 0
|
||||
messages.forEach {
|
||||
@@ -40,5 +53,5 @@ class ErrorReporter {
|
||||
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
||||
}
|
||||
|
||||
fun isEmpty() = messages.isEmpty()
|
||||
override fun isEmpty() = messages.isEmpty()
|
||||
}
|
||||
@@ -21,7 +21,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||
|
||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int {
|
||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
||||
|
||||
if(options.zeropage==ZeropageType.DONTUSE)
|
||||
|
||||
@@ -8,7 +8,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.functions.BuiltinFunctions
|
||||
import prog8.compiler.functions.builtinFunctionReturnType
|
||||
import prog8.compiler.target.C64Target
|
||||
@@ -18,7 +18,7 @@ import java.io.File
|
||||
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions,
|
||||
private val errors: ErrorReporter,
|
||||
private val errors: IErrorReporter,
|
||||
private val compTarget: ICompilationTarget
|
||||
) : IAstVisitor {
|
||||
|
||||
@@ -41,19 +41,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// there can be an optional single 'irq' block with a 'irq' subroutine in it,
|
||||
// which will be used as the 60hz irq routine in the vm if it's present
|
||||
val irqBlocks = program.modules.flatMap { it.statements }.filter { it is Block && it.name=="irq" }.map { it as Block }
|
||||
if(irqBlocks.size>1)
|
||||
errors.err("more than one 'irq' block", irqBlocks[0].position)
|
||||
for(irqBlock in irqBlocks) {
|
||||
val irqSub = irqBlock.subScope("irq") as? Subroutine
|
||||
if (irqSub != null) {
|
||||
if (irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
|
||||
errors.err("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(program)
|
||||
}
|
||||
|
||||
@@ -187,7 +174,7 @@ internal class AstChecker(private val program: Program,
|
||||
else -> false
|
||||
}
|
||||
if (!ok) {
|
||||
errors.err("statement occurs in a block, where it will never be executed. Use it in a subroutine instead.", statement.position)
|
||||
errors.err("non-declarative statement occurs in block scope, where it will never be executed. Move it to a subroutine instead.", statement.position)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -351,10 +338,6 @@ internal class AstChecker(private val program: Program,
|
||||
if(statusFlagsNoCarry.isNotEmpty())
|
||||
err("can only use Carry as status flag parameter")
|
||||
|
||||
val carryParameter = subroutine.asmParameterRegisters.singleOrNull { it.statusflag==Statusflag.Pc }
|
||||
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
|
||||
err("carry parameter has to come last")
|
||||
|
||||
} else {
|
||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||
// Instead, their reference (address) should be passed (as an UWORD).
|
||||
@@ -756,7 +739,7 @@ internal class AstChecker(private val program: Program,
|
||||
err("this directive may only occur in a block or at module level")
|
||||
if(directive.args.isEmpty())
|
||||
err("missing option directive argument(s)")
|
||||
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it })
|
||||
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
|
||||
err("invalid option directive argument(s)")
|
||||
}
|
||||
"%target" -> {
|
||||
@@ -1145,12 +1128,18 @@ internal class AstChecker(private val program: Program,
|
||||
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(conditionType !in IntegerDatatypes)
|
||||
errors.err("when condition must be an integer value", whenStatement.position)
|
||||
val choiceValues = whenStatement.choiceValues(program)
|
||||
val occurringValues = choiceValues.map {it.first}
|
||||
val tally = choiceValues.associate { it.second to occurringValues.count { ov->it.first==ov} }
|
||||
tally.filter { it.value>1 }.forEach {
|
||||
errors.err("choice value occurs multiple times", it.key.position)
|
||||
val tally = mutableSetOf<Int>()
|
||||
for((choices, choiceNode) in whenStatement.choiceValues(program)) {
|
||||
if(choices!=null) {
|
||||
for (c in choices) {
|
||||
if(c in tally)
|
||||
errors.err("choice value already occurs earlier", choiceNode.position)
|
||||
else
|
||||
tally.add(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(whenStatement.choices.isEmpty())
|
||||
errors.err("empty when statement", whenStatement.position)
|
||||
|
||||
@@ -1361,7 +1350,7 @@ internal class AstChecker(private val program: Program,
|
||||
val array = value.value.map {
|
||||
when (it) {
|
||||
is NumericLiteralValue -> it.number.toInt()
|
||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||
is AddressOf -> it.identifier.hashCode() and 0xffff
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
val cast = constVal?.cast(it.type)
|
||||
@@ -1420,7 +1409,10 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
false
|
||||
}
|
||||
else -> errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||
else -> {
|
||||
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
if(result)
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
|
||||
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter, compTarget: ICompilationTarget) {
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val checker = AstChecker(this, compilerOptions, errors, compTarget)
|
||||
checker.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter, compTarget: ICompilationTarget) {
|
||||
internal fun Program.processAstBeforeAsmGeneration(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
|
||||
fixer.visit(this)
|
||||
fixer.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Program.reorderStatements(errors: ErrorReporter) {
|
||||
internal fun Program.reorderStatements(errors: IErrorReporter) {
|
||||
val reorder = StatementReorderer(this, errors)
|
||||
reorder.visit(this)
|
||||
reorder.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
||||
internal fun Program.addTypecasts(errors: IErrorReporter) {
|
||||
val caster = TypecastsAdder(this, errors)
|
||||
caster.visit(this)
|
||||
caster.applyModifications()
|
||||
@@ -37,7 +37,7 @@ internal fun Program.verifyFunctionArgTypes() {
|
||||
fixer.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.checkIdentifiers(errors: ErrorReporter, compTarget: ICompilationTarget) {
|
||||
internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
|
||||
val checker2 = AstIdentifiersChecker(this, errors, compTarget)
|
||||
checker2.visit(this)
|
||||
|
||||
@@ -3,7 +3,6 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.ArrayLiteralValue
|
||||
@@ -11,10 +10,11 @@ import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.StringLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.functions.BuiltinFunctions
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
|
||||
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
||||
internal class AstIdentifiersChecker(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
||||
private var blocks = mutableMapOf<String, Block>()
|
||||
|
||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||
|
||||
@@ -32,7 +32,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernel subroutines and non-asm parameters:
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||
|
||||
@@ -17,13 +17,10 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||
|
||||
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||
// replace the literal string by a identifier reference to a new local vardecl
|
||||
val vardecl = VarDecl.createAuto(string)
|
||||
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(string, identifier, parent),
|
||||
IAstModification.InsertFirst(vardecl, string.definingScope())
|
||||
)
|
||||
// replace the literal string by a identifier reference to the interned string
|
||||
val scopedName = program.internString(string)
|
||||
val identifier = IdentifierReference(scopedName, string.position)
|
||||
return listOf(IAstModification.ReplaceNode(string, identifier, parent))
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.functions.BuiltinFunctions
|
||||
|
||||
|
||||
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||
internal class StatementReorderer(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||
// Reorders the statements in a way the compiler needs.
|
||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||
// - library blocks are put last.
|
||||
@@ -167,6 +167,38 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
else if(expr.operator in logicalOperators) {
|
||||
// make sure that logical expressions like "var and other-logical-expression
|
||||
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
|
||||
// generating the wrong results later
|
||||
|
||||
fun wrapped(expr: Expression): Expression =
|
||||
BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0, expr.position), expr.position)
|
||||
|
||||
fun isLogicalExpr(expr: Expression?): Boolean {
|
||||
if(expr is BinaryExpression && expr.operator in (logicalOperators + comparisonOperators))
|
||||
return true
|
||||
if(expr is PrefixExpression && expr.operator in logicalOperators)
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
return if(isLogicalExpr(expr.left)) {
|
||||
if(isLogicalExpr(expr.right))
|
||||
noModifications
|
||||
else
|
||||
listOf(IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr))
|
||||
} else {
|
||||
if(isLogicalExpr(expr.right))
|
||||
listOf(IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr))
|
||||
else {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
|
||||
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -345,7 +377,8 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// TODO use a pointer loop instead of individual assignments
|
||||
// TODO use memcopy instead of individual assignments after certain amount of elements
|
||||
// TODO what does assigning a struct var use?
|
||||
return alv.value.mapIndexed { index, value ->
|
||||
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
|
||||
Assignment(AssignTarget(null, idx, null, position), value, value.position)
|
||||
|
||||
@@ -8,11 +8,11 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.functions.BuiltinFunctions
|
||||
|
||||
|
||||
class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||
class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||
/*
|
||||
* Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
||||
* (this includes function call arguments)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package prog8.compiler.functions
|
||||
|
||||
import prog8.ast.IMemSizer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
class FParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||
|
||||
|
||||
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
||||
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer) -> NumericLiteralValue
|
||||
|
||||
|
||||
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||
@@ -88,6 +88,7 @@ class FSignature(val name: String,
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
||||
private val functionSignatures: List<FSignature> = listOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
@@ -97,40 +98,40 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// these few have a return value depending on the argument(s):
|
||||
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||
FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof),
|
||||
// normal functions follow:
|
||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
||||
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
||||
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
||||
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
||||
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
||||
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
||||
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
||||
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
||||
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
||||
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
||||
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
|
||||
FSignature("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("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::log) },
|
||||
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, ::log2) },
|
||||
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
||||
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
|
||||
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
|
||||
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||
FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE),
|
||||
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
|
||||
@@ -271,7 +272,8 @@ private fun collectionArg(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
|
||||
}
|
||||
|
||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// 1 arg, type = float or int, result type= isSameAs as argument type
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("abs requires one numeric argument", position)
|
||||
@@ -284,7 +286,7 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("offsetof requires one argument", position)
|
||||
@@ -301,12 +303,12 @@ private fun builtinOffsetof(args: List<Expression>, position: Position, program:
|
||||
for(member in struct.statements) {
|
||||
if((member as VarDecl).name == membername)
|
||||
return NumericLiteralValue(DataType.UBYTE, offset, position)
|
||||
offset += ICompilationTarget.instance.memorySize(member.datatype)
|
||||
offset += memsizer.memorySize(member.datatype)
|
||||
}
|
||||
throw SyntaxError("undefined struct member", position)
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("sizeof requires one argument", position)
|
||||
@@ -319,13 +321,13 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
|
||||
fun structSize(target: StructDecl) =
|
||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { ICompilationTarget.instance.memorySize((it as VarDecl).datatype) }.sum(), position)
|
||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum(), position)
|
||||
|
||||
return when {
|
||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||
numericLiteral(ICompilationTarget.instance.memorySize(elementDt) * length, position)
|
||||
numericLiteral(memsizer.memorySize(elementDt) * length, position)
|
||||
}
|
||||
dt.istype(DataType.STRUCT) -> {
|
||||
when (target) {
|
||||
@@ -335,14 +337,15 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
}
|
||||
}
|
||||
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, ICompilationTarget.instance.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
|
||||
}
|
||||
} else {
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
@@ -366,7 +369,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.STR -> {
|
||||
val refLv = target.value as StringLiteralValue
|
||||
val refLv = target.value as? StringLiteralValue ?: throw CannotEvaluateException("len", "stringsize unknown")
|
||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||
}
|
||||
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||
@@ -376,7 +379,8 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
}
|
||||
|
||||
|
||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -385,7 +389,8 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||
}
|
||||
|
||||
private fun builtinSin8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinSin8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -393,7 +398,8 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -401,7 +407,8 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -409,7 +416,8 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -417,7 +425,8 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -425,7 +434,8 @@ private fun builtinSin16(args: List<Expression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -433,7 +443,8 @@ private fun builtinSin16u(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinCos16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -441,7 +452,8 @@ private fun builtinCos16(args: List<Expression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -449,7 +461,8 @@ private fun builtinCos16u(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sgn requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
import prog8.ast.IMemSizer
|
||||
import prog8.ast.IStringEncoding
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
@@ -7,7 +8,7 @@ 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.IErrorReporter
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
@@ -16,16 +17,11 @@ import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
internal interface ICompilationTarget: IStringEncoding {
|
||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
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
|
||||
@@ -67,7 +63,6 @@ internal interface ICompilationTarget: IStringEncoding {
|
||||
else -> return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +108,7 @@ internal object Cx16Target: ICompilationTarget {
|
||||
internal fun asmGeneratorFor(
|
||||
compTarget: ICompilationTarget,
|
||||
program: Program,
|
||||
errors: ErrorReporter,
|
||||
errors: IErrorReporter,
|
||||
zp: Zeropage,
|
||||
options: CompilationOptions,
|
||||
outputDir: Path
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
import prog8.ast.IStringEncoding
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.parser.ModuleImporter
|
||||
|
||||
|
||||
internal interface IMachineFloat {
|
||||
interface IMachineFloat {
|
||||
fun toDouble(): Double
|
||||
fun makeFloatFillAsm(): String
|
||||
}
|
||||
|
||||
internal enum class CpuType {
|
||||
enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02
|
||||
}
|
||||
|
||||
internal interface IMachineDefinition {
|
||||
interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
@@ -34,10 +31,7 @@ internal interface IMachineDefinition {
|
||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||
fun getFloat(num: Number): IMachineFloat
|
||||
|
||||
// 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 importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
|
||||
fun launchEmulator(programName: String)
|
||||
fun isRegularRAMaddress(address: Int): Boolean
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
package prog8.compiler.target.c64
|
||||
|
||||
import prog8.ast.IStringEncoding
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.IMachineDefinition
|
||||
import prog8.compiler.target.IMachineFloat
|
||||
import prog8.parser.ModuleImporter
|
||||
import java.io.IOException
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
@@ -31,15 +28,11 @@ internal object C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||
|
||||
override fun importLibs(
|
||||
compilerOptions: CompilationOptions,
|
||||
importer: ModuleImporter,
|
||||
program: Program,
|
||||
encoder: IStringEncoding,
|
||||
compilationTargetName: String)
|
||||
{
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
|
||||
override fun importLibs(compilerOptions: CompilationOptions,compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(programName: String) {
|
||||
@@ -109,7 +102,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
// 0x90-0xfa is 'kernel work storage area'
|
||||
// 0x90-0xfa is 'kernal work storage area'
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -236,7 +236,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -431,7 +431,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -495,7 +495,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -626,7 +626,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -885,7 +885,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||
|
||||
@@ -15,6 +15,7 @@ import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
|
||||
import java.io.CharConversionException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
@@ -22,10 +23,10 @@ import kotlin.math.absoluteValue
|
||||
|
||||
|
||||
internal class AsmGen(private val program: Program,
|
||||
val errors: ErrorReporter,
|
||||
val errors: IErrorReporter,
|
||||
val zeropage: Zeropage,
|
||||
val options: CompilationOptions,
|
||||
val compTarget: ICompilationTarget,
|
||||
private val compTarget: ICompilationTarget,
|
||||
private val outputDir: Path): IAssemblyGenerator {
|
||||
|
||||
// for expressions and augmented assignments:
|
||||
@@ -88,12 +89,14 @@ internal class AsmGen(private val program: Program,
|
||||
return AssemblyProgram(program.name, outputDir, compTarget.name)
|
||||
}
|
||||
|
||||
internal fun isTargetCpu(cpu: CpuType) = compTarget.machine.cpu == cpu
|
||||
internal fun haveFPWR() = compTarget is Cx16Target
|
||||
|
||||
private fun header() {
|
||||
val ourName = this.javaClass.name
|
||||
val cpu = when(compTarget.machine.cpu) {
|
||||
CpuType.CPU6502 -> "6502"
|
||||
CpuType.CPU65c02 -> "65c02"
|
||||
CpuType.CPU65c02 -> "w65c02"
|
||||
else -> "unsupported"
|
||||
}
|
||||
|
||||
@@ -130,12 +133,14 @@ internal class AsmGen(private val program: Program,
|
||||
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||
if(!options.noSysInit)
|
||||
out(" jsr ${compTarget.name}.init_system")
|
||||
out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
}
|
||||
options.output == OutputType.PRG -> {
|
||||
out("; ---- program without basic sys call ----")
|
||||
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||
if(!options.noSysInit)
|
||||
out(" jsr ${compTarget.name}.init_system")
|
||||
out(" jsr ${compTarget.name}.init_system_phase2")
|
||||
}
|
||||
options.output == OutputType.RAW -> {
|
||||
out("; ---- raw assembler program ----")
|
||||
@@ -176,14 +181,17 @@ internal class AsmGen(private val program: Program,
|
||||
|
||||
private fun block2asm(block: Block) {
|
||||
out("\n\n; ---- block: '${block.name}' ----")
|
||||
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||
|
||||
val addr = block.address
|
||||
if(addr!=null) {
|
||||
out(".cerror * > ${addr.toHex()}, 'block address overlaps by ', *-${addr.toHex()},' bytes'")
|
||||
out("* = ${addr.toHex()}")
|
||||
if(block.address!=null)
|
||||
out("* = ${block.address!!.toHex()}")
|
||||
else {
|
||||
if("align_word" in block.options())
|
||||
out("\t.align 2")
|
||||
else if("align_page" in block.options())
|
||||
out("\t.align $100")
|
||||
}
|
||||
|
||||
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
|
||||
|
||||
outputSourceLine(block)
|
||||
zeropagevars2asm(block.statements)
|
||||
memdefs2asm(block.statements)
|
||||
@@ -262,7 +270,7 @@ internal class AsmGen(private val program: Program,
|
||||
try {
|
||||
val errors = ErrorReporter()
|
||||
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
||||
errors.handle()
|
||||
errors.report()
|
||||
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
||||
// make sure we add the var to the set of zpvars for this block
|
||||
allocatedZeropageVariables[fullName] = address to variable.datatype
|
||||
@@ -348,7 +356,7 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun memdefs2asm(statements: List<Statement>) {
|
||||
out("\n; memdefs and kernel subroutines")
|
||||
out("\n; memdefs and kernal subroutines")
|
||||
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
|
||||
for(m in memvars) {
|
||||
if(m.value is NumericLiteralValue)
|
||||
@@ -361,7 +369,7 @@ internal class AsmGen(private val program: Program,
|
||||
val addr = sub.asmAddress
|
||||
if(addr!=null) {
|
||||
if(sub.statements.isNotEmpty())
|
||||
throw AssemblyError("kernel subroutine cannot have statements")
|
||||
throw AssemblyError("kernal subroutine cannot have statements")
|
||||
out(" ${sub.name} = ${addr.toHex()}")
|
||||
}
|
||||
}
|
||||
@@ -399,8 +407,9 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
|
||||
val string = (lastvar.value as StringLiteralValue).value
|
||||
out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"")
|
||||
val sv = lastvar.value as StringLiteralValue
|
||||
val altEncoding = if(sv.altEncoding) "@" else ""
|
||||
out("${lastvar.name}\t; ${lastvar.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
|
||||
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
|
||||
for (chunk in outputBytes.chunked(16))
|
||||
out(" .byte " + chunk.joinToString())
|
||||
@@ -527,7 +536,7 @@ internal class AsmGen(private val program: Program,
|
||||
val sourceName = asmVariableName(pointervar)
|
||||
val vardecl = pointervar.targetVarDecl(program)!!
|
||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
return if (isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
out(" lda ($sourceName)")
|
||||
@@ -562,7 +571,7 @@ internal class AsmGen(private val program: Program,
|
||||
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||
|
||||
internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
// just use the cpu's stack for all registers, shorter code
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
@@ -591,7 +600,7 @@ internal class AsmGen(private val program: Program,
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
CpuRegister.X -> {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phx")
|
||||
if (isTargetCpu(CpuType.CPU65c02)) out(" phx")
|
||||
else {
|
||||
if(keepA)
|
||||
out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
|
||||
@@ -600,7 +609,7 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phy")
|
||||
if (isTargetCpu(CpuType.CPU65c02)) out(" phy")
|
||||
else {
|
||||
if(keepA)
|
||||
out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
|
||||
@@ -612,7 +621,7 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
internal fun restoreRegisterLocal(register: CpuRegister) {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if (isTargetCpu(CpuType.CPU65c02)) {
|
||||
when (register) {
|
||||
// this just used the stack, for all registers. Shorter code.
|
||||
CpuRegister.A -> out(" pla")
|
||||
@@ -637,7 +646,7 @@ internal class AsmGen(private val program: Program,
|
||||
out(" pla")
|
||||
}
|
||||
CpuRegister.X -> {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" plx")
|
||||
if (isTargetCpu(CpuType.CPU65c02)) out(" plx")
|
||||
else {
|
||||
if(keepA)
|
||||
out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
|
||||
@@ -646,7 +655,7 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" ply")
|
||||
if (isTargetCpu(CpuType.CPU65c02)) out(" ply")
|
||||
else {
|
||||
if(keepA)
|
||||
out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
|
||||
@@ -1254,7 +1263,9 @@ $counterVar .word 0""")
|
||||
"%asmbinary" -> {
|
||||
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
|
||||
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
|
||||
out(" .binary \"${stmt.args[0].str}\" $offset $length")
|
||||
val includedSourcePath = stmt.definingModule().source.resolveSibling(stmt.args[0].str)
|
||||
val relPath = Paths.get("").relativize(includedSourcePath)
|
||||
out(" .binary \"./$relPath\" $offset $length")
|
||||
}
|
||||
"%breakpoint" -> {
|
||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||
@@ -1329,7 +1340,7 @@ $label nop""")
|
||||
// sign extend signed byte on stack to signed word on stack
|
||||
when(valueDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(isTargetCpu(CpuType.CPU65c02))
|
||||
out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
@@ -1343,7 +1354,7 @@ $label nop""")
|
||||
// sign extend signed byte in a var to a full word in that variable
|
||||
when(valueDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(isTargetCpu(CpuType.CPU65c02))
|
||||
out(" stz $asmvar+1")
|
||||
else
|
||||
out(" lda #0 | sta $asmvar+1")
|
||||
@@ -1396,39 +1407,44 @@ $label nop""")
|
||||
|
||||
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
|
||||
// optimize pointer,indexregister if possible
|
||||
|
||||
fun evalBytevalueWillClobberA(expr: Expression): Boolean {
|
||||
val dt = expr.inferType(program)
|
||||
if(!dt.istype(DataType.UBYTE) && !dt.istype(DataType.BYTE))
|
||||
return true
|
||||
return when(expr) {
|
||||
is IdentifierReference -> false
|
||||
is NumericLiteralValue -> false
|
||||
is DirectMemoryRead -> expr.addressExpression !is IdentifierReference && expr.addressExpression !is NumericLiteralValue
|
||||
is TypecastExpression -> evalBytevalueWillClobberA(expr.expression)
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(expr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
|
||||
if(ptrAndIndex!=null) {
|
||||
val pointervar = ptrAndIndex.first as? IdentifierReference
|
||||
if(write) {
|
||||
when(ptrAndIndex.second) {
|
||||
is NumericLiteralValue, is IdentifierReference -> {
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// same as above but we need to save the A register
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
out(" pha")
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" pla")
|
||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
out(" pha")
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" pla")
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
val saveA = evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||
if(saveA)
|
||||
out(" pha")
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
if(saveA)
|
||||
out(" pla")
|
||||
out(" sta (${asmSymbolName(pointervar)}),y")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||
if(saveA)
|
||||
out(" pha")
|
||||
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
if(saveA)
|
||||
out(" pla")
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
} else {
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
|
||||
@@ -5,7 +5,10 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.functions.FSignature
|
||||
@@ -68,7 +71,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult || fcall !is FunctionCall)
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val scope = fcall.definingScope()
|
||||
val nameRef = fcall.args[0] as IdentifierReference
|
||||
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
|
||||
val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
|
||||
@@ -85,12 +87,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
||||
else
|
||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen)
|
||||
val assign = AsmAssignment(src, target, false, fcall.position)
|
||||
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign)
|
||||
|
||||
// remove the variable for the name, it's not used as a variable only as a tag for the assembler.
|
||||
val nameDecl = scope.statements.single { it is VarDecl && it.name==nameRef.nameInSource.single() }
|
||||
asmgen.removals.add(Pair(nameDecl, scope))
|
||||
asmgen.slabs[name] = size
|
||||
}
|
||||
|
||||
@@ -651,12 +649,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val assignFirst = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
|
||||
targetFromExpr(first, datatype),
|
||||
false, first.position
|
||||
false, program.memsizer, first.position
|
||||
)
|
||||
val assignSecond = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
|
||||
targetFromExpr(second, datatype),
|
||||
false, second.position
|
||||
false, program.memsizer, second.position
|
||||
)
|
||||
asmgen.translateNormalAssignment(assignFirst)
|
||||
asmgen.translateNormalAssignment(assignSecond)
|
||||
@@ -668,12 +666,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val assignFirst = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
||||
targetFromExpr(first, datatype),
|
||||
false, first.position
|
||||
false, program.memsizer, first.position
|
||||
)
|
||||
val assignSecond = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
||||
targetFromExpr(second, datatype),
|
||||
false, second.position
|
||||
false, program.memsizer, second.position
|
||||
)
|
||||
asmgen.translateNormalAssignment(assignFirst)
|
||||
asmgen.translateNormalAssignment(assignSecond)
|
||||
@@ -683,8 +681,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
||||
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
@@ -797,7 +795,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
@@ -856,7 +854,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt)
|
||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
@@ -977,7 +975,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
// 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) {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
sta ($varname)
|
||||
txa
|
||||
@@ -1028,7 +1026,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
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) {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda ($varname),y
|
||||
@@ -1292,7 +1290,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
|
||||
val assign = AsmAssignment(src, tgt, false, value.position)
|
||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign)
|
||||
}
|
||||
conv.reg != null -> {
|
||||
@@ -1308,7 +1306,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
|
||||
val assign = AsmAssignment(src, tgt, false, value.position)
|
||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign)
|
||||
}
|
||||
else -> throw AssemblyError("callconv")
|
||||
|
||||
@@ -1393,7 +1393,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
@@ -1459,7 +1459,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
|
||||
fun assignViaExprEval() {
|
||||
asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
if (pushResultOnEstack) {
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
|
||||
} else {
|
||||
@@ -1673,7 +1673,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount>=16) {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||
@@ -1888,7 +1888,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||
if(arrayExpr.indexer.indexNum!=null) {
|
||||
val indexValue = arrayExpr.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt)
|
||||
val indexValue = arrayExpr.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||
|
||||
@@ -91,11 +91,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
when {
|
||||
stmt.args.all {isNoClobberRisk(it)} -> {
|
||||
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
|
||||
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
|
||||
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
|
||||
val (cx16virtualRegsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||
for(arg in cx16virtualRegsArgsInfo)
|
||||
val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
|
||||
val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
|
||||
for(arg in cx16virtualRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
for(arg in otherRegsArgsInfo)
|
||||
for(arg in cpuRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
for(arg in statusRegs)
|
||||
argumentViaRegister(sub, arg.first.first, arg.first.second)
|
||||
}
|
||||
else -> {
|
||||
@@ -168,7 +172,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
""")
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
@@ -318,7 +322,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(targetArrayIdx.indexer.indexNum!=null) {
|
||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt)
|
||||
val indexValue = targetArrayIdx.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
in WordDatatypes -> {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package prog8.compiler.target.c64.codegen.assignment
|
||||
|
||||
import prog8.ast.IMemSizer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
|
||||
|
||||
@@ -207,12 +207,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
internal class AsmAssignment(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val isAugmentable: Boolean,
|
||||
memsizer: IMemSizer,
|
||||
val position: Position) {
|
||||
|
||||
init {
|
||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
||||
require(ICompilationTarget.instance.memorySize(source.datatype) <= ICompilationTarget.instance.memorySize(target.datatype)) {
|
||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||
"source storage size must be less or equal to target datatype storage size"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
||||
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
||||
|
||||
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
|
||||
val assign = AsmAssignment(source, target, assignment.isAugmentable, program.memsizer, assignment.position)
|
||||
target.origAssign = assign
|
||||
|
||||
if(assign.isAugmentable)
|
||||
@@ -66,7 +66,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
||||
if (value.indexer.indexNum!=null) {
|
||||
// constant array index value
|
||||
val indexValue = value.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt)
|
||||
val indexValue = value.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue")
|
||||
@@ -114,7 +114,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
SourceStorageKind.MEMORY -> {
|
||||
fun assignViaExprEval(expression: Expression) {
|
||||
assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
@@ -319,7 +319,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
|
||||
fun assignViaExprEval(addressExpression: Expression) {
|
||||
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
|
||||
@@ -441,7 +441,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
|
||||
lsb.linkParents(value.parent)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||
val assign = AsmAssignment(src, target, false, value.position)
|
||||
val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
|
||||
translateNormalAssignment(assign)
|
||||
}
|
||||
|
||||
@@ -473,7 +473,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
@@ -496,7 +496,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
@@ -592,7 +592,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
@@ -620,7 +620,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
@@ -762,7 +762,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
||||
@@ -968,7 +968,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
TargetStorageKind.ARRAY -> {
|
||||
target.array!!
|
||||
if(target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||
@@ -1191,7 +1191,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype)
|
||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||
}
|
||||
else {
|
||||
@@ -1295,7 +1295,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when(wordtarget.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
@@ -1304,7 +1304,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
if (wordtarget.constArrayIndexValue!=null) {
|
||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
@@ -1329,7 +1329,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
@@ -1571,7 +1571,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
|
||||
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
||||
if(word==0 && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if(word==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
// optimize setting zero value for this processor
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
@@ -1666,7 +1666,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
|
||||
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
|
||||
if(byte==0.toShort() && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if(byte==0.toShort() && asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
// optimize setting zero value for this cpu
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
@@ -1736,7 +1736,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
@@ -1757,7 +1757,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
// optimized case for float zero
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out("""
|
||||
stz ${target.asmVarname}
|
||||
stz ${target.asmVarname}+1
|
||||
@@ -1777,8 +1777,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.array!!.indexer.indexNum!=null) {
|
||||
val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT)
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out("""
|
||||
stz ${target.asmVarname}+$indexValue
|
||||
stz ${target.asmVarname}+$indexValue+1
|
||||
@@ -1842,7 +1842,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val arrayVarName = target.asmVarname
|
||||
if (target.array!!.indexer.indexNum!=null) {
|
||||
val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT)
|
||||
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
|
||||
asmgen.out("""
|
||||
lda $constFloat
|
||||
sta $arrayVarName+$indexValue
|
||||
@@ -1975,7 +1975,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when(wordtarget.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
@@ -1991,7 +1991,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
@@ -2003,7 +2003,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname}")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
@@ -2023,7 +2023,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
asmgen.out(" sta P8ESTACK_LO,x")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
@@ -2041,7 +2041,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when(addressExpr) {
|
||||
is NumericLiteralValue, is IdentifierReference -> {
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
@@ -2051,7 +2051,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.out(" pla")
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W2)")
|
||||
else
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
@@ -2063,7 +2063,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val sourceName = asmgen.asmVariableName(pointervar)
|
||||
val vardecl = pointervar.targetVarDecl(program)!!
|
||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) {
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
|
||||
if (asmgen.isZpVar(scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.out(" sta ($sourceName)")
|
||||
@@ -2109,21 +2109,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
|
||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
||||
val assign = AsmAssignment(src, tgt, false, expr.position)
|
||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||
translateNormalAssignment(assign)
|
||||
}
|
||||
|
||||
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
|
||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
|
||||
val assign = AsmAssignment(src, tgt, false, expr.position)
|
||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||
translateNormalAssignment(assign)
|
||||
}
|
||||
|
||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
|
||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
||||
val assign = AsmAssignment(src, tgt, false, Position.DUMMY)
|
||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
|
||||
translateNormalAssignment(assign)
|
||||
}
|
||||
}
|
||||
+38
-39
@@ -7,7 +7,6 @@ import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
||||
|
||||
@@ -201,7 +200,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
with(target.array!!.indexer) {
|
||||
when {
|
||||
indexNum!=null -> {
|
||||
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*asmgen.compTarget.memorySize(target.datatype)}"
|
||||
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
when {
|
||||
@@ -245,19 +244,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
when(target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assign)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assign)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assign)
|
||||
assignmentAsmGen.assignFAC1float(target)
|
||||
}
|
||||
@@ -631,7 +630,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
"<<" -> {
|
||||
if(value>=8) {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
@@ -642,7 +641,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
if(value>0) {
|
||||
if (dt == DataType.UBYTE) {
|
||||
if(value>=8) {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
@@ -857,14 +856,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
"<<" -> {
|
||||
when {
|
||||
value>=16 -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value==8 -> {
|
||||
asmgen.out(" lda $name | sta $name+1")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
@@ -884,14 +883,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
if(dt==DataType.UWORD) {
|
||||
when {
|
||||
value>=16 -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value==8 -> {
|
||||
asmgen.out(" lda $name+1 | sta $name")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
@@ -940,13 +939,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
"&", "and" -> {
|
||||
when {
|
||||
value == 0 -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value and 255 == 0 -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
@@ -954,7 +953,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
value < 0x0100 -> {
|
||||
asmgen.out(" lda $name | and #$value | sta $name")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
@@ -1041,7 +1040,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
"*" -> {
|
||||
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||
@@ -1092,7 +1091,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
"&", "and" -> {
|
||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
@@ -1351,7 +1350,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
@@ -1474,7 +1473,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
if(asmgen.compTarget is Cx16Target) {
|
||||
if(asmgen.haveFPWR()) {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
} else
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
@@ -1485,15 +1493,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.MOVFM
|
||||
jsr floats.FPWRT
|
||||
""")
|
||||
} else
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
}
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
@@ -1552,7 +1551,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
if(asmgen.compTarget is Cx16Target) {
|
||||
if(asmgen.haveFPWR()) {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
} else
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
@@ -1563,15 +1571,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.MOVFM
|
||||
jsr floats.FPWRT
|
||||
""")
|
||||
} else
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
}
|
||||
"+" -> {
|
||||
if (value == 0.0)
|
||||
@@ -1644,7 +1643,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${target.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
|
||||
@@ -1654,7 +1653,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.out(" lda #0 | sta ${target.asmVarname},y")
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package prog8.compiler.target.cx16
|
||||
|
||||
import prog8.ast.IStringEncoding
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.IMachineDefinition
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.parser.ModuleImporter
|
||||
import java.io.IOException
|
||||
|
||||
internal object CX16MachineDefinition: IMachineDefinition {
|
||||
@@ -28,16 +25,11 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||
|
||||
override fun importLibs(
|
||||
compilerOptions: CompilationOptions,
|
||||
importer: ModuleImporter,
|
||||
program: Program,
|
||||
encoder: IStringEncoding,
|
||||
compilationTargetName: String)
|
||||
{
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
|
||||
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
listOf("syslib")
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun launchEmulator(programName: String) {
|
||||
|
||||
@@ -10,7 +10,7 @@ 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, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
@@ -80,7 +80,7 @@ X = BinExpr X = LeftExpr
|
||||
|
||||
private fun isSimpleTarget(target: AssignTarget, program: Program) =
|
||||
if (target.identifier!=null || target.memoryAddress!=null)
|
||||
ICompilationTarget.instance.isInRegularRAM(target, program)
|
||||
compTarget.isInRegularRAM(target, program)
|
||||
else
|
||||
false
|
||||
|
||||
|
||||
@@ -5,25 +5,25 @@ import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.AddressOf
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compiler.loadAsmIncludeFile
|
||||
import prog8.compiler.IErrorReporter
|
||||
import java.nio.file.Path
|
||||
|
||||
private val alwaysKeepSubroutines = setOf(
|
||||
Pair("main", "start"),
|
||||
Pair("irq", "irq")
|
||||
Pair("main", "start")
|
||||
)
|
||||
|
||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||
|
||||
|
||||
class CallGraph(private val program: Program) : IAstVisitor {
|
||||
class CallGraph(private val program: Program, private val asmFileLoader: (filename: String, source: Path)->String) : IAstVisitor {
|
||||
|
||||
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||
@@ -80,7 +80,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
|
||||
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||
} else if (directive.directive == "%asminclude") {
|
||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||
val asm = asmFileLoader(directive.args[0].str!!, thisModule.source)
|
||||
val scope = directive.definingSubroutine()
|
||||
if(scope!=null) {
|
||||
scanAssemblyCode(asm, directive, scope)
|
||||
@@ -150,6 +150,17 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
val otherSub = addressOf.identifier.targetSubroutine(program)
|
||||
if(otherSub!=null) {
|
||||
addressOf.definingSubroutine()?.let { thisSub ->
|
||||
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
|
||||
calledBy[otherSub] = calledBy.getValue(otherSub).plus(thisSub)
|
||||
}
|
||||
}
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
override fun visit(jump: Jump) {
|
||||
val otherSub = jump.identifier?.targetSubroutine(program)
|
||||
if (otherSub != null) {
|
||||
@@ -213,7 +224,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
fun checkRecursiveCalls(errors: ErrorReporter) {
|
||||
fun checkRecursiveCalls(errors: IErrorReporter) {
|
||||
val cycles = recursionCycles()
|
||||
if(cycles.any()) {
|
||||
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY)
|
||||
|
||||
@@ -13,7 +13,7 @@ import prog8.compiler.target.ICompilationTarget
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
@@ -224,7 +224,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, ICompilationTarget.instance, range.position)
|
||||
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, compTarget, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
|
||||
@@ -10,11 +10,11 @@ import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
|
||||
// Fix up the literal value's type to match that of the vardecl
|
||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
@@ -39,7 +39,7 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
||||
// Replace all constant identifiers with their actual value,
|
||||
// and the array var initializer values and sizes.
|
||||
// This is needed because further constant optimizations depend on those.
|
||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
@@ -192,7 +192,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
if(rangeExpr==null && litval!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = litval.number.toDouble()
|
||||
if (fillvalue < ICompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > ICompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
||||
if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", litval.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
|
||||
@@ -2,16 +2,18 @@ package prog8.optimizer
|
||||
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||
internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||
valuetypefixer.visit(this)
|
||||
if(errors.isEmpty()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val replacer = ConstantIdentifierReplacer(this, errors)
|
||||
val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
|
||||
replacer.visit(this)
|
||||
if (errors.isEmpty()) {
|
||||
replacer.applyModifications()
|
||||
@@ -20,7 +22,7 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
||||
if(errors.isEmpty()) {
|
||||
valuetypefixer.applyModifications()
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this)
|
||||
val optimizer = ConstantFoldingOptimizer(this, compTarget)
|
||||
optimizer.visit(this)
|
||||
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
||||
optimizer.visit(this)
|
||||
@@ -39,8 +41,11 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
||||
}
|
||||
|
||||
|
||||
internal fun Program.optimizeStatements(errors: ErrorReporter, functions: IBuiltinFunctions): Int {
|
||||
val optimizer = StatementOptimizer(this, errors, functions)
|
||||
internal fun Program.optimizeStatements(errors: IErrorReporter,
|
||||
functions: IBuiltinFunctions,
|
||||
compTarget: ICompilationTarget,
|
||||
asmFileLoader: (filename: String, source: Path)->String): Int {
|
||||
val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader)
|
||||
optimizer.visit(this)
|
||||
val optimizationCount = optimizer.applyModifications()
|
||||
|
||||
@@ -55,8 +60,8 @@ internal fun Program.simplifyExpressions() : Int {
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Program.splitBinaryExpressions() : Int {
|
||||
val opti = BinExprSplitter(this)
|
||||
internal fun Program.splitBinaryExpressions(compTarget: ICompilationTarget) : Int {
|
||||
val opti = BinExprSplitter(this, compTarget)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
@@ -10,23 +10,27 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import java.nio.file.Path
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
internal class StatementOptimizer(private val program: Program,
|
||||
private val errors: ErrorReporter,
|
||||
private val functions: IBuiltinFunctions
|
||||
private val errors: IErrorReporter,
|
||||
private val functions: IBuiltinFunctions,
|
||||
private val compTarget: ICompilationTarget,
|
||||
asmFileLoader: (filename: String, source: Path)->String
|
||||
) : AstWalker() {
|
||||
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
private val callgraph = CallGraph(program)
|
||||
private val callgraph = CallGraph(program, asmFileLoader)
|
||||
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars()) {
|
||||
errors.warn("removing empty block '${block.name}'", block.position)
|
||||
if(block.name != program.internedStringsModuleName)
|
||||
errors.warn("removing empty block '${block.name}'", block.position)
|
||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||
}
|
||||
|
||||
@@ -52,7 +56,8 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
if(!subroutine.isAsmSubroutine)
|
||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
||||
}
|
||||
|
||||
@@ -81,11 +86,9 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||
// this is a C-64 specific optimization
|
||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference?
|
||||
stringVar = if(arg is AddressOf) {
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
arg.identifier
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
@@ -96,29 +99,29 @@ internal class StatementOptimizer(private val program: Program,
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
val firstCharEncoded = ICompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
||||
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = ICompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
||||
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
||||
anonscope.statements.add(chrout1)
|
||||
anonscope.statements.add(chrout2)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +215,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val size = sv.value.length
|
||||
if(size==1) {
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = ICompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0]
|
||||
val character = compTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||
@@ -293,18 +296,6 @@ internal class StatementOptimizer(private val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||
// remove empty choices
|
||||
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
|
||||
override fun perform() {
|
||||
whenStatement.choices.remove(choice)
|
||||
}
|
||||
}
|
||||
return whenStatement.choices
|
||||
.filter { !it.statements.containsCodeOrVars() }
|
||||
.map { ChoiceRemover(it) }
|
||||
}
|
||||
|
||||
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
// if the jump is to the next statement, remove the jump
|
||||
val scope = jump.definingScope()
|
||||
|
||||
@@ -3,7 +3,6 @@ package prog8.optimizer
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.ErrorReporter
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
@@ -11,20 +10,26 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() {
|
||||
internal class UnusedCodeRemover(private val program: Program,
|
||||
private val errors: IErrorReporter,
|
||||
private val compTarget: ICompilationTarget,
|
||||
private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() {
|
||||
|
||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
||||
val callgraph = CallGraph(program)
|
||||
val callgraph = CallGraph(program, asmFileLoader)
|
||||
val removals = mutableListOf<IAstModification>()
|
||||
|
||||
// remove all subroutines that aren't called, or are empty
|
||||
val entrypoint = program.entrypoint()
|
||||
program.modules.forEach {
|
||||
callgraph.forAllSubroutines(it) { sub ->
|
||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||
val forceOutput = "force_output" in sub.definingBlock().options()
|
||||
if (sub !== entrypoint && !forceOutput && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||
removals.add(IAstModification.Remove(sub, sub.definingScope()))
|
||||
}
|
||||
}
|
||||
@@ -96,7 +101,7 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
||||
val assign1 = stmtPairs[0] as? Assignment
|
||||
val assign2 = stmtPairs[1] as? Assignment
|
||||
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
||||
if (assign1.target.isSameAs(assign2.target, program) && ICompilationTarget.instance.isInRegularRAM(assign1.target, program)) {
|
||||
if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target, program)) {
|
||||
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
||||
// only remove the second assignment if its value is a simple expression!
|
||||
when(assign2.value) {
|
||||
|
||||
+67
-41
@@ -5,21 +5,22 @@ import org.hamcrest.Matchers.closeTo
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.junit.jupiter.api.Test
|
||||
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.*
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.toHex
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||
import java.io.CharConversionException
|
||||
import java.nio.file.Path
|
||||
import kotlin.test.*
|
||||
@@ -134,7 +135,7 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testNames() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
@@ -147,37 +148,37 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testZpFloatEnable() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpModesWithFloats() {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
|
||||
}
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpDontuse() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
|
||||
println(zp.free)
|
||||
assertEquals(0, zp.available())
|
||||
assertFailsWith<CompilerException> {
|
||||
@@ -187,19 +188,19 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp1.available())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(89, zp2.available())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(125, zp3.available())
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp4.available())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp1.available())
|
||||
assertTrue(50 in zp1.free)
|
||||
assertTrue(100 in zp1.free)
|
||||
@@ -208,7 +209,7 @@ class TestC64Zeropage {
|
||||
assertTrue(200 in zp1.free)
|
||||
assertTrue(255 in zp1.free)
|
||||
assertTrue(199 in zp1.free)
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
|
||||
assertEquals(139, zp2.available())
|
||||
assertFalse(50 in zp2.free)
|
||||
assertFalse(100 in zp2.free)
|
||||
@@ -221,7 +222,7 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testBasicsafeAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp.available())
|
||||
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
@@ -244,7 +245,7 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testFullAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp.available())
|
||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||
assertTrue(loc > 3)
|
||||
@@ -274,7 +275,7 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testEfficientAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp.available())
|
||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
@@ -293,7 +294,7 @@ class TestC64Zeropage {
|
||||
|
||||
@Test
|
||||
fun testReservedLocations() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
||||
}
|
||||
}
|
||||
@@ -303,9 +304,30 @@ class TestC64Zeropage {
|
||||
class TestCx16Zeropage {
|
||||
@Test
|
||||
fun testReservedLocations() {
|
||||
val zp = CX16MachineDefinition.CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
|
||||
assertEquals(88, zp1.available())
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(175, zp3.available())
|
||||
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(216, zp4.available())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(216, zp1.available())
|
||||
assertTrue(0x22 in zp1.free)
|
||||
assertTrue(0x80 in zp1.free)
|
||||
assertTrue(0xff in zp1.free)
|
||||
assertFalse(0x02 in zp1.free)
|
||||
assertFalse(0x21 in zp1.free)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -408,16 +430,20 @@ class TestMemory {
|
||||
private class DummyFunctions: IBuiltinFunctions {
|
||||
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 constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? = null
|
||||
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||
}
|
||||
|
||||
private class DummyMemsizer: IMemSizer {
|
||||
override fun memorySize(dt: DataType): Int = 0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInValidRamC64_memory_addresses() {
|
||||
|
||||
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
|
||||
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
|
||||
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
|
||||
@@ -442,7 +468,7 @@ class TestMemory {
|
||||
|
||||
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
|
||||
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||
|
||||
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
|
||||
@@ -461,7 +487,7 @@ class TestMemory {
|
||||
@Test
|
||||
fun testInValidRamC64_memory_identifiers() {
|
||||
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
|
||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
|
||||
@@ -490,7 +516,7 @@ class TestMemory {
|
||||
fun testInValidRamC64_memory_expression() {
|
||||
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||
val program = Program("test", mutableListOf(), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
|
||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
|
||||
@@ -501,7 +527,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
@@ -514,7 +540,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
@@ -527,7 +553,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
@@ -540,7 +566,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
@@ -554,7 +580,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertTrue(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
@@ -568,7 +594,7 @@ class TestMemory {
|
||||
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 module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions())
|
||||
val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
|
||||
module.linkParents(ParentSentinel)
|
||||
assertFalse(C64Target.isInRegularRAM(target, program))
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ dependencies {
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
useIR = true
|
||||
// verbose = true
|
||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||
}
|
||||
@@ -41,6 +42,7 @@ compileKotlin {
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
useIR = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
@@ -244,10 +241,14 @@ interface IAssignable {
|
||||
// just a tag for now
|
||||
}
|
||||
|
||||
interface IMemSizer {
|
||||
fun memorySize(dt: DataType): Int
|
||||
}
|
||||
|
||||
interface IBuiltinFunctions {
|
||||
val names: Set<String>
|
||||
val purefunctionNames: Set<String>
|
||||
fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue?
|
||||
fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue?
|
||||
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
|
||||
}
|
||||
|
||||
@@ -255,13 +256,32 @@ interface IBuiltinFunctions {
|
||||
|
||||
|
||||
|
||||
class Program(val name: String, val modules: MutableList<Module>, val builtinFunctions: IBuiltinFunctions): Node {
|
||||
class Program(val name: String,
|
||||
val modules: MutableList<Module>,
|
||||
val builtinFunctions: IBuiltinFunctions,
|
||||
val memsizer: IMemSizer): Node {
|
||||
val namespace = GlobalNamespace(modules, builtinFunctions.names)
|
||||
|
||||
val mainModule: Module
|
||||
get() = modules.first { it.name!=internedStringsModuleName }
|
||||
val definedLoadAddress: Int
|
||||
get() = modules.first().loadAddress
|
||||
get() = mainModule.loadAddress
|
||||
|
||||
var actualLoadAddress: Int = 0
|
||||
private val internedStrings = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||
val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
init {
|
||||
// insert a container module for all interned strings later
|
||||
if(modules.firstOrNull()?.name != internedStringsModuleName) {
|
||||
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of(""))
|
||||
modules.add(0, internedStringsModule)
|
||||
val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY)
|
||||
internedStringsModule.statements.add(block)
|
||||
internedStringsModule.linkParents(this)
|
||||
internedStringsModule.program = this
|
||||
}
|
||||
}
|
||||
|
||||
fun entrypoint(): Subroutine? {
|
||||
val mainBlocks = allBlocks().filter { it.name=="main" }
|
||||
@@ -274,6 +294,22 @@ class Program(val name: String, val modules: MutableList<Module>, val builtinFun
|
||||
}
|
||||
}
|
||||
|
||||
fun internString(string: StringLiteralValue): List<String> {
|
||||
val key = Pair(string.value, string.altEncoding)
|
||||
val existing = internedStrings[key]
|
||||
if(existing!=null)
|
||||
return existing
|
||||
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", null, string,
|
||||
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
|
||||
(internedStringsBlock as Block).statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
val scopedName = listOf(internedStringsModuleName, decl.name)
|
||||
internedStrings[key] = scopedName
|
||||
return scopedName
|
||||
}
|
||||
|
||||
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||
|
||||
override val position: Position = Position.DUMMY
|
||||
|
||||
@@ -13,6 +13,8 @@ import kotlin.math.abs
|
||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
|
||||
val logicalOperators = setOf("and", "or", "xor", "not")
|
||||
|
||||
|
||||
sealed class Expression: Node {
|
||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||
@@ -478,15 +480,11 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
}
|
||||
}
|
||||
|
||||
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
|
||||
|
||||
class StringLiteralValue(val value: String,
|
||||
val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
val heapId = ++heapIdSequence
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
@@ -516,8 +514,6 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
val heapId = ++heapIdSequence
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
value.forEach {it.linkParents(this)}
|
||||
@@ -763,17 +759,6 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
|
||||
|
||||
fun heapId(namespace: INameScope): Int {
|
||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
||||
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
|
||||
return when (value) {
|
||||
is IdentifierReference -> value.heapId(namespace)
|
||||
is StringLiteralValue -> value.heapId
|
||||
is ArrayLiteralValue -> value.heapId
|
||||
else -> throw FatalAstException("requires a reference value")
|
||||
}
|
||||
}
|
||||
|
||||
fun firstStructVarName(program: Program): String? {
|
||||
// take the name of the first struct member of the structvariable instead
|
||||
// if it's just a regular variable, return null.
|
||||
@@ -818,7 +803,7 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||
if(target.nameInSource.size>1)
|
||||
return null
|
||||
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
||||
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position, program.memsizer)
|
||||
if(withDatatypeCheck) {
|
||||
val resultDt = this.inferType(program)
|
||||
if(resultValue==null || resultDt istype resultValue.type)
|
||||
|
||||
@@ -176,12 +176,6 @@ open class VarDecl(val type: VarDeclType,
|
||||
companion object {
|
||||
private var autoHeapValueSequenceNumber = 0
|
||||
|
||||
fun createAuto(string: StringLiteralValue): VarDecl {
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
return VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
|
||||
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||
}
|
||||
|
||||
fun createAuto(array: ArrayLiteralValue): VarDecl {
|
||||
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
|
||||
val arrayDt =
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.8 virtualenv" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
+1
-1
@@ -20,7 +20,7 @@ import os
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Prog8'
|
||||
copyright = '2019, Irmen de Jong'
|
||||
copyright = '2021, Irmen de Jong'
|
||||
author = 'Irmen de Jong'
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Language features
|
||||
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
||||
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the C64.
|
||||
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
|
||||
|
||||
Code example
|
||||
|
||||
@@ -182,7 +182,7 @@ Provides string manipulation routines.
|
||||
|
||||
floats
|
||||
------
|
||||
Provides definitions for the ROM/kernel subroutines and utility routines dealing with floating
|
||||
Provides definitions for the ROM/kernal subroutines and utility routines dealing with floating
|
||||
point variables. This includes ``print_f``, the routine used to print floating point numbers.
|
||||
|
||||
|
||||
|
||||
@@ -522,12 +522,12 @@ Use a ``when`` statement if you have a set of fixed choices that each should res
|
||||
action. It is possible to combine several choices to result in the same action::
|
||||
|
||||
when value {
|
||||
4 -> c64scr.print("four")
|
||||
5 -> c64scr.print("five")
|
||||
4 -> txt.print("four")
|
||||
5 -> txt.print("five")
|
||||
10,20,30 -> {
|
||||
c64scr.print("ten or twenty or thirty")
|
||||
txt.print("ten or twenty or thirty")
|
||||
}
|
||||
else -> c64scr.print("don't know")
|
||||
else -> txt.print("don't know")
|
||||
}
|
||||
|
||||
The when-*value* can be any expression but the choice values have to evaluate to
|
||||
|
||||
@@ -70,7 +70,7 @@ Directives
|
||||
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
||||
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
|
||||
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
||||
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
|
||||
are required to perform floating point operations (from the BASIC kernal). No clean exit is possible.
|
||||
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
||||
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
|
||||
during normal system operation.
|
||||
@@ -120,17 +120,18 @@ Directives
|
||||
Level: module, block.
|
||||
Sets special compiler options.
|
||||
|
||||
- For a module option, there is ``enable_floats``, which will tell the compiler
|
||||
- ``enable_floats`` (module level) tells the compiler
|
||||
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
|
||||
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
|
||||
importing the ``floats`` library is required anyway and that will enable it for you automatically.
|
||||
- There's also ``no_sysinit`` which cause the resulting program to *not* include
|
||||
- ``no_sysinit`` (module level) which cause the resulting program to *not* include
|
||||
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
|
||||
take care of that yourself. The program will just start running from whatever state the machine is in when the
|
||||
program was launched.
|
||||
- When used in a block with the ``force_output`` option, it will force the block to be outputted
|
||||
in the final program. Can be useful to make sure some
|
||||
data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
|
||||
- ``force_output`` (in a block) will force the block to be outputted in the final program.
|
||||
Can be useful to make sure some data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
|
||||
- ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address).
|
||||
- ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0).
|
||||
|
||||
|
||||
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
|
||||
@@ -513,7 +514,7 @@ Multiple return values
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Normal subroutines can only return zero or one return values.
|
||||
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
||||
(referencing a routine in kernel ROM) can return more than one return value.
|
||||
(referencing a routine in kernal ROM) can return more than one return value.
|
||||
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
||||
It is not possible to process the results of a call to these kind of routines
|
||||
directly from the language, because only single value assignments are possible.
|
||||
@@ -772,11 +773,11 @@ Choices can result in a single statement or a block of multiple statements in w
|
||||
case you have to use { } to enclose them::
|
||||
|
||||
when value {
|
||||
4 -> c64scr.print("four")
|
||||
5 -> c64scr.print("five")
|
||||
4 -> txt.print("four")
|
||||
5 -> txt.print("five")
|
||||
10,20,30 -> {
|
||||
c64scr.print("ten or twenty or thirty")
|
||||
txt.print("ten or twenty or thirty")
|
||||
}
|
||||
else -> c64scr.print("don't know")
|
||||
else -> txt.print("don't know")
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ Currently there are two machines that are supported as compiler target (selectab
|
||||
This chapter explains the relevant system details of these machines.
|
||||
|
||||
.. hint::
|
||||
If you only use standard kernel and prog8 library routines,
|
||||
If you only use standard kernal and prog8 library routines,
|
||||
it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
|
||||
|
||||
@@ -139,26 +139,17 @@ IRQ Handling
|
||||
============
|
||||
|
||||
Normally, the system's default IRQ handling is not interfered with.
|
||||
You can however install your own IRQ handler.
|
||||
This is possible ofcourse by doing it all using customized inline assembly,
|
||||
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
||||
You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block).
|
||||
There are a few library routines available to make setting up C-64 60hz IRQs and Raster IRQs a lot easier (no assembly code required).
|
||||
|
||||
For the C64 these routines are::
|
||||
|
||||
c64.set_irqvec()
|
||||
c64.set_irqvec_excl()
|
||||
c64.set_irq(uword handler_address, boolean useKernal)
|
||||
c64.set_rasterirq(uword handler_address, uword rasterline, boolean useKernal)
|
||||
c64.restore_irq() ; set everything back to the systems default irq handler
|
||||
|
||||
c64.set_rasterirq( <raster line> )
|
||||
c64.set_rasterirq_excl( <raster line> )
|
||||
|
||||
c64.restore_irqvec() ; set it back to the systems default irq handler
|
||||
|
||||
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
||||
as a subroutine ``irq`` in the module ``irq`` so like this::
|
||||
|
||||
irq {
|
||||
sub irq() {
|
||||
; ... irq handling here ...
|
||||
}
|
||||
}
|
||||
And for the Commander X16:
|
||||
|
||||
cx16.set_irq(uword handler_address, boolean useKernal) ; vsync irq
|
||||
cx16.set_rasterirq(uword handler_address, uword rasterline) ; note: disables kernal irq handler! sys.wait() won't work anymore
|
||||
cx16.restore_irq() ; set everything back to the systems default irq handler
|
||||
@@ -50,7 +50,7 @@ Calling the routine is just a simple JSR instruction, but the other two work lik
|
||||
``asmsub`` routines
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
These are usually declarations of kernel (ROM) routines or low-level assembly only routines,
|
||||
These are usually declarations of kernal (ROM) routines or low-level assembly only routines,
|
||||
that have their arguments solely passed into specific registers.
|
||||
Sometimes even via a processor status flag such as the Carry flag.
|
||||
Return values also via designated registers.
|
||||
|
||||
+11
-29
@@ -2,21 +2,21 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- optimize assigning array and struct variables (multi-element assings -> memcopy)
|
||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
||||
- optimize several inner loops in gfx2
|
||||
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
|
||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)
|
||||
- add a flood fill routine to gfx2?
|
||||
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
||||
- refactor the asmgen into their own submodule?
|
||||
- refactor the compiler optimizers into their own submodule?
|
||||
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
||||
- optimize several inner loops in gfx2 (highres 4 color mode)
|
||||
- use the 65c02 bit clear/set/test instructions for single-bit operations
|
||||
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
|
||||
- add a flood fill routine to gfx2?
|
||||
- add sound to the cx16 tehtriz
|
||||
- add a f_seek() routine for the Cx16 that uses its seek dos api?
|
||||
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
|
||||
- add a compiler option to not remove unused subroutines. this allows for building library programs
|
||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
|
||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
- some support for recursive subroutines?
|
||||
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
|
||||
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
|
||||
@@ -28,30 +28,12 @@ More optimizations
|
||||
Add more compiler optimizations to the existing ones.
|
||||
|
||||
- further optimize assignment codegeneration, such as the following:
|
||||
- rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?)
|
||||
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
||||
- more optimizations on the language AST level
|
||||
- more optimizations on the final assembly source level
|
||||
|
||||
|
||||
Eval stack redesign? (lot of work)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The eval stack is now a split lsb/msb stack using X as the stackpointer.
|
||||
Is it easier/faster to just use a single page unsplit stack?
|
||||
It could then even be moved into the zeropage to reduce code size and slowness.
|
||||
|
||||
Or just move the LSB portion into a slab of the zeropage.
|
||||
|
||||
Allocate a fixed word in ZP that is the Top Of Stack value so we can always operate on TOS directly
|
||||
without having to index with X into the eval stack all the time?
|
||||
This could GREATLY improve code size and speed for operations that work on just a single value.
|
||||
|
||||
|
||||
Bug Fixing
|
||||
^^^^^^^^^^
|
||||
Ofcourse there are always bugs to fix ;)
|
||||
|
||||
|
||||
Misc
|
||||
^^^^
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
%import textio
|
||||
%import string
|
||||
|
||||
; Animal guessing game where the computer gets smarter every time.
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
|
||||
main {
|
||||
const ubyte database_size = 100
|
||||
|
||||
uword animal_names_buf
|
||||
uword questions_buf
|
||||
uword animal_names_ptr
|
||||
uword questions_ptr
|
||||
|
||||
uword[database_size] animals
|
||||
uword[database_size] questions
|
||||
uword[database_size] answers_questions
|
||||
uword[database_size] answers_animals
|
||||
ubyte new_animal_number
|
||||
ubyte new_question_number
|
||||
str userinput = "x"*80
|
||||
|
||||
sub start() {
|
||||
; initialize the database
|
||||
animal_names_buf = memory("animalnames", 500)
|
||||
questions_buf = memory("questions", 2000)
|
||||
animal_names_ptr = animal_names_buf
|
||||
questions_ptr = questions_buf
|
||||
|
||||
animals[0] = 0
|
||||
animals[1] = "dolphin"
|
||||
animals[2] = "eagle"
|
||||
animals[3] = "horse"
|
||||
new_animal_number = 4
|
||||
|
||||
questions[0] = 0
|
||||
questions[1] = "does it swim"
|
||||
questions[2] = "can it fly"
|
||||
new_question_number = 3
|
||||
|
||||
answers_questions[0] = mkword(0, 0)
|
||||
answers_questions[1] = mkword(0, 2)
|
||||
answers_questions[2] = mkword(0, 0)
|
||||
|
||||
answers_animals[0] = mkword(0, 0)
|
||||
answers_animals[1] = mkword(1, 0)
|
||||
answers_animals[2] = mkword(2, 3)
|
||||
|
||||
; play the game
|
||||
game()
|
||||
}
|
||||
|
||||
sub game() {
|
||||
repeat {
|
||||
ubyte current_question = 1
|
||||
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
|
||||
ubyte guessed = false
|
||||
while not guessed {
|
||||
txt.print(questions[current_question])
|
||||
txt.print("? ")
|
||||
if txt.input_chars(userinput) {
|
||||
txt.nl()
|
||||
ubyte animal_number
|
||||
if userinput[0]=='y' {
|
||||
animal_number = msb(answers_animals[current_question])
|
||||
if animal_number {
|
||||
guess(current_question, true, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = msb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else if userinput[0]=='n' {
|
||||
animal_number = lsb(answers_animals[current_question])
|
||||
if animal_number {
|
||||
guess(current_question, false, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = lsb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else {
|
||||
txt.print("answer (y)es or (n)o please.\n")
|
||||
}
|
||||
} else
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub guess(ubyte question_number, ubyte given_answer_yesno, ubyte animal_number) {
|
||||
txt.print("is it a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print("? ")
|
||||
txt.input_chars(userinput)
|
||||
if userinput[0] == 'y' {
|
||||
txt.print("\n\nsee, i knew it!\n")
|
||||
return
|
||||
}
|
||||
|
||||
str name = "x"*30
|
||||
txt.print("\n\ni give up. what is it? ")
|
||||
txt.input_chars(name)
|
||||
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print(" and a ")
|
||||
txt.print(name)
|
||||
txt.print("? ")
|
||||
txt.input_chars(userinput)
|
||||
txt.print("\nfor a ")
|
||||
txt.print(name)
|
||||
txt.print(", what is the answer to that; yes or no? ")
|
||||
str answer = "x"*10
|
||||
txt.input_chars(answer)
|
||||
|
||||
animals[new_animal_number] = animal_names_ptr
|
||||
questions[new_question_number] = questions_ptr
|
||||
animal_names_ptr += string.copy(name, animal_names_ptr)+1 ; store animal name in buffer
|
||||
questions_ptr += string.copy(userinput, questions_ptr)+1 ; store question in buffer
|
||||
|
||||
answers_questions[new_question_number] = mkword(0, 0)
|
||||
if answer[0]=='y'
|
||||
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
|
||||
else
|
||||
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
|
||||
|
||||
uword previous_animals = answers_animals[question_number]
|
||||
uword previous_questions = answers_questions[question_number]
|
||||
if given_answer_yesno {
|
||||
answers_animals[question_number] = mkword(0, lsb(previous_animals))
|
||||
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
|
||||
} else {
|
||||
answers_animals[question_number] = mkword(msb(previous_animals), 0)
|
||||
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
|
||||
}
|
||||
|
||||
new_animal_number++
|
||||
new_question_number++
|
||||
|
||||
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ main {
|
||||
|
||||
c64.SCROLX &= %11110111 ; 38 column mode
|
||||
|
||||
c64.set_rasterirq(200) ; enable animation
|
||||
c64.set_rasterirq(&irq.irq, 200, false) ; enable animation via raster interrupt
|
||||
|
||||
ubyte target_height = 10
|
||||
ubyte active_height = 24
|
||||
|
||||
@@ -7,7 +7,7 @@ main {
|
||||
|
||||
sub start() {
|
||||
txt.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||
c64.set_rasterirq(60) ; enable raster irq
|
||||
c64.set_rasterirq(&irq.irq, 60, true) ; enable playback via raster irq
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
c1541 -format examples,01 d64 examples.d64
|
||||
for filename in *.prg; do
|
||||
c1541 examples.d64 -write $filename ${filename%.*}
|
||||
done
|
||||
c1541 examples.d64 -dir
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
These .prg files are the compiled versions of most of the examples.
|
||||
They can be loaded and ran on a C64 or in an emulator, for example in Vice:
|
||||
|
||||
x64sc hello.prg
|
||||
|
||||
will load and run the hello example.
|
||||
|
||||
You can also attach the d64 disk image and load the examples from there.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
%target cx16
|
||||
%import textio
|
||||
%import palette
|
||||
%import string
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
%target cx16
|
||||
%import textio
|
||||
%import palette
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
cx16.screen_set_mode(0)
|
||||
txt.plot(14,14)
|
||||
txt.print("raster bars!")
|
||||
|
||||
cx16.rombank(0) ; switch to kernal rom for faster irq handling
|
||||
cx16.set_rasterirq(&irq.irq, 0)
|
||||
|
||||
repeat {
|
||||
; don't exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
irq {
|
||||
uword[32] colors = [
|
||||
$011, $112, $213, $214,
|
||||
$315, $316, $417, $418,
|
||||
$519, $51a, $62b, $62c,
|
||||
$73d, $73e, $84f, $94f,
|
||||
$93e, $83d, $82c, $72b,
|
||||
$71a, $619, $618, $517,
|
||||
$516, $415, $414, $313,
|
||||
$312, $211, $100, $000
|
||||
]
|
||||
|
||||
uword next_irq_line = 0
|
||||
ubyte color_idx = 0
|
||||
ubyte yanim = 0
|
||||
const ubyte barheight = 4
|
||||
|
||||
sub irq() {
|
||||
uword c = colors[color_idx]
|
||||
color_idx++
|
||||
color_idx &= 31
|
||||
|
||||
if color_idx==0 {
|
||||
yanim++
|
||||
next_irq_line = $0030 + sin8u(yanim)
|
||||
} else {
|
||||
next_irq_line += barheight
|
||||
}
|
||||
|
||||
palette.set_color(0, c)
|
||||
|
||||
cx16.set_rasterline(next_irq_line)
|
||||
}
|
||||
}
|
||||
+105
-87
@@ -5,7 +5,7 @@
|
||||
; wall kick rotations
|
||||
; shows next piece
|
||||
; staged speed increase
|
||||
; TODO: replicate the simple sound effects from the C64 version
|
||||
; simplistic sound effects (Vera PSG)
|
||||
|
||||
|
||||
%target cx16
|
||||
@@ -33,7 +33,7 @@ main {
|
||||
|
||||
|
||||
sub start() {
|
||||
cx16.screen_set_mode(0) ; low res
|
||||
void cx16.screen_set_mode(0) ; low res
|
||||
sound.init()
|
||||
newGame()
|
||||
drawBoard()
|
||||
@@ -49,11 +49,11 @@ waitkey:
|
||||
if time_lo>=(60-4*speedlevel) {
|
||||
c64.SETTIM(0,0,0)
|
||||
|
||||
drawBlock(xpos, ypos, 32) ; hide block
|
||||
drawBlock(xpos, ypos, true) ; hide block
|
||||
if blocklogic.noCollision(xpos, ypos+1) {
|
||||
; slowly move the block down
|
||||
ypos++
|
||||
drawBlock(xpos, ypos, 160) ; show block on new position
|
||||
drawBlock(xpos, ypos, false) ; 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.
|
||||
@@ -84,31 +84,31 @@ waitkey:
|
||||
}
|
||||
|
||||
sub move_left() {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
if blocklogic.noCollision(xpos-1, ypos) {
|
||||
xpos--
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
|
||||
sub move_right() {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
if blocklogic.noCollision(xpos+1, ypos) {
|
||||
xpos++
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
|
||||
sub move_down_faster() {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
if blocklogic.noCollision(xpos, ypos+1) {
|
||||
ypos++
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
|
||||
sub drop_down_immediately() {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
ubyte dropypos
|
||||
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
|
||||
if not blocklogic.noCollision(xpos, dropypos) {
|
||||
@@ -120,7 +120,7 @@ waitkey:
|
||||
if dropypos>ypos {
|
||||
ypos = dropypos
|
||||
sound.blockdrop()
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
checkForLines()
|
||||
spawnNextBlock()
|
||||
score++
|
||||
@@ -137,7 +137,7 @@ waitkey:
|
||||
'z' -> {
|
||||
; no joystick equivalent (there is only 1 fire button)
|
||||
; rotate counter clockwise
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
if blocklogic.canRotateCCW(xpos, ypos) {
|
||||
blocklogic.rotateCCW()
|
||||
sound.blockrotate()
|
||||
@@ -152,11 +152,11 @@ waitkey:
|
||||
blocklogic.rotateCCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
'x' -> {
|
||||
; rotate clockwise
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
if blocklogic.canRotateCW(xpos, ypos) {
|
||||
blocklogic.rotateCW()
|
||||
sound.blockrotate()
|
||||
@@ -171,21 +171,21 @@ waitkey:
|
||||
blocklogic.rotateCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
'c' -> {
|
||||
; hold
|
||||
if holdingAllowed {
|
||||
sound.swapping()
|
||||
if holding<7 {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
ubyte newholding = blocklogic.currentBlockNum
|
||||
swapBlock(holding)
|
||||
holding = newholding
|
||||
holdingAllowed = false
|
||||
} else {
|
||||
holding = blocklogic.currentBlockNum
|
||||
drawBlock(xpos, ypos, 32)
|
||||
drawBlock(xpos, ypos, true)
|
||||
spawnNextBlock()
|
||||
}
|
||||
drawHoldBlock()
|
||||
@@ -206,7 +206,7 @@ waitkey:
|
||||
num_lines++
|
||||
ubyte x
|
||||
for x in boardOffsetX to boardOffsetX+boardWidth-1
|
||||
txt.setcc(x, linepos, 160, 1)
|
||||
txt.setcc2(x, linepos, 160, 1)
|
||||
}
|
||||
}
|
||||
if num_lines {
|
||||
@@ -214,7 +214,7 @@ waitkey:
|
||||
sound.lineclear_big()
|
||||
else
|
||||
sound.lineclear()
|
||||
sys.wait(20) ; slight delay to flash the line
|
||||
sys.wait(10) ; slight delay to flash the line
|
||||
for linepos in complete_lines
|
||||
if linepos and blocklogic.isLineFull(linepos)
|
||||
blocklogic.collapse(linepos)
|
||||
@@ -272,7 +272,7 @@ waitkey:
|
||||
blocklogic.newCurrentBlock(newblock)
|
||||
xpos = startXpos
|
||||
ypos = startYpos
|
||||
drawBlock(xpos, ypos, 160)
|
||||
drawBlock(xpos, ypos, false)
|
||||
}
|
||||
|
||||
sub spawnNextBlock() {
|
||||
@@ -358,14 +358,14 @@ waitkey:
|
||||
const ubyte nextBlockYpos = 5
|
||||
ubyte x
|
||||
for x in nextBlockXpos+3 downto nextBlockXpos {
|
||||
txt.setcc(x, nextBlockYpos, ' ', 0)
|
||||
txt.setcc(x, nextBlockYpos+1, ' ', 0)
|
||||
txt.setcc2(x, nextBlockYpos, ' ', 0)
|
||||
txt.setcc2(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)
|
||||
drawBlock(nextBlockXpos, nextBlockYpos, false)
|
||||
blocklogic.newCurrentBlock(prev)
|
||||
}
|
||||
|
||||
@@ -374,24 +374,35 @@ waitkey:
|
||||
const ubyte holdBlockYpos = 6
|
||||
ubyte x
|
||||
for x in holdBlockXpos+3 downto holdBlockXpos {
|
||||
txt.setcc(x, holdBlockYpos, '@', 0)
|
||||
txt.setcc(x, holdBlockYpos+1, '@', 0)
|
||||
txt.setcc2(x, holdBlockYpos, '@', 0)
|
||||
txt.setcc2(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)
|
||||
drawBlock(holdBlockXpos, holdBlockYpos, false)
|
||||
blocklogic.newCurrentBlock(prev)
|
||||
}
|
||||
}
|
||||
|
||||
sub drawBlock(ubyte x, ubyte y, ubyte character) {
|
||||
sub drawBlock(ubyte x, ubyte y, ubyte erase) {
|
||||
ubyte char = 79 ; top left edge
|
||||
if erase
|
||||
char = 32 ; space
|
||||
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)
|
||||
if c {
|
||||
if erase
|
||||
c=0
|
||||
else {
|
||||
ubyte edge = blocklogic.edgecolors[c]
|
||||
c <<= 4
|
||||
c |= edge
|
||||
}
|
||||
txt.setcc2((i&3)+x, (i/4)+y, char, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,35 +415,37 @@ blocklogic {
|
||||
ubyte[16] rotated
|
||||
|
||||
; the 7 tetrominos
|
||||
ubyte[] blockI = [0,0,0,0, ; cyan ; note: special rotation (around matrix center)
|
||||
3,3,3,3,
|
||||
ubyte[] blockI = [0,0,0,0, ; blue note: special rotation (around matrix center)
|
||||
6,6,6,6,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockJ = [6,0,0,0, ; blue
|
||||
6,6,6,0,
|
||||
ubyte[] blockJ = [5,0,0,0, ; green
|
||||
5,5,5,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockL = [0,0,8,0, ; orange
|
||||
8,8,8,0,
|
||||
ubyte[] blockL = [0,0,2,0, ; red
|
||||
2,2,2,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockO = [0,7,7,0, ; yellow ; note: no rotation (square)
|
||||
0,7,7,0,
|
||||
ubyte[] blockO = [0,12,12,0, ; grey ; note: no rotation (square)
|
||||
0,12,12,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockS = [0,5,5,0, ; green
|
||||
5,5,0,0,
|
||||
ubyte[] blockS = [0,11,11,0, ; dark grey
|
||||
11,11,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockT = [0,4,0,0, ; purple
|
||||
4,4,4,0,
|
||||
ubyte[] blockT = [0,9,0,0, ; brown
|
||||
9,9,9,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[] blockZ = [2,2,0,0, ; red
|
||||
0,2,2,0,
|
||||
ubyte[] blockZ = [4,4,0,0, ; purple
|
||||
0,4,4,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
|
||||
ubyte[16] edgecolors = [11, 1, 10, 0, 10, 13, 14, 1, 7, 7, 11, 12, 15, 1, 1, 1] ; highlighed colors for the edges
|
||||
|
||||
uword[] blocks = [&blockI, &blockJ, &blockL, &blockO, &blockS, &blockT, &blockZ]
|
||||
|
||||
sub newCurrentBlock(ubyte block) {
|
||||
@@ -545,9 +558,9 @@ blocklogic {
|
||||
}
|
||||
|
||||
sub isGameOver(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
main.drawBlock(xpos, ypos, 32)
|
||||
main.drawBlock(xpos, ypos, true)
|
||||
ubyte result = ypos==main.startYpos and not noCollision(xpos, ypos+1)
|
||||
main.drawBlock(xpos, ypos, 160)
|
||||
main.drawBlock(xpos, ypos, false)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -566,7 +579,7 @@ blocklogic {
|
||||
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)
|
||||
txt.setcc2(x, ypos, char, color)
|
||||
}
|
||||
ypos--
|
||||
}
|
||||
@@ -575,68 +588,73 @@ blocklogic {
|
||||
|
||||
|
||||
sound {
|
||||
; TODO stubs for now
|
||||
sub init() {
|
||||
; c64.MVOL = 15
|
||||
cx16.vpoke(1, $f9c2, %00111111) ; volume max, no channels
|
||||
}
|
||||
|
||||
sub blockrotate() {
|
||||
; soft click
|
||||
; c64.MVOL = 5
|
||||
; c64.AD1 = %00100010
|
||||
; c64.SR1 = %00000000
|
||||
; c64.FREQ1 = 15600
|
||||
; c64.CR1 = %10000000
|
||||
; c64.CR1 = %10000001
|
||||
; soft click/"tschk" sound
|
||||
uword freq = 15600
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11110000) ; half volume
|
||||
cx16.vpoke(1, $f9c3, %11000000) ; noise waveform
|
||||
sys.wait(2)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
|
||||
sub blockdrop() {
|
||||
; swish
|
||||
; c64.MVOL = 5
|
||||
; c64.AD1 = %01010111
|
||||
; c64.SR1 = %00000000
|
||||
; c64.FREQ1 = 4600
|
||||
; c64.CR1 = %10000000
|
||||
; c64.CR1 = %10000001
|
||||
uword freq = 4600
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11110000) ; half volume
|
||||
cx16.vpoke(1, $f9c3, %11000000) ; noise waveform
|
||||
sys.wait(6)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
|
||||
sub swapping() {
|
||||
; beep
|
||||
; c64.MVOL = 8
|
||||
; c64.AD1 = %01010111
|
||||
; c64.SR1 = %00000000
|
||||
; c64.FREQ1 = 5500
|
||||
; c64.CR1 = %00010000
|
||||
; c64.CR1 = %00010001
|
||||
uword freq = 1500
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11110000) ; half volume
|
||||
cx16.vpoke(1, $f9c3, %10000000) ; triangle waveform
|
||||
sys.wait(6)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
|
||||
sub lineclear() {
|
||||
; explosion
|
||||
; c64.MVOL = 15
|
||||
; c64.AD1 = %01100110
|
||||
; c64.SR1 = %00000000
|
||||
; c64.FREQ1 = 1600
|
||||
; c64.CR1 = %10000000
|
||||
; c64.CR1 = %10000001
|
||||
uword freq = 1400
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11111111) ; max volume
|
||||
cx16.vpoke(1, $f9c3, %11000000) ; noise waveform
|
||||
sys.wait(8)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
|
||||
sub lineclear_big() {
|
||||
; big explosion
|
||||
; c64.MVOL = 15
|
||||
; c64.AD1 = %01101010
|
||||
; c64.SR1 = %00000000
|
||||
; c64.FREQ1 = 2600
|
||||
; c64.CR1 = %10000000
|
||||
; c64.CR1 = %10000001
|
||||
uword freq = 2500
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11111111) ; max volume
|
||||
cx16.vpoke(1, $f9c3, %11000000) ; noise waveform
|
||||
sys.wait(30)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
|
||||
sub gameover() {
|
||||
; buzz
|
||||
; c64.MVOL = 15
|
||||
; c64.FREQ2 = 600
|
||||
; c64.AD2 = %00111010
|
||||
; c64.SR2 = %00000000
|
||||
; c64.CR2 = %00110000
|
||||
; c64.CR2 = %00110001
|
||||
; attempt at buzz/boing (but can't get the sawtooth/triangle combined waveform of the sid)
|
||||
uword freq = 200
|
||||
cx16.vpoke(1, $f9c0, lsb(freq))
|
||||
cx16.vpoke(1, $f9c1, msb(freq))
|
||||
cx16.vpoke(1, $f9c2, %11111111) ; max volume
|
||||
cx16.vpoke(1, $f9c3, %01000000) ; sawtooth waveform
|
||||
sys.wait(30)
|
||||
cx16.vpoke(1, $f9c2, 0) ; shut off
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,115 @@
|
||||
%target cx16
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
; todo feedback: new routines at the end of the jump table so existing jump vectors remain unchanged
|
||||
|
||||
txt.lowercase()
|
||||
|
||||
vtui.initialize()
|
||||
vtui.screen_set(2)
|
||||
vtui.clr_scr('%', $50)
|
||||
vtui.gotoxy(5,5)
|
||||
vtui.fill_box(':', 70, 50, $c6)
|
||||
vtui.gotoxy(10,10)
|
||||
vtui.border(1, 40, 6, $47)
|
||||
vtui.gotoxy(12,12)
|
||||
vtui.print_str(@"Hello, world! vtui from Prog8!", $f2, $80)
|
||||
vtui.gotoxy(12,13)
|
||||
vtui.print_str("Hello, world! vtui from Prog8!", $f2, $00)
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub logo_mover() {
|
||||
ubyte xcoord
|
||||
ubyte ycoord
|
||||
ubyte newx
|
||||
ubyte newy
|
||||
|
||||
vtui.initialize()
|
||||
;vtui.screen_set(2)
|
||||
vtui.gotoxy(30, 32)
|
||||
vtui.print_str("arrow keys to move!", $61, 0)
|
||||
vtui.gotoxy(0, 0)
|
||||
vtui.save_rect(1, 1, $0000, 7, 7)
|
||||
vtui.gotoxy(0, 0)
|
||||
vtui.save_rect(1, 1, $0100, 7, 7)
|
||||
|
||||
char_loop:
|
||||
ubyte char = c64.GETIN()
|
||||
if not char
|
||||
goto char_loop
|
||||
|
||||
when char {
|
||||
$91 -> {
|
||||
if newy {
|
||||
newy--
|
||||
move_logo()
|
||||
}
|
||||
}
|
||||
$11 -> {
|
||||
if newy<53 {
|
||||
newy++
|
||||
move_logo()
|
||||
}
|
||||
}
|
||||
$9d -> {
|
||||
if newx {
|
||||
newx--
|
||||
move_logo()
|
||||
}
|
||||
}
|
||||
$1d -> {
|
||||
if newx<70 {
|
||||
newx++
|
||||
move_logo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto char_loop
|
||||
|
||||
sub move_logo() {
|
||||
vtui.gotoxy(xcoord, ycoord)
|
||||
vtui.rest_rect(1, 1, $0100, 7, 7)
|
||||
vtui.gotoxy(newx, newy)
|
||||
vtui.save_rect(1, 1, $0100, 7, 7)
|
||||
vtui.gotoxy(newx, newy)
|
||||
vtui.rest_rect(1, 1, $0000, 7, 7)
|
||||
xcoord = newx
|
||||
ycoord = newy
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
vtui $1000 {
|
||||
|
||||
%asmbinary "VTUI0.6.BIN", 2 ; skip the 2 dummy load address bytes
|
||||
|
||||
; NOTE: base address $1000 here must be the same as the block's memory address, for obvious reasons!
|
||||
romsub $1000 = initialize() clobbers(A, X, Y)
|
||||
romsub $1002 = screen_set(ubyte mode @A) clobbers(A, X, Y)
|
||||
romsub $1005 = set_bank(ubyte bank @Pc) clobbers(A)
|
||||
romsub $1008 = set_stride(ubyte stride @A) clobbers(A)
|
||||
romsub $100b = set_decr(ubyte incrdecr @Pc) clobbers(A)
|
||||
romsub $100e = clr_scr(ubyte char @A, ubyte colors @X) clobbers(Y)
|
||||
romsub $1011 = gotoxy(ubyte column @A, ubyte row @Y)
|
||||
romsub $1014 = plot_char(ubyte char @A, ubyte colors @X)
|
||||
romsub $1017 = scan_char() -> ubyte @A, ubyte @X
|
||||
romsub $101a = hline(ubyte char @A, ubyte length @Y, ubyte colors @X) clobbers(A)
|
||||
romsub $101d = vline(ubyte char @A, ubyte height @Y, ubyte colors @X) clobbers(A)
|
||||
romsub $1020 = print_str(str string @R0, ubyte colors @X, ubyte convertchars @A) clobbers(A, Y)
|
||||
romsub $1023 = fill_box(ubyte char @A, ubyte width @R1, ubyte height @R2, ubyte colors @X) clobbers(A, Y)
|
||||
romsub $1026 = pet2scr(ubyte char @A) -> ubyte @A
|
||||
romsub $1029 = scr2pet(ubyte char @A) -> ubyte @A
|
||||
romsub $102c = border(ubyte mode @A, ubyte width @R1, ubyte height @R2, ubyte colors @X) clobbers(Y) ; NOTE: mode 6 means 'custom' characters taken from r3 - r6
|
||||
romsub $102f = save_rect(ubyte ramtype @A, ubyte vbank @Pc, uword address @R0, ubyte width @R1, ubyte height @R2) clobbers(A, X, Y)
|
||||
romsub $1032 = rest_rect(ubyte ramtype @A, ubyte vbank @Pc, uword address @R0, ubyte width @R1, ubyte height @R2) clobbers(A, X, Y)
|
||||
}
|
||||
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user