Compare commits

...
33 Commits
Author SHA1 Message Date
Irmen de Jong 0d7a291b81 regenerated example disk , version 5.3 2020-12-08 23:15:31 +01:00
Irmen de Jong 2265ae9600 optimized setting word values into array if index is fixed number 2020-12-08 22:54:20 +01:00
Irmen de Jong cba502e87a fixed crash when trying to assign a string literal to an array element in a string-array 2020-12-08 22:27:42 +01:00
Irmen de Jong ac94236614 fixed compiler crash when declaring a str(pointer) array without initializer 2020-12-08 22:19:11 +01:00
Irmen de Jong ddf1be2a13 status condition couldn't properly be tested because restoring the X register clobbers the status flag 2020-12-08 22:15:07 +01:00
Irmen de Jong b7694686c2 optimized code for branches containing just a goto or break statement 2020-12-08 22:00:52 +01:00
Irmen de Jong 63332c0530 fix wrong branch instructions for some if_xxx 2020-12-08 21:29:40 +01:00
Irmen de Jong 8a504f8eee fixed compiler crash: when passing the name of a subroutine instead of an array or string to an UWORD parameter
now allows taking the address of a subroutine &routine
2020-12-08 21:17:31 +01:00
Irmen de Jong 106fc5daa4 tweak 2020-12-08 03:39:45 +01:00
Irmen de Jong 7accb73993 iterative file listing instead 2020-12-08 03:34:45 +01:00
Irmen de Jong e9aa6a0956 TODOs 2020-12-08 02:20:24 +01:00
Irmen de Jong df20467e03 completed diskio file lister 2020-12-08 02:16:41 +01:00
Irmen de Jong ecbd9d739e completed diskio file lister 2020-12-08 01:34:08 +01:00
Irmen de Jong 8af17c295a fixed diskio directory block sizes 2020-12-08 01:02:38 +01:00
Irmen de Jong 329b28cad1 making diskio.listfiles 2020-12-07 23:49:34 +01:00
Irmen de Jong 452c29574d added optimized mul 320 routine 2020-12-07 22:55:16 +01:00
Irmen de Jong 5bedc1b333 remove test file 2020-12-06 18:40:47 +01:00
Irmen de Jong 0bf6d2f72c tweak 2020-12-06 18:38:27 +01:00
Irmen de Jong c09b8af491 optimized koalaviewer to plot 8 pixels at once in the loop 2020-12-06 18:25:01 +01:00
Irmen de Jong 260bcd3a55 added syntax error for non-constant array size declaration 2020-12-06 17:02:56 +01:00
Irmen de Jong 6b5211ad12 tweak word shift unroll 2020-12-06 08:36:19 +01:00
Irmen de Jong a92ec14989 use 'stz' more often on 65c02 cpu (cx16) 2020-12-06 08:30:13 +01:00
Irmen de Jong b3348eb22b formatting 2020-12-06 07:52:58 +01:00
Irmen de Jong bec5a261e5 optimizing koalaviewer 2020-12-06 07:47:54 +01:00
Irmen de Jong 4b53641e1d optimized text screen clear/fill and scrolling on c64 2020-12-06 01:16:31 +01:00
Irmen de Jong 00071d53d5 optimized disc (filled circle) drawing on c64, fixed off by 1 disc width in cx16 version 2020-12-06 00:33:32 +01:00
Irmen de Jong 6902834568 remove dummy argument for txt.scroll_XXXX() functions on cx16 2020-12-06 00:19:47 +01:00
Irmen de Jong fa2d87f3dd optimized disc (filled circle) drawing on cx16 2020-12-06 00:01:19 +01:00
Irmen de Jong 44019d1a61 strings and arrays are no longer directly assignable to an UWORD, you need an explicit & (address-of) now 2020-12-03 18:39:32 +01:00
Irmen de Jong 6f74fb49bd added c64colors module. added vpeek/vpoke to cx16 syslib. koalaviewer example now uses better c64 color palette. 2020-12-03 18:14:49 +01:00
Irmen de Jong a303b39cf0 added C64 'koala' image viewer example for Cx16 2020-12-03 16:02:51 +01:00
Irmen de Jong 3e63a29c59 diskio now properly closes files after a load or save 2020-12-03 16:01:58 +01:00
Irmen de Jong 261c0fc9b6 started adding syntax highlighting files 2020-12-02 20:48:50 +01:00
63 changed files with 934 additions and 309 deletions

No files matched your search

+6 -10
View File
@@ -158,21 +158,17 @@ graphics {
ubyte ycenter_plus_xx = ycenter + xx
ubyte ycenter_min_xx = ycenter - xx
for internal_plotx in xcenter to xcenter+xx {
internal_plotx = xcenter-xx
repeat xx*2+1 {
internal_plot(ycenter_plus_yy)
internal_plot(ycenter_min_yy)
internal_plotx++
}
for internal_plotx in xcenter-xx to xcenter-1 {
internal_plot(ycenter_plus_yy)
internal_plot(ycenter_min_yy)
}
for internal_plotx in xcenter to xcenter+yy {
internal_plot(ycenter_plus_xx)
internal_plot(ycenter_min_xx)
}
for internal_plotx in xcenter-yy to xcenter {
internal_plotx = xcenter-yy
repeat yy*2+1 {
internal_plot(ycenter_plus_xx)
internal_plot(ycenter_min_xx)
internal_plotx++
}
yy++
if decisionOver2<=0
+70 -62
View File
@@ -38,13 +38,13 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; ---- clear the character screen with the given fill character (leaves colors)
; (assumes screen matrix is at the default address)
%asm {{
ldy #0
_loop sta c64.Screen,y
sta c64.Screen+$0100,y
sta c64.Screen+$0200,y
sta c64.Screen+$02e8,y
iny
bne _loop
ldy #250
- sta c64.Screen+250*0-1,y
sta c64.Screen+250*1-1,y
sta c64.Screen+250*2-1,y
sta c64.Screen+250*3-1,y
dey
bne -
rts
}}
}
@@ -53,13 +53,13 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; ---- clear the character screen colors with the given color (leaves characters).
; (assumes color matrix is at the default address)
%asm {{
ldy #0
_loop sta c64.Colors,y
sta c64.Colors+$0100,y
sta c64.Colors+$0200,y
sta c64.Colors+$02e8,y
iny
bne _loop
ldy #250
- sta c64.Colors+250*0-1,y
sta c64.Colors+250*1-1,y
sta c64.Colors+250*2-1,y
sta c64.Colors+250*3-1,y
dey
bne -
rts
}}
}
@@ -83,29 +83,31 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and the color memory
ldx #0
ldy #38
-
.for row=0, row<=24, row+=1
lda c64.Colors + 40*row + 1,x
sta c64.Colors + 40*row,x
.next
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x
lda c64.Colors + 40*row + 1,x
sta c64.Colors + 40*row + 0,x
.next
inx
dey
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only the screen memory
ldx #0
ldy #38
-
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x
sta c64.Screen + 40*row,x
.next
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x
.next
inx
dey
bpl -
@@ -121,26 +123,28 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and the color memory
ldx #38
-
.for row=0, row<=24, row+=1
lda c64.Colors + 40*row + 0,x
sta c64.Colors + 40*row + 1,x
.next
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x
lda c64.Colors + 40*row + 0,x
sta c64.Colors + 40*row + 1,x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only the screen memory
ldx #38
-
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x
.next
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x
.next
dex
bpl -
@@ -155,26 +159,28 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and the color memory
ldx #39
-
.for row=1, row<=24, row+=1
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row-1),x
.next
.for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row-1),x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only the screen memory
ldx #39
-
.for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x
.next
.for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x
.next
dex
bpl -
@@ -189,26 +195,28 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and the color memory
ldx #39
-
.for row=23, row>=0, row-=1
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row+1),x
.next
.for row=23, row>=0, row-=1
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row+1),x
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only the screen memory
ldx #39
-
.for row=23, row>=0, row-=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x
.next
.for row=23, row>=0, row-=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x
.next
dex
bpl -
+87
View File
@@ -0,0 +1,87 @@
%target cx16
c64colors {
uword[] colorpalette_dark = [ ; this is a darker palette with more contrast
$000, ; 0 = black
$FFF, ; 1 = white
$632, ; 2 = red
$7AB, ; 3 = cyan
$638, ; 4 = purple
$584, ; 5 = green
$327, ; 6 = blue
$BC6, ; 7 = yellow
$642, ; 8 = orange
$430, ; 9 = brown
$965, ; 10 = light red
$444, ; 11 = dark grey
$666, ; 12 = medium grey
$9D8, ; 13 = light green
$65B, ; 14 = light blue
$999 ; 15 = light grey
]
uword[] colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
$000, ; 0 = black
$FFF, ; 1 = white
$833, ; 2 = red
$7cc, ; 3 = cyan
$839, ; 4 = purple
$5a4, ; 5 = green
$229, ; 6 = blue
$ef7, ; 7 = yellow
$852, ; 8 = orange
$530, ; 9 = brown
$c67, ; 10 = light red
$444, ; 11 = dark grey
$777, ; 12 = medium grey
$af9, ; 13 = light green
$76e, ; 14 = light blue
$bbb ; 15 = light grey
]
uword[] colorpalette_light = [ ; this is a lighter palette
$000, ; 0 = black
$FFF, ; 1 = white
$944, ; 2 = red
$7CC, ; 3 = cyan
$95A, ; 4 = purple
$6A5, ; 5 = green
$549, ; 6 = blue
$CD8, ; 7 = yellow
$963, ; 8 = orange
$650, ; 9 = brown
$C77, ; 10 = light red
$666, ; 11 = dark grey
$888, ; 12 = medium grey
$AE9, ; 13 = light green
$87C, ; 14 = light blue
$AAA ; 15 = light grey
]
ubyte color
sub set_palette_pepto() {
for color in 0 to 15 {
uword cc = colorpalette_pepto[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
sub set_palette_light() {
for color in 0 to 15 {
uword cc = colorpalette_light[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
sub set_palette_dark() {
for color in 0 to 15 {
uword cc = colorpalette_dark[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
}
+23 -35
View File
@@ -21,7 +21,7 @@ graphics {
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_set_mode(2)
txt.fill_screen(' ', 1) ; TODO doesn't seem to fully clear the text screen after returning from gfx mode
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
}
@@ -43,7 +43,7 @@ graphics {
;cx16.r1 = ycenter - radius/2
;cx16.r2 = radius*2
;cx16.r3 = radius*2
;cx16.GRAPH_draw_oval(false) ; TODO currently is not implemented on cx16, does a BRK
;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK
; Midpoint algorithm
ubyte @zp xx = radius
@@ -94,7 +94,7 @@ graphics {
; cx16.r1 = ycenter - radius/2
; cx16.r2 = radius*2
; cx16.r3 = radius*2
; cx16.GRAPH_draw_oval(true) ; TODO currently is not implemented on cx16, does a BRK
; cx16.GRAPH_draw_oval(true) ; currently this call is not implemented on cx16, does a BRK
ubyte xx = radius
ubyte yy = 0
@@ -107,42 +107,30 @@ graphics {
ubyte ycenter_min_xx = ycenter - xx
uword @zp plotx
for plotx in xcenter to xcenter+xx {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-xx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
repeat xx*2+1
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-xx
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
repeat xx*2+1
cx16.FB_set_pixel(1)
}
for plotx in xcenter-xx to xcenter-1 {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-yy
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
repeat yy*2+1
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-yy
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
repeat yy*2+1
cx16.FB_set_pixel(1)
}
for plotx in xcenter to xcenter+yy {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
}
for plotx in xcenter-yy to xcenter {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
}
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
+36 -1
View File
@@ -242,9 +242,44 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y)
; ---- end of kernal routines ----
; ---- utilities -----
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; -- get a byte from VERA's video memory
; note: inefficient when reading multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
sty cx16.VERA_ADDR_M
stx cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
rts
}}
}
sub vpoke(ubyte bank, uword address, ubyte value) {
; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
lda bank
and #1
sta cx16.VERA_ADDR_H
lda address
sta cx16.VERA_ADDR_L
lda address+1
sta cx16.VERA_ADDR_M
lda value
sta cx16.VERA_DATA0
rts
}}
}
; ---- system stuff -----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
+4 -8
View File
@@ -154,10 +154,9 @@ sub uppercase() {
cx16.screen_set_charset(2, 0) ; uppercase charset
}
asmsub scroll_left (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_left() clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -198,10 +197,9 @@ _lx ldx #0 ; modified
}}
}
asmsub scroll_right (ubyte dummy @ Pc) clobbers(A) {
asmsub scroll_right() clobbers(A) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -250,10 +248,9 @@ _lx ldx #0 ; modified
}}
}
asmsub scroll_up (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_up() clobbers(A, Y) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -300,10 +297,9 @@ _nextline
}}
}
asmsub scroll_down (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_down() clobbers(A, Y) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
+128 -7
View File
@@ -6,8 +6,8 @@
diskio {
sub directory(ubyte drivenumber) -> byte {
; -- Shows the directory contents of disk drive 8-11 (provide as argument).
sub directory(ubyte drivenumber) -> ubyte {
; -- Shows the directory contents of disk drive 8-11 (provide as argument). Returns success flag.
c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0)
@@ -25,7 +25,9 @@ diskio {
; while not key pressed / EOF encountered, read data.
ubyte status = c64.READST()
while not status {
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
ubyte low = c64.CHRIN()
ubyte high = c64.CHRIN()
txt.print_uw(mkword(high, low))
txt.chrout(' ')
ubyte @zp char
do {
@@ -57,9 +59,121 @@ io_error:
}
; internal variables for the iterative file lister
ubyte list_suffixmatch
ubyte list_in_progress = false
ubyte list_pattern_size
ubyte list_skip_disk_name
uword list_pattern
uword list_blocks
str list_filename = "????????????????"
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
; -- start an iterative file listing with optional prefix or suffix name matching.
lf_end_list()
list_pattern = pattern
list_suffixmatch = suffixmatch
list_skip_disk_name = true
list_in_progress = true
if pattern==0
list_pattern_size = 0
else
list_pattern_size = strlen(pattern)
c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0)
void c64.OPEN() ; open 1,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(1) ; use #1 as input channel
if_cs
goto io_error
repeat 4 {
void c64.CHRIN() ; skip the 4 prologue bytes
}
if c64.READST()==0
return true
io_error:
lf_end_list()
return false
}
sub lf_next_entry() -> ubyte {
; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks and list_filename.
; if it returns false though, there are no more entries (or an error occurred).
if not list_in_progress
return false
while not c64.READST() {
uword nameptr = &list_filename
ubyte blocks_lsb = c64.CHRIN()
ubyte blocks_msb = c64.CHRIN()
list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first "
while c64.CHRIN()!='\"' {
if c64.READST()
goto close_end
}
; read the filename
repeat {
ubyte char = c64.CHRIN()
if_z
break
if char=='\"'
break
@(nameptr) = char
nameptr++
}
@(nameptr) = 0
while c64.CHRIN() {
; read the rest of the entry until the end
}
void c64.CHRIN() ; skip 2 bytes
void c64.CHRIN()
if not list_skip_disk_name {
if list_pattern_size {
; do filename matching
if list_suffixmatch
rightstr(list_filename, filename, list_pattern_size)
else
leftstr(list_filename, filename, list_pattern_size)
if strcmp(filename, list_pattern)==0
return true
} else
return true
}
list_skip_disk_name = false
}
close_end:
lf_end_list()
return false
}
sub lf_end_list() {
; -- end an iterative file listing session (close channels).
if list_in_progress {
c64.CLRCHN()
c64.CLOSE(1)
list_in_progress = false
}
}
sub status(ubyte drivenumber) {
; -- display the disk drive's current status message
c64.SETNAM(0, $0000)
c64.SETNAM(0, filename)
c64.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15
if_cs
@@ -77,7 +191,7 @@ io_error:
}
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> byte {
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
c64.SETNAM(strlen(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0)
uword end_address = address + size
@@ -97,10 +211,14 @@ io_error:
plp
}}
ubyte result=0
if_cc
return c64.READST()==0
result = c64.READST()==0
return false
c64.CLRCHN()
c64.CLOSE(1)
return result
}
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
@@ -122,6 +240,9 @@ io_error:
+ ldx P8ZP_SCRATCH_REG
}}
c64.CLRCHN()
c64.CLOSE(1)
if end_of_load
return end_of_load - address_override
+51
View File
@@ -774,6 +774,31 @@ stack_mul_word_100 .proc
rts
.pend
stack_mul_word_320 .proc
; stackW = stackLo * 256 + stackLo * 64 (stackHi doesn't matter)
ldy P8ESTACK_LO+1,x
lda #0
sta P8ESTACK_HI+1,x
tya
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
sta P8ESTACK_LO+1,x
tya
clc
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
mul_byte_3 .proc
@@ -1241,6 +1266,32 @@ mul_word_100 .proc
rts
.pend
mul_word_320 .proc
; AY = A * 256 + A * 64 (msb doesn't matter)
sta P8ZP_SCRATCH_B1
ldy #0
sty 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
asl a
rol P8ZP_SCRATCH_REG
pha
clc
lda P8ZP_SCRATCH_B1
adc P8ZP_SCRATCH_REG
tay
pla
rts
.pend
; ----------- end optimized multiplications -----------
+1 -1
View File
@@ -1 +1 @@
5.2
5.3
+2 -2
View File
@@ -30,8 +30,8 @@ enum class DataType {
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR || targetType == UWORD
in ArrayDatatypes -> targetType == this || targetType == UWORD
STR -> targetType == STR
in ArrayDatatypes -> targetType == this
else -> false
}
+26 -24
View File
@@ -384,7 +384,7 @@ internal class AstChecker(private val program: Program,
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
errors.err("cannot assign value to string or array", assignment.value.position)
else
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
errors.err("value's type doesn't match target", assignment.value.position)
}
@@ -455,15 +455,12 @@ internal class AstChecker(private val program: Program,
override fun visit(addressOf: AddressOf) {
val variable=addressOf.identifier.targetVarDecl(program.namespace)
if(variable==null)
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
else {
if(variable.datatype !in ArrayDatatypes
&& variable.type!=VarDeclType.MEMORY
&& variable.struct == null
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
if(variable!=null
&& variable.datatype !in ArrayDatatypes
&& variable.type!=VarDeclType.MEMORY
&& variable.struct == null
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
errors.err("invalid pointer-of operand type", addressOf.position)
}
super.visit(addressOf)
}
@@ -604,23 +601,28 @@ internal class AstChecker(private val program: Program,
}
}
// array length limits
// array length limits and constant lenghts
if(decl.isArray) {
val length = decl.arraysize!!.constIndex() ?: 1
when (decl.datatype) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
if(length==0 || length>256)
err("string and byte array length must be 1-256")
val length = decl.arraysize!!.constIndex()
if(length==null)
err("array length must be a constant")
else {
when (decl.datatype) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
if (length == 0 || length > 256)
err("string and byte array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128)
err("word array length must be 1-128")
}
DataType.ARRAY_F -> {
if (length == 0 || length > 51)
err("float array length must be 1-51")
}
else -> {
}
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(length==0 || length>128)
err("word array length must be 1-128")
}
DataType.ARRAY_F -> {
if(length==0 || length>51)
err("float array length must be 1-51")
}
else -> {}
}
}
@@ -41,8 +41,9 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
val argITypes = call.args.map { it.inferType(program) }
if(argITypes.any { !it.isKnown })
throw FatalAstException("unknown dt")
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
if(firstUnknownDt>=0)
return "argument ${firstUnknownDt+1} invalid argument type"
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
val target = call.target.targetStatement(scope)
if (target is Subroutine) {
@@ -200,7 +200,7 @@ open class VarDecl(val type: VarDeclType,
fun defaultZero(dt: DataType, position: Position) = when(dt) {
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
DataType.UWORD, DataType.STR -> NumericLiteralValue(DataType.UWORD, 0, position)
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
else -> throw FatalAstException("can only determine default zero value for a numeric type")
@@ -757,6 +757,8 @@ class Subroutine(override val name: String,
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldPreserveStatusRegisterAfterCall() = asmReturnvaluesRegisters.any { it.statusflag != null } || shouldSaveX()
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
fun amountOfRtsInAsm(): Int = statements
.asSequence()
@@ -36,7 +36,7 @@ internal class AsmGen(private val program: Program,
// for expressions and augmented assignments:
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100)
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
private val assemblyLines = mutableListOf<String>()
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
@@ -814,12 +814,12 @@ internal class AsmGen(private val program: Program,
when (condition) {
BranchCondition.CS -> "bcc"
BranchCondition.CC -> "bcs"
BranchCondition.EQ, BranchCondition.Z -> "beq"
BranchCondition.NE, BranchCondition.NZ -> "bne"
BranchCondition.EQ, BranchCondition.Z -> "bne"
BranchCondition.NE, BranchCondition.NZ -> "beq"
BranchCondition.VS -> "bvc"
BranchCondition.VC -> "bvs"
BranchCondition.MI, BranchCondition.NEG -> "bmi"
BranchCondition.PL, BranchCondition.POS -> "bpl"
BranchCondition.MI, BranchCondition.NEG -> "bpl"
BranchCondition.PL, BranchCondition.POS -> "bmi"
}
} else {
when (condition) {
@@ -1071,26 +1071,48 @@ $counterVar .byte 0""")
val jump = stmt.truepart.statements.first() as? Jump
if(jump!=null) {
// branch with only a jump
// branch with only a jump (goto)
val instruction = branchInstruction(stmt.condition, false)
out(" $instruction ${getJumpTarget(jump)}")
translate(stmt.elsepart)
} else {
val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break
val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break
if(stmt.elsepart.containsNoCodeNorVars()) {
if(truePartIsJustBreak) {
// branch with just a break (jump out of loop)
val instruction = branchInstruction(stmt.condition, false)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
} else {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(elseLabel)
}
}
else if(truePartIsJustBreak) {
// branch with just a break (jump out of loop)
val instruction = branchInstruction(stmt.condition, false)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
translate(stmt.elsepart)
} else if(elsePartIsJustBreak) {
// branch with just a break (jump out of loop) but true/false inverted
val instruction = branchInstruction(stmt.condition, true)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
translate(stmt.truepart)
} else {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
val endLabel = makeLabel("branch_end")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(elseLabel)
} else {
val instruction = branchInstruction(stmt.condition, false)
val trueLabel = makeLabel("branch_true")
val endLabel = makeLabel("branch_end")
out(" $instruction $trueLabel")
translate(stmt.elsepart)
out(" jmp $endLabel")
out(trueLabel)
translate(stmt.truepart)
out(elseLabel)
translate(stmt.elsepart)
out(endLabel)
}
}
@@ -1206,7 +1228,12 @@ $label nop""")
internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word on stack
when(valueDt) {
DataType.UBYTE -> out(" lda #0 | sta P8ESTACK_HI+1,x")
DataType.UBYTE -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
out(" stz P8ESTACK_HI+1,x")
else
out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
else -> throw AssemblyError("need byte type")
}
@@ -1216,7 +1243,10 @@ $label nop""")
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
DataType.UBYTE -> {
out(" lda #0 | sta $asmvar+1")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
out(" stz $asmvar+1")
else
out(" lda #0 | sta $asmvar+1")
}
DataType.BYTE -> {
out("""
@@ -75,11 +75,11 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// the when statement (on bytes) generates a sequence of:
// lda $ce01,x
// cmp #$20
// lda $ce01,x
// cmp #$20
// beq check_prog8_s72choice_32
// lda $ce01,x
// cmp #$21
// lda $ce01,x
// cmp #$21
// beq check_prog8_s73choice_33
// the repeated lda can be removed
val mods = mutableListOf<Modification>()
@@ -1329,7 +1329,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true)
} else {
sub as Subroutine
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((_, reg) in returns) {
@@ -16,7 +16,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace)!!
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
if(preserveStatusRegisterAfterCall)
@@ -28,7 +28,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
val saveX = sub.shouldSaveX()
if(saveX)
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!)
@@ -142,7 +142,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> {
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null }
when (returnValue.first) {
@@ -725,10 +725,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
throw AssemblyError("can't store word into memory byte")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign address $sourceName to array ${target.asmVarname}")
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
assignRegisterpairWord(target, RegisterOrPair.AY)
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
@@ -755,10 +756,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(target.datatype) {
DataType.UWORD -> {
asmgen.out("""
lda #<$sourceName
ldy #>$sourceName
sta ${target.asmVarname}
sty ${target.asmVarname}+1
lda #<$sourceName
ldy #>$sourceName
sta ${target.asmVarname}
sty ${target.asmVarname}+1
""")
}
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
@@ -1110,21 +1111,28 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val sourceName = asmgen.asmVariableName(bytevar)
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | iny")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname},y")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> {
@@ -1136,12 +1144,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("target type isn't word")
}
@@ -1157,22 +1164,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
storeRegisterInMemoryAddress(register, target.memory!!)
}
TargetStorageKind.ARRAY -> {
when {
target.constArrayIndexValue!=null -> {
when (register) {
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
}
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
}
else -> {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
}
else {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> {
@@ -1231,20 +1236,30 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.ARRAY -> {
// TODO can be a bit more optimized if the array indexer is a number
when(regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
if (target.constArrayIndexValue!=null) {
val idx = target.constArrayIndexValue!! * 2
when (regs) {
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
else -> throw AssemblyError("expected reg pair")
}
}
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
else {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
}
}
TargetStorageKind.REGISTER -> {
when(regs) {
@@ -1392,14 +1407,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.ARRAY -> {
if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize()
asmgen.out("""
lda #0
sta ${target.asmVarname}+$indexValue
sta ${target.asmVarname}+$indexValue+1
sta ${target.asmVarname}+$indexValue+2
sta ${target.asmVarname}+$indexValue+3
sta ${target.asmVarname}+$indexValue+4
""")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out("""
stz ${target.asmVarname}+$indexValue
stz ${target.asmVarname}+$indexValue+1
stz ${target.asmVarname}+$indexValue+2
stz ${target.asmVarname}+$indexValue+3
stz ${target.asmVarname}+$indexValue+4
""")
else
asmgen.out("""
lda #0
sta ${target.asmVarname}+$indexValue
sta ${target.asmVarname}+$indexValue+1
sta ${target.asmVarname}+$indexValue+2
sta ${target.asmVarname}+$indexValue+3
sta ${target.asmVarname}+$indexValue+4
""")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
asmgen.out("""
@@ -1561,12 +1585,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
if (address != null) {
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${address.toHex()}
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
@@ -1578,12 +1601,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("word regs can only be pair")
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda ${address.toHex()}
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("other types aren't word")
}
@@ -1591,7 +1613,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta ${wordtarget.asmVarname} | lda #0 | sta ${wordtarget.asmVarname}+1")
asmgen.out(" sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
@@ -1607,7 +1633,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
TargetStorageKind.STACK -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
asmgen.out(" sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("other types aren't word")
}
@@ -1619,7 +1649,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val addressLv = addressExpr as? NumericLiteralValue
when {
addressLv != null -> {
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
@@ -643,14 +643,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name""")
}
"<<" -> {
if(value>=8) asmgen.out(" lda #0 | sta $name")
else repeat(value) { asmgen.out(" asl $name") }
if(value>=8) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
else repeat(value) { asmgen.out(" asl $name") }
}
">>" -> {
if(value>0) {
if (dt == DataType.UBYTE) {
if(value>=8) asmgen.out(" lda #0 | sta $name")
else repeat(value) { asmgen.out(" lsr $name") }
if(value>=8) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
else repeat(value) { asmgen.out(" lsr $name") }
} else {
when {
value>=8 -> asmgen.out("""
@@ -665,7 +675,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldy #$value
jsr math.lsr_byte_A
sta $name""")
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
}
}
}
@@ -858,9 +868,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
"<<" -> {
when {
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
value==8 -> asmgen.out(" lda $name | sta $name+1 | lda #0 | sta $name")
value>2 -> asmgen.out("""
value>=16 -> {
if(CompilationTarget.instance.machine.cpu == 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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
value>3 -> asmgen.out("""
ldy #$value
- asl $name
rol $name+1
@@ -874,8 +895,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if (value > 0) {
if(dt==DataType.UWORD) {
when {
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
value==8 -> asmgen.out(" lda $name+1 | sta $name | lda #0 | sta $name+1")
value>=16 -> {
if(CompilationTarget.instance.machine.cpu == 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(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
value>2 -> asmgen.out("""
ldy #$value
- lsr $name+1
@@ -921,41 +953,41 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when {
value == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name | stz $name+1")
asmgen.out(" stz $name | stz $name+1")
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value and 255 == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
asmgen.out(" lda #0 | sta $name")
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
}
value < 0x0100 -> {
asmgen.out(" lda $name | and #$value | sta $name")
asmgen.out(" lda $name | and #$value | sta $name")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
}
}
"^" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
}
}
"|" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
}
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
@@ -1019,11 +1051,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name+1""")
}
"*" -> {
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
else
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
asmgen.out("""
lda $otherName
sta P8ZP_SCRATCH_W1
lda #0
sta P8ZP_SCRATCH_W1+1
lda $name
ldy $name+1
jsr math.multiply_words
@@ -1068,12 +1101,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
"&" -> {
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes)
asmgen.out(" lda #0 | sta $name+1")
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
}
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1081,8 +1118,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"*" -> {
asmgen.out("""
lda $otherName
@@ -1322,8 +1359,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes)
asmgen.out(" lda #0 | sta $name+1")
if(dt in WordDatatypes) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
+11 -1
View File
@@ -54,7 +54,8 @@ diskio
------
Provides several routines that deal with disk drive I/O, such as:
- show directory
- list files on disk, optionally filtering by prefix or suffix
- show disk directory as-is
- display disk drive status
- load and save data from and to the disk
- delete and rename files on the disk
@@ -88,6 +89,15 @@ A 'fun' module that contains the Commander X16 logo and that allows you
to print it anywhere on the screen.
c64colors
---------
Available for the CommanderX16 target, a module that contains a few better
color palettes for how the colors of the VIC-II looked on the Commodore-64.
There are subroutines to activate one of the several palettes of your liking.
The Commander X16's default colors for this (the first 16 colors) are too saturated
and are quite different than how a C-64 looked.
prog8_lib
---------
Low level language support. You should not normally have to bother with this directly.
+9 -6
View File
@@ -309,18 +309,21 @@ read the syntax reference on strings.
.. hint::
Strings and uwords (=memory address) can often be interchanged.
Strings/arrays and uwords (=memory address) can often be interchanged.
An array of strings is actually an array of uwords where every element is the memory
address of the string. You can pass a memory address to assembly functions
that require a string as an argument.
For regular assignments you still need to use an explicit ``&`` (address-of) to take
the address of the string or array.
.. caution::
It's probably best to avoid changing strings after they've been created. This
includes changing certain letters by index, or by assigning a new value, or by
It's probably best to avoid changing the contents in strings and treat them as static.
This includes changing certain letters by index, or by assigning a new value, or by
modifying the string via other means for example ``substr`` function and its cousins.
This is because if your program exits and is restarted (without loading it again),
it will then start working with the changed strings instead of the original ones!
The same is true for arrays.
This is because the changes persist in memory. If your program exits and is restarted
(without reloading it from disk), it will then start working with the modified strings
instead of the original ones!
The same is true for arrays! So be careful to (re)initialize them if needed.
Structs
+1
View File
@@ -4,6 +4,7 @@ TODO
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
- Cx16 target: support full-screen 640x480 and 320x240 graphics? That requires our own custom graphics routines though to draw lines.
- 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 '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
+1 -1
View File
@@ -17,7 +17,7 @@ main {
c64.SCROLX &= %11110111 ; 38 column mode
c64.set_rasterirq(1) ; enable animation
c64.set_rasterirq(40) ; enable animation
ubyte target_height = 10
ubyte active_height = 24
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.
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.
+118
View File
@@ -0,0 +1,118 @@
%target cx16
%import graphics
%import textio
%import diskio
%import c64colors
main {
const uword load_location = $4000
sub start() {
str[] pictures = [
"i01-blubb-sphinx.bin",
"i02-bugjam-jsl.bin",
"i03-dinothawr-ar.bin",
"i04-fox-leon.bin",
"i05-hunter-agod.bin",
"i06-jazzman-jds.bin",
"i07-katakis-jegg.bin"
]
; set a better C64 color palette, the Cx16's default is too saturated
c64colors.set_palette_pepto()
graphics.enable_bitmap_mode()
repeat {
ubyte file_idx
for file_idx in 0 to len(pictures)-1 {
load_image(pictures[file_idx])
wait()
}
}
}
sub wait() {
uword jiffies = 0
c64.SETTIM(0,0,0)
while jiffies < 180 {
; read clock
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta jiffies
stx jiffies+1
ldx P8ZP_SCRATCH_REG
}}
}
}
sub load_image(uword filenameptr) {
uword length = diskio.load(8, filenameptr, load_location)
if length != 10001 {
txt.print_uw(length)
txt.print("\nload error\n")
diskio.status(8)
exit(1)
}
convert_koalapic()
}
sub convert_koalapic() {
ubyte cy
ubyte @zp cx
uword @zp cy_times_forty = 0
ubyte @zp d
uword bitmap_ptr = load_location
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
ubyte[8] pixels
for cy in 0 to 24*8 step 8 {
for cx in 0 to 39 {
for d in 0 to 7 {
cx16.r0 = cx as uword * 8
cx16.r1 = cy as uword + d
cx16.FB_cursor_position()
get_8_pixels()
cx16.r0 = &pixels
cx16.r1 = 8
cx16.FB_set_pixels()
}
}
cy_times_forty += 40
}
sub get_8_pixels() {
ubyte bm = @(bitmap_ptr)
ubyte @zp m = mcol(bm)
pixels[7] = m
pixels[6] = m
bm >>= 2
m = mcol(bm)
pixels[5] = m
pixels[4] = m
bm >>= 2
m = mcol(bm)
pixels[3] = m
pixels[2] = m
bm >>= 2
m = mcol(bm)
pixels[1] = m
pixels[0] = m
bitmap_ptr++
sub mcol(ubyte b) -> ubyte {
ubyte @zp color
when b & 3 {
0 -> color = @(load_location + 8000 + 1000 + 1000) & 15
1 -> color = @(load_location + 8000 + cy_times_forty + cx) >>4
2 -> color = @(load_location + 8000 + cy_times_forty + cx) & 15
else -> color = @(load_location + 8000 + 1000 + cy_times_forty + cx) & 15
}
return color
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
%import textio
%import diskio
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start() {
if diskio.lf_start_list(8, "cub", false) {
txt.print("\nfiles starting with 'cub':\n")
while diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
if diskio.lf_start_list(8, "gfx", true) {
txt.print("\nfiles ending with 'gfx':\n")
while diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
if diskio.lf_start_list(8, 0, false) {
txt.print("\nfirst 10 files:\n")
ubyte counter=0
while counter < 10 and diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
counter++
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
; test_stack.test()
}
}
+11 -15
View File
@@ -41,7 +41,7 @@ main {
rect(10, 10, 10, 10, false)
rect(6, 0, 16, 20, true)
; test_stack.test()
test_stack.test()
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
@@ -140,21 +140,17 @@ main {
ubyte xx
while x>=y {
for xx in cx to cx+x {
txt.setcc(xx, cy + y as ubyte, 81, 1)
txt.setcc(xx, cy - y as ubyte, 81, 2)
xx = cx-x
repeat 2*x+1 {
txt.setcc(xx, cy + y as ubyte, 81, 11)
txt.setcc(xx, cy - y as ubyte, 81, 12)
xx++
}
for xx in cx-x to cx-1 {
txt.setcc(xx, cy + y as ubyte, 81, 3)
txt.setcc(xx, cy - y as ubyte, 81, 4)
}
for xx in cx to cx+y {
txt.setcc(xx, cy + x as ubyte, 81, 5)
txt.setcc(xx, cy - x as ubyte, 81, 6)
}
for xx in cx-y to cx {
txt.setcc(xx, cy + x as ubyte, 81, 7)
txt.setcc(xx, cy - x as ubyte, 81, 8)
xx = cx-y
repeat 2*y+1 {
txt.setcc(xx, cy + x as ubyte, 81, 13)
txt.setcc(xx, cy - x as ubyte, 81, 14)
xx++
}
y++
if decisionOver2<=0
+15 -2
View File
@@ -1,11 +1,24 @@
%import textio
%import diskio
%import floats
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start() {
}
ubyte[] barr = [0,0,0]
uword[] warr = [0,0,0]
ubyte xx = 0
barr[1] = xx+9
warr[1] = &warr
txt.print_ub(barr[1])
txt.chrout('\n')
txt.print_uwhex(warr[1],1 )
txt.chrout('\n')
test_stack.test()
}
}
+23
View File
@@ -0,0 +1,23 @@
<filetype binary="false" description="Prog8 source file" name="Prog8">
<highlighting>
<options>
<option name="LINE_COMMENT" value=";" />
<option name="COMMENT_START" value="{{" />
<option name="COMMENT_END" value="}}" />
<option name="HEX_PREFIX" value="$" />
<option name="NUM_POSTFIXES" value="" />
<option name="HAS_BRACES" value="true" />
<option name="HAS_BRACKETS" value="true" />
<option name="HAS_PARENS" value="true" />
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
</highlighting>
<extensionMap>
<mapping ext="p8" />
<mapping ext="prog8" />
</extensionMap>
</filetype>
+8
View File
@@ -0,0 +1,8 @@
Prog8 syntax highlighting file for IntelliJ IDEA.
Copy the file Prog8.xml to your IDEA filetypes folder.
The exact path may vary with the version of the IDE,
but for me it is currently this on Linux:
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
+1
View File
@@ -0,0 +1 @@
TODO
+1
View File
@@ -0,0 +1 @@
TODO
+4
View File
@@ -0,0 +1,4 @@
A contributed syntax definition file for Sublime Text 3 can be obtained from:
https://github.com/akubiczek/Prog8-TmLanguage-VsCode/tree/master/sublime3
+5
View File
@@ -0,0 +1,5 @@
A contributed syntax definition file for Visual Studio Code can be obtained from:
https://github.com/akubiczek/Prog8-TmLanguage-VsCode
+1
View File
@@ -0,0 +1 @@
TODO