Compare commits

...
69 Commits
Author SHA1 Message Date
Irmen de Jong 52e8a44517 version 5.4 2020-12-15 22:59:02 +01:00
Irmen de Jong 59f33658ad removed the rom path argument for launching the x16emu which made it fail on a non-Linux system 2020-12-15 22:51:10 +01:00
Irmen de Jong e0315bffdc decided not to change mkword() again, added note to docs about argument order 2020-12-15 22:25:06 +01:00
Irmen de Jong cd28d0c0e0 tweak 2020-12-14 21:57:16 +01:00
Irmen de Jong 0baa2c8b23 fix oversight in binexpr operand swap that could result in suboptimal code 2020-12-14 21:37:40 +01:00
Irmen de Jong 4977d1fbd5 bit shift expressions are "expanded" to the target value's datatype, now also for subroutine arguments.
implemented word bit shifts by variable number of bits.
2020-12-14 20:44:48 +01:00
Irmen de Jong 3b7a92f1b4 adding strcopy() 2020-12-14 17:26:17 +01:00
Irmen de Jong f6920172dd image viewer tweaks 2020-12-14 15:36:15 +01:00
Irmen de Jong 93bfc8f5f4 rename 2020-12-14 14:30:55 +01:00
Irmen de Jong 39b7655264 imageviewer is now a single program 2020-12-14 14:30:18 +01:00
Irmen de Jong 8b75ceb412 diskio.list_files now has a bigger buffer to store more filenames (around 30-40 max) 2020-12-14 14:29:42 +01:00
Irmen de Jong c39fc4010d textio.clear_screen() now uses kernal routine to clear the text screen, this also resets the cursor to top left. 2020-12-14 14:28:53 +01:00
Irmen de Jong 8df778a515 fixed crash when importing modules from the same directory as the main program 2020-12-14 13:14:12 +01:00
Irmen de Jong 5134ea76bf added bmp viewer 2020-12-14 02:12:26 +01:00
Irmen de Jong 3ba37df29d added iff viewer 2020-12-13 19:42:30 +01:00
Irmen de Jong e221d674d9 pcxviewer done 2020-12-13 01:32:03 +01:00
Irmen de Jong 251f947293 fixed parameter signature for FB_set_8_pixels_opaque() (docs are wrong) 2020-12-12 03:32:01 +01:00
Irmen de Jong 41e1e1cbb0 adding pcxviewer 2020-12-12 02:40:54 +01:00
Irmen de Jong da1bc351d2 koalaviewer auto disk detect 2020-12-11 23:32:47 +01:00
Irmen de Jong 43c0afdea0 fixed strlen() to work on arguments other than just a variable 2020-12-11 23:32:29 +01:00
Irmen de Jong add5bfa2ec koalaviewer scans directory for *.koa 2020-12-11 23:00:58 +01:00
Irmen de Jong 34babfb5de added diskio.list_files(). ci-viewer now loads all *.ci files it finds. 2020-12-11 22:36:14 +01:00
Irmen de Jong 4f6c45c86c incremental file loading 2020-12-11 21:05:03 +01:00
Irmen de Jong e6220a464c using progend() to maximize amount of mem available to load image 2020-12-10 23:52:30 +01:00
Irmen de Jong 8dcd49934a added progend() builtin function 2020-12-10 23:33:45 +01:00
Irmen de Jong bedc3bdb56 allow bit shifting to be as large as the target variable's datatype 2020-12-10 22:49:27 +01:00
Irmen de Jong 83ceb0fde9 optimize various simple cases for '**' (pow) like 2**x => bitshift 2020-12-10 22:37:12 +01:00
Irmen de Jong 1d299c56e0 fix float '**' (pow) on cx16 2020-12-10 22:19:07 +01:00
Irmen de Jong 0d735c2ccc workaround for FB_set_pixels bug 2020-12-10 21:51:32 +01:00
Irmen de Jong 4094f89d4a not a bug 2020-12-10 03:22:43 +01:00
Irmen de Jong cf1e8b194a fix compiler crash for expressions of the form x = x and y (the logical booleans, not the bitwise) 2020-12-10 03:12:32 +01:00
Irmen de Jong 74e5644f55 working on CI viewer 2020-12-10 03:00:37 +01:00
Irmen de Jong b5dc5fc615 added iterative file loading to diskio 2020-12-10 00:58:59 +01:00
Irmen de Jong 7a7270d769 adding CI (CommanderX16 Image) file viewer 2020-12-10 00:03:47 +01:00
Irmen de Jong 7549ddcd2b added TODOs for missing assignments 2020-12-10 00:03:20 +01:00
Irmen de Jong 08f0303178 diskio status() now returns the status string instead of printing it 2020-12-10 00:02:21 +01:00
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
75 changed files with 2282 additions and 481 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
+16
View File
@@ -269,6 +269,22 @@ asmsub reset_system() {
}}
}
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta current_time
stx current_time+1
ldx P8ZP_SCRATCH_REG
}}
}
}
asmsub disable_runstop_and_charsetswitch() {
%asm {{
lda #$80
+71 -63
View File
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
sub clear_screen() {
clear_screenchars(' ')
txt.chrout(147)
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
@@ -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
}
}
}
+1
View File
@@ -64,6 +64,7 @@ romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += sign
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
; note: there is no FPWR() on the Cx16
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
+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
+89 -2
View File
@@ -220,7 +220,7 @@ romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L
romsub $ff14 = FB_set_8_pixels_opaque(ubyte mask @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses r0L=pattern
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
@@ -242,9 +242,96 @@ 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
}}
}
sub FB_set_pixels_from_buf(uword buffer, uword count) {
%asm {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
; See https://github.com/commanderx16/x16-rom/issues/179
phx
lda buffer
ldy buffer+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
jsr _pixels
plx
rts
_pixels lda count+1
beq +
ldx #0
- jsr _loop
inc P8ZP_SCRATCH_W1+1
dec count+1
bne -
+ ldx count
_loop ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta cx16.VERA_DATA0
iny
dex
bne -
rts
}}
}
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
phx
jsr c64.RDTIM
sta current_time
stx current_time+1
plx
}}
}
}
; ---- system stuff -----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
+5 -9
View File
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
sub clear_screen() {
clear_screenchars(' ')
txt.chrout(147)
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
@@ -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
+228 -16
View File
@@ -6,15 +6,15 @@
diskio {
sub directory(ubyte drivenumber) -> byte {
; -- Shows the directory contents of disk drive 8-11 (provide as argument).
sub directory(ubyte drivenumber) -> ubyte {
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0)
void c64.OPEN() ; open 1,8,0,"$"
c64.SETLFS(13, drivenumber, 0)
void c64.OPEN() ; open 13,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(1) ; use #1 as input channel
void c64.CHKIN(13) ; use #13 as input channel
if_cs
goto io_error
@@ -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 {
@@ -37,14 +39,14 @@ diskio {
void c64.CHRIN()
status = c64.READST()
void c64.STOP()
if_nz
if_z
break
}
io_error:
status = c64.READST()
c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(1)
c64.CLOSE(13)
if status and status != 64 { ; 64=end of file
txt.print("\ni/o error, status: ")
@@ -57,9 +59,208 @@ io_error:
}
sub status(ubyte drivenumber) {
; -- display the disk drive's current status message
c64.SETNAM(0, $0000)
; internal variables for the iterative file lister / loader
ubyte list_suffixmatch
ubyte list_pattern_size
ubyte list_skip_disk_name
uword list_pattern
uword list_blocks
ubyte iteration_in_progress = false
str list_filename = "?" * 32
; ----- get a list of files (uses iteration functions internally -----
sub list_files(ubyte drivenumber, uword pattern, ubyte suffixmatch, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
ubyte[256] names_buffer
ubyte[256] names_buffer1 ; to store a bit more names
uword buf_ptr = &names_buffer
ubyte files_found = 0
if lf_start_list(drivenumber, pattern, suffixmatch) {
while lf_next_entry() {
@(name_ptrs) = lsb(buf_ptr)
name_ptrs++
@(name_ptrs) = msb(buf_ptr)
name_ptrs++
buf_ptr += strcopy(diskio.list_filename, buf_ptr) + 1
files_found++
if buf_ptr - &names_buffer > (len(names_buffer) + len(names_buffer1) - 18)
break
if files_found == max_names
break
}
lf_end_list()
}
return files_found
}
; ----- iterative file lister functions -----
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
; -- start an iterative file listing with optional prefix or suffix name matching.
; note: only a single iteration loop can be active at a time!
lf_end_list()
list_pattern = pattern
list_suffixmatch = suffixmatch
list_skip_disk_name = true
iteration_in_progress = true
if pattern==0
list_pattern_size = 0
else
list_pattern_size = strlen(pattern)
c64.SETNAM(1, "$")
c64.SETLFS(12, drivenumber, 0)
void c64.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(12) ; use #12 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 iteration_in_progress
return false
repeat {
void c64.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename
ubyte blocks_lsb = c64.CHRIN()
ubyte blocks_msb = c64.CHRIN()
if c64.READST()
goto close_end
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 iteration_in_progress {
c64.CLRCHN()
c64.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file loader functions -----
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time!
f_close()
c64.SETNAM(strlen(filenameptr), filenameptr)
c64.SETLFS(11, drivenumber, 0)
void c64.OPEN() ; open 11,8,0,"filename"
if_cc {
iteration_in_progress = true
void c64.CHKIN(11) ; use #11 as input channel
if_cc
return true
}
f_close()
return false
}
sub f_read(uword bufferpointer, uword buffersize) -> uword {
if not iteration_in_progress
return 0
uword actual = 0
void c64.CHKIN(11) ; use #11 as input channel again
repeat buffersize {
ubyte data = c64.CHRIN()
@(bufferpointer) = data
bufferpointer++
actual++
ubyte status = c64.READST()
if status==64
f_close() ; end of file, close it
if status
return actual
}
return actual
}
sub f_close() {
; -- end an iterative file loading session (close channels).
if iteration_in_progress {
c64.CLRCHN()
c64.CLOSE(11)
iteration_in_progress = false
}
}
sub status(ubyte drivenumber) -> uword {
; -- retrieve the disk drive's current status message
uword messageptr = &filename
c64.SETNAM(0, filename)
c64.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15
if_cs
@@ -68,16 +269,20 @@ io_error:
if_cs
goto io_error
while not c64.READST()
txt.chrout(c64.CHRIN())
while not c64.READST() {
@(messageptr) = c64.CHRIN()
messageptr++
}
io_error:
@(messageptr) = 0
c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(15)
return filename
}
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 +302,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 +331,9 @@ io_error:
+ ldx P8ZP_SCRATCH_REG
}}
c64.CLRCHN()
c64.CLOSE(1)
if end_of_load
return end_of_load - address_override
+93
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 -----------
@@ -1301,6 +1352,33 @@ shift_left_w_3 .proc
jmp shift_left_w_7._shift3
.pend
shift_left_w .proc
; -- variable number of shifts left
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
dey
bne _shift
rts
.pend
shift_right_uw .proc
; -- uword variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lsr P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
shift_right_uw_7 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
@@ -1431,6 +1509,21 @@ shift_right_w_3 .proc
.pend
shift_right_w .proc
; -- signed word variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lda P8ESTACK_HI+1,x
asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
; support for bit shifting that is too large to be unrolled:
+21
View File
@@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
dex
rts
.pend
func_strcopy .proc
lda _arg_to
ldy _arg_to+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_from
ldy _arg_from+1
jsr strcpy
tya
rts
_arg_from .word 0
_arg_to .word 0
.pend
func_strcopy_to_stack .proc
jsr func_strcopy
sta P8ESTACK_LO,x
dex
rts
.pend
+1
View File
@@ -993,6 +993,7 @@ _arg_index .byte 0
strcpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; it is assumed the target string is large enough.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #$ff
+1 -1
View File
@@ -1 +1 @@
5.2
5.4
+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 -> {}
}
}
@@ -4,6 +4,7 @@ import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
@@ -91,6 +92,55 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
}
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// when using a simple bit shift and assigning it to a variable of a different type,
// try to make the bit shifting 'wide enough' to fall into the variable's type.
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
if(expr.operator=="<<" || expr.operator==">>") {
val leftDt = expr.left.inferType(program)
when (parent) {
is Assignment -> {
val targetDt = parent.target.inferType(program)
if(leftDt != targetDt) {
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is VarDecl -> {
if(!leftDt.istype(parent.datatype)) {
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is IFunctionCall -> {
val argnum = parent.args.indexOf(expr)
when (val callee = parent.target.targetStatement(program.namespace)) {
is Subroutine -> {
val paramType = callee.parameters[argnum].type
if(leftDt isAssignableTo paramType) {
val cast = TypecastExpression(expr.left, paramType, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(callee.name)
val paramTypes = func.parameters[argnum].possibleDatatypes
for(type in paramTypes) {
if(leftDt isAssignableTo type) {
val cast = TypecastExpression(expr.left, type, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
}
else -> throw FatalAstException("weird callee")
}
}
else -> return noModifications
}
}
return noModifications
}
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val subroutine = expr.definingSubroutine()!!
@@ -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)
@@ -160,6 +160,7 @@ internal class AsmGen(private val program: Program,
val floatvalue = flt.key
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
out("prog8_program_end\t; end of program label for progend()")
}
private fun block2asm(block: Block) {
@@ -814,12 +815,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 +1072,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 +1229,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 +1244,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>()
@@ -5,10 +5,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target
@@ -84,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"set_irqd" -> asmgen.out(" sei")
"strlen" -> funcStrlen(fcall, resultToStack)
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
"strcopy" -> {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_strcopy_to_stack")
else
asmgen.out(" jsr prog8_lib.func_strcopy")
}
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
"substr", "leftstr", "rightstr" -> {
translateArguments(fcall.args, func, scope)
@@ -93,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
translateArguments(fcall.args, func, scope)
asmgen.out(" jmp prog8_lib.func_exit")
}
"progend" -> {
if(resultToStack)
asmgen.out("""
lda #<prog8_program_end
sta P8ESTACK_LO,x
lda #>prog8_program_end
sta P8ESTACK_HI,x
dex""")
else
asmgen.out(" lda #<prog8_program_end | ldy #>prog8_program_end")
}
else -> TODO("missing asmgen for builtin func ${func.name}")
}
}
@@ -532,14 +547,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
val type = fcall.args[0].inferType(program)
when {
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
if (fcall.args[0] is IdentifierReference) {
// use the address of the variable
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
val type = fcall.args[0].inferType(program)
when {
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
}
}
if(resultToStack)
else {
// use the expression value as address of the string
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
}
if (resultToStack)
asmgen.out(" jsr prog8_lib.func_strlen_stack")
else
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
@@ -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) {
@@ -1947,24 +1947,29 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
private fun translateBinaryOperatorWords(operator: String, types: DataType) {
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) {
"**" -> throw AssemblyError("** operator requires floats")
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> {
if(types==DataType.WORD)
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_uw")
}
"+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> throw AssemblyError("<< should not operate via stack")
">>" -> throw AssemblyError(">> should not operate via stack")
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> {
if(dt==DataType.UWORD)
asmgen.out(" jsr math.shift_right_uw")
else
asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
@@ -16,11 +16,11 @@ 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)
asmgen.out(" plp\t; restore status flags from call")
asmgen.out(" plp") // restore status flags from call
}
@@ -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()!!)
@@ -69,7 +69,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName")
if(preserveStatusRegisterAfterCall) {
asmgen.out(" php\t; save status flags from call")
asmgen.out(" php") // save status flags from call
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
// must take care of popping this value again at the end!
}
@@ -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) {
@@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
if (preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
asmgen.out(" plp") // restore status flags from call
}
is BuiltinFunctionStatementPlaceholder -> {
val signature = BuiltinFunctions.getValue(sub.name)
@@ -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)
@@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex
@@ -317,9 +318,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
if(ptrOnZp)
@@ -357,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&" -> asmgen.out(" and $otherName")
"^" -> asmgen.out(" eor $otherName")
"|" -> asmgen.out(" ora $otherName")
"&", "and" -> asmgen.out(" and $otherName")
"|", "or" -> asmgen.out(" ora $otherName")
"^", "xor" -> asmgen.out(" eor $otherName")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
if(ptrOnZp)
@@ -438,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
}
"&" -> {
"&", "and" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value")
if(ptrOnZp)
@@ -446,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"^" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"|" -> {
"|", "or" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value")
if(ptrOnZp)
@@ -462,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"^", "xor" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -525,18 +529,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> {
"&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -591,27 +596,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"and" -> asmgen.out("""
lda $name
and $otherName
beq +
lda #1
+ sta $name""")
"or" -> asmgen.out("""
lda $name
ora $otherName
beq +
lda #1
+ sta $name""")
"xor" -> asmgen.out("""
lda $name
eor $otherName
beq +
lda #1
+ sta $name""")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -643,14 +631,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,14 +663,15 @@ 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") }
}
}
}
}
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -858,9 +857,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 +884,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
@@ -917,47 +938,48 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
"&" -> {
"&", "and" -> {
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")
}
}
"^" -> {
"|", "or" -> {
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 | 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")
}
}
"|" -> {
"^", "xor" -> {
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 | 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")
}
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1019,11 +1041,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
@@ -1067,13 +1090,18 @@ 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")
"&", "and" -> {
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")
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1081,8 +1109,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
@@ -1144,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""")
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1319,20 +1348,25 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&" -> {
"&", "and" -> {
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)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1361,18 +1395,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
remainderVarByWordInAY()
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> {
"&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
"|" -> {
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1420,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
asmgen.out("""
@@ -1439,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
if(CompilationTarget.instance is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT()
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
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("""
@@ -1488,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
// store Fac1 back into memory
@@ -1504,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
if(CompilationTarget.instance is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT()
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
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)
@@ -1560,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
// store Fac1 back into memory
@@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
override fun launchEmulator(programName: String) {
for(emulator in listOf("x16emu")) {
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
"-run", "-prg", programName + ".prg")
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
@@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("set_irqd" , false, emptyList(), null),
FSignature("clear_irqd" , false, emptyList(), null),
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
FSignature("progend" , true, emptyList(), DataType.UWORD),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("memcopy" , false, listOf(
FParam("from", IterableDatatypes + DataType.UWORD),
@@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
FParam("numwords", setOf(DataType.UWORD)),
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
FSignature("strcopy" , false, listOf(FParam("from", IterableDatatypes + DataType.UWORD), FParam("to", IterableDatatypes + DataType.UWORD)), DataType.UBYTE),
FSignature("substr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD),
@@ -353,7 +355,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
val argument=args[0]
if(argument is StringLiteralValue)
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
val vardecl = (argument as? IdentifierReference)?.targetVarDecl(program.namespace)
if(vardecl!=null) {
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
throw SyntaxError("strlen must have string argument", position)
@@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.*
import kotlin.math.pow
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@@ -97,6 +98,43 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
val modifications = mutableListOf<IAstModification>()
if(expr.operator == "**" && leftconst!=null) {
// optimize various simple cases of ** :
// optimize away 1 ** x into just 1 and 0 ** x into just 0
// optimize 2 ** x into (1<<x) if both operands are integer.
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
when (leftconst.number.toDouble()) {
0.0 -> {
val value = NumericLiteralValue(leftDt, 0, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
1.0 -> {
val value = NumericLiteralValue(leftDt, 1, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
2.0 -> {
if(rightconst!=null) {
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
} else {
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val targetDt =
when (parent) {
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
is VarDecl -> parent.datatype
else -> leftDt
}
val one = NumericLiteralValue(targetDt, 1, expr.position)
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
modifications += IAstModification.ReplaceNode(expr, shift, parent)
}
}
}
}
}
val subExpr: BinaryExpression? = when {
leftconst!=null -> expr.right as? BinaryExpression
@@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
val change = groupTwoConstsTogether(expr, subExpr,
leftconst != null, rightconst != null,
subleftconst != null, subrightconst != null)
return change?.let { listOf(it) } ?: noModifications
if(change!=null)
modifications += change
}
}
@@ -119,10 +158,10 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(leftconst != null && rightconst != null) {
val evaluator = ConstExprEvaluator()
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
return listOf(IAstModification.ReplaceNode(expr, result, parent))
modifications += IAstModification.ReplaceNode(expr, result, parent)
}
return noModifications
return modifications
}
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
@@ -6,6 +6,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment
import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
@@ -98,6 +99,12 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target))
return listOf(IAstModification.SwapOperands(expr))
}
// X + (-A) --> X - A
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
return listOf(IAstModification.ReplaceNode(
@@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null)
return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal2
@@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null)
return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val leftValue: Expression = expr2.left
@@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
return null
}
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
if (expr.operator in associativeOperators && leftVal != null) {
// swap left and right so that right is always the constant
val tmp = expr.left
expr.left = expr.right
expr.right = tmp
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
return BinExprWithConstants(expr, expr.right.constValue(program), leftVal)
}
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
}
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
}
@@ -323,7 +323,7 @@ internal class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator
val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) {
// associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr))
}
+1 -1
View File
@@ -102,7 +102,7 @@ internal class ModuleImporter {
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
val fileName = "$name.p8"
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
val propPath = System.getProperty("prog8.libdir")
if(propPath!=null)
+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.
+25 -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
@@ -811,6 +814,12 @@ substr(source, target, start, length)
Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression).
strcopy(from, to)
Copy a string to another, overwriting that one. Returns the length of the string that was copied.
Often you don't have to call this explicitly and can just write ``string1 = string2``
but this function is useful if you're dealing with addresses for instance.
Miscellaneous
^^^^^^^^^^^^^
exit(returncode)
@@ -827,6 +836,12 @@ mkword(msb, lsb)
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
So mkword($80, $22) results in $8022.
.. note::
The arguments to the mkword() function are in 'natural' order that is first the msb then the lsb.
Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb)
rnd()
returns a pseudo-random byte from 0..255
@@ -891,6 +906,10 @@ set_irqd() / clear_irqd()
swap(x, y)
Swap the values of numerical variables (or memory locations) x and y in a fast way.
progend()
Returns the last address of the program in memory + 1.
Can be used to load dynamic data after the program, instead of hardcoding something.
Library routines
----------------
+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?
+4
View File
@@ -324,6 +324,10 @@ main {
txt.print(result)
txt.chrout('\n')
void strcopy(s2, s1)
txt.print_ub(99+strcopy(s2,s1))
txt.chrout('\n')
test_stack.test()
}
+9 -3
View File
@@ -4,6 +4,12 @@
%import test_stack
%zeropage basicsafe
; note: The flickering in the scrolling is caused by the CPU requiring
; too long to scroll the characters + the colors in course scroll.
; This takes nearly a full frame to accomplish, and causes tearing.
; It's very difficult to remove this flicker: it requires double buffering
; and splitting the coarse character scrolling on multiple phases...
main {
ubyte perform_scroll = false
@@ -17,7 +23,7 @@ main {
c64.SCROLX &= %11110111 ; 38 column mode
c64.set_rasterirq(1) ; enable animation
c64.set_rasterirq(200) ; enable animation
ubyte target_height = 10
ubyte active_height = 24
@@ -123,10 +129,10 @@ spritedata $0f00 {
irq {
ubyte smoothx=7
ubyte smoothx=0
sub irq() {
smoothx = (smoothx-1) & 7
main.perform_scroll = smoothx==0
main.perform_scroll = smoothx==7
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
}
}
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.
+125
View File
@@ -0,0 +1,125 @@
%target cx16
%import graphics
%import diskio
bmp_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
ubyte[$36] header
uword size
uword width
uword height
ubyte bpp
uword offsetx
uword offsety
ubyte[256] palette0
ubyte[256] palette1
ubyte[256] palette2
ubyte[256] palette3
uword total_read = 0
palette0[0] = 0
palette1[0] = 0
palette2[0] = 0
palette3[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(header, $36)
if size==$36 {
total_read = $36
if header[0]=='b' and header[1]=='m' {
uword bm_data_offset = mkword(header[11], header[10])
uword header_size = mkword(header[$f], header[$e])
width = mkword(header[$13], header[$12])
height = mkword(header[$17], header[$16])
bpp = header[$1c]
uword num_colors = header[$2e]
if num_colors == 0
num_colors = 2**bpp
uword skip_hdr = header_size - 40
repeat skip_hdr
void c64.CHRIN()
total_read += skip_hdr
size = diskio.f_read(&palette0, num_colors*4)
if size==num_colors*4 {
total_read += size
repeat bm_data_offset - total_read
void c64.CHRIN()
graphics.clear_screen(1, 0)
palette.set_bgra(&palette0, num_colors)
decode_bitmap()
load_ok = true
}
}
}
diskio.f_close()
}
return load_ok
sub start_plot() {
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_bitmap() {
start_plot()
uword bits_width = width * bpp
ubyte pad_bytes = (((bits_width + 31) >> 5) << 2) - ((bits_width + 7) >> 3) as ubyte
uword x
uword y
ubyte b
for y in height-1 downto 0 {
set_cursor(0, y)
when bpp {
8 -> {
for x in 0 to width-1
cx16.FB_set_pixel(c64.CHRIN())
}
4 -> {
for x in 0 to width-1 step 2 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b&15)
}
}
2 -> {
for x in 0 to width-1 step 4 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>6)
cx16.FB_set_pixel(b>>4 & 3)
cx16.FB_set_pixel(b>>2 & 3)
cx16.FB_set_pixel(b & 3)
}
}
1 -> {
for x in 0 to width-1 step 8 {
cx16.r0 = c64.CHRIN()
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
}
}
repeat pad_bytes
void c64.CHRIN()
}
}
}
}
+140
View File
@@ -0,0 +1,140 @@
%target cx16
%import graphics
%import textio
%import diskio
%option no_sysinit
; CommanderX16 Image file format. (EXPERIMENTAL/ UNFINISHED)
; Numbers are encoded in little endian format (lsb first).
;
; offset value
; -----------------
; HEADER (12 bytes):
; 0-1 'CI' in petscii , from "CommanderX16 Image".
; 2 Size of the header data following this byte (always 9, could become more if format changes)
; 3-4 Width in pixels (must be multiple of 8)
; 5-6 Height in pixels
; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors)
; this also determines the number of palette entries following later.
; 8 Settings bits.
; bit 0 and 1 = compression. 00 = uncompressed
; 01 = RLE [TODO not yet implemented]
; 10 = LZSA [TODO not yet implemented]
; 11 = Exomizer [TODO not yet implemented]
; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB)
; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB)
; 4 bits per channel is what the Vera in the Cx16 supports.
; bit 3 = bitmap format. 0 = raw bitmap pixels
; 1 = tile-based image [TODO not yet implemented]
; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels
; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels
; bit 6,7: reserved, set to 0
; 9-11 Size of the bitmap data following the palette data.
; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end).
;
; PALETTE (always present but size varies):
; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per
; entry is 2 or 3, depending on the chosen palette format in the setting bits.
;
; BITMAPDATA (size varies):
; After this, the actual image data follows.
; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence
; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8
; If it is 'tiles', .... [TODO]
; If a compression scheme is used, the bitmap data here has to be decompressed first.
; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed?
; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that)
; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen)
ci_module {
%option force_output
ubyte[256] buffer
ubyte[256] buffer2 ; add two more buffers to make enough space
ubyte[256] buffer3 ; to store a 256 color palette
ubyte[256] buffer4 ; .. and some more to be able to store 1280=
ubyte[256] buffer5 ; two 640 bytes worth of bitmap scanline data
sub show_image(uword filename) -> ubyte {
ubyte read_success = false
uword bitmap_load_address = progend()
; uword max_bitmap_size = $9eff - bitmap_load_address
if(diskio.f_open(8, filename)) {
uword size = diskio.f_read(buffer, 12) ; read the header
if size==12 {
if buffer[0]=='c' and buffer[1]=='i' and buffer[2] == 9 {
uword width = mkword(buffer[4], buffer[3])
uword height = mkword(buffer[6], buffer[5])
ubyte bpp = buffer[7]
uword num_colors = 2 ** bpp
ubyte flags = buffer[8]
ubyte compression = flags & %00000011
ubyte palette_format = (flags & %00000100) >> 2
ubyte bitmap_format = (flags & %00001000) >> 3
; ubyte hscale = (flags & %00010000) >> 4
; ubyte vscale = (flags & %00100000) >> 5
uword bitmap_size = mkword(buffer[10], buffer[9])
uword palette_size = num_colors*2
if palette_format
palette_size += num_colors ; 3
if width > graphics.WIDTH {
txt.print("image is too wide for the display!\n")
} else if compression!=0 {
txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions
} else if bitmap_format==1 {
txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image
} else {
size = diskio.f_read(buffer, palette_size)
if size==palette_size {
if compression {
txt.print("todo: compressed image support\n")
} else {
; uncompressed bitmap data. read it a scanline at a time and display as we go.
; restrict height to the maximun that can be displayed
if height > graphics.HEIGHT
height = graphics.HEIGHT
graphics.enable_bitmap_mode()
if palette_format
palette.set_rgb8(buffer, num_colors)
else
palette.set_rgb4(buffer, num_colors)
graphics.clear_screen(1,0)
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
uword scanline_size = width * bpp / 8
ubyte y
for y in 0 to lsb(height)-1 {
void diskio.f_read(buffer, scanline_size)
when bpp {
8 -> cx16.FB_set_pixels_from_buf(buffer, scanline_size) ; FB_set_pixels in rom v38 crashes with a size > 255 so we use our own replacement for now
4 -> display_scanline_16c(buffer, scanline_size)
2 -> display_scanline_4c(buffer, scanline_size)
1 -> display_scanline_2c(buffer, scanline_size)
}
}
read_success = true
}
}
}
}
}
diskio.f_close()
}
return read_success
}
sub display_scanline_16c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_4c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_2c(uword dataptr, uword numbytes) {
; TODO
}
}
+214
View File
@@ -0,0 +1,214 @@
%target cx16
%import graphics
%import textio
%import diskio
iff_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
uword size
ubyte[32] buffer
ubyte[256] cmap
ubyte[256] cmap1
ubyte[256] cmap2
uword camg = 0
str chunk_id = "????"
uword chunk_size_hi
uword chunk_size_lo
uword scanline_data_ptr = progend()
uword width
uword height
ubyte num_planes
uword num_colors
ubyte compression
ubyte have_cmap = false
cmap[0] = 0
cmap1[0] = 0
cmap2[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(buffer, 12)
if size==12 {
if buffer[0]=='f' and buffer[1]=='o' and buffer[2]=='r' and buffer[3]=='m'
and buffer[8]=='i' and buffer[9]=='l' and buffer[10]=='b' and buffer[11]=='m'{
while read_chunk_header() {
if chunk_id == "bmhd" {
void diskio.f_read(buffer, chunk_size_lo)
width = mkword(buffer[0], buffer[1])
height = mkword(buffer[2], buffer[3])
num_planes = buffer[8]
num_colors = 2 ** num_planes
compression = buffer[10]
}
else if chunk_id == "camg" {
void diskio.f_read(buffer, chunk_size_lo)
camg = mkword(buffer[2], buffer[3])
if camg & $0800 {
txt.print("ham mode not supported!\n")
break
}
}
else if chunk_id == "cmap" {
have_cmap = true
void diskio.f_read(&cmap, chunk_size_lo)
}
else if chunk_id == "body" {
graphics.clear_screen(1, 0)
if camg & $0004
height /= 2 ; interlaced: just skip every odd scanline later
if camg & $0080 and have_cmap
make_ehb_palette()
palette.set_rgb8(&cmap, num_colors)
if compression
decode_rle()
else
decode_raw()
load_ok = true
break ; done after body
}
else {
skip_chunk()
}
}
} else
txt.print("not an iff ilbm file!\n")
}
diskio.f_close()
}
return load_ok
sub read_chunk_header() -> ubyte {
size = diskio.f_read(buffer, 8)
if size==8 {
chunk_id[0] = buffer[0]
chunk_id[1] = buffer[1]
chunk_id[2] = buffer[2]
chunk_id[3] = buffer[3]
chunk_size_hi = mkword(buffer[4], buffer[5])
chunk_size_lo = mkword(buffer[6], buffer[7])
return true
}
return false
}
sub skip_chunk() {
repeat lsb(chunk_size_hi)*8 + (chunk_size_lo >> 13)
void diskio.f_read(scanline_data_ptr, $2000)
void diskio.f_read(scanline_data_ptr, chunk_size_lo & $1fff)
}
sub make_ehb_palette() {
; generate 32 additional Extra-Halfbrite colors in the cmap
uword palletteptr = &cmap
uword ehbptr = palletteptr + 32*3
repeat 32 {
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
}
}
uword bitplane_stride
uword interleave_stride
uword offsetx
uword offsety
sub start_plot() {
bitplane_stride = width>>3
interleave_stride = bitplane_stride * num_planes
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_raw() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
void diskio.f_read(scanline_data_ptr, interleave_stride)
if interlaced
void diskio.f_read(scanline_data_ptr, interleave_stride)
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
decode_rle_scanline()
if interlaced
decode_rle_scanline()
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle_scanline() {
uword x = interleave_stride
uword plane_ptr = scanline_data_ptr
while x {
ubyte b = c64.CHRIN()
if b > 128 {
ubyte b2 = c64.CHRIN()
repeat 2+(b^255) {
@(plane_ptr) = b2
plane_ptr++
x--
}
} else if b < 128 {
repeat b+1 {
@(plane_ptr) = c64.CHRIN()
plane_ptr++
x--
}
} else
break
}
}
sub planar_to_chunky_scanline() {
uword x
for x in 0 to width-1 {
ubyte bitnr = ((lsb(x) ^ 255) & 7) + 1
uword pixptr = x/8 + scanline_data_ptr
ubyte bits=0
repeat num_planes {
ubyte bb = @(pixptr) >> bitnr ; extract bit
ror(bits) ; shift it into the byte
pixptr += bitplane_stride
}
bits >>= 8-num_planes
cx16.FB_set_pixel(bits)
}
}
}
}
+197
View File
@@ -0,0 +1,197 @@
%target cx16
%import graphics
%import textio
%import diskio
%import koala_module
%import iff_module
%import pcx_module
%import bmp_module
;; %import ci_module
main {
sub start() {
; trick to check if we're running on sdcard or host system shared folder
txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n")
if strlen(diskio.status(8)) {
txt.print("enter image file name or just enter for all on disk: ")
ubyte i = txt.input_chars(diskio.filename)
graphics.enable_bitmap_mode()
if i
attempt_load(diskio.filename)
else
show_pics_sdcard()
txt.print("\nnothing more to do.\n")
}
else
txt.print("files are read with sequential file loading.\nin the emulator this currently only works with files on an sd-card image.\nsorry :(\n")
repeat {
;
}
}
sub show_pics_sdcard() {
; load and show the images on the disk with the given extensions.
; this only works in the emulator V38 with an sd-card image with the files on it.
str[40] filename_ptrs
ubyte num_files = diskio.list_files(8, 0, false, &filename_ptrs, len(filename_ptrs))
if num_files {
while num_files {
num_files--
attempt_load(filename_ptrs[num_files])
}
} else
txt.print("no files in directory!\n")
}
sub attempt_load(uword filenameptr) {
txt.print(">> ")
txt.print(filenameptr)
txt.chrout('\n')
uword extension = filenameptr + rfind(filenameptr, '.')
if strcmp(extension, ".iff")==0 {
txt.print("loading ")
txt.print("iff\n")
if iff_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".pcx")==0 {
txt.print("loading ")
txt.print("pcx\n")
if pcx_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".koa")==0 {
txt.print("loading ")
txt.print("koala\n")
if koala_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".bmp")==0 {
txt.print("loading ")
txt.print("bmp\n")
if bmp_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
; else if strcmp(extension, ".ci")==0 {
; txt.print("loading ")
; txt.print("ci\n")
; if ci_module.show_image(filenameptr)
; txt.clear_screen()
; else
; txt.print("load error!\n")
; cx16.wait(120)
; }
}
sub extension_equals(uword stringptr, uword extensionptr) -> ubyte {
ubyte ix = rfind(stringptr, '.')
return ix<255 and strcmp(stringptr+ix, extensionptr)==0
}
sub rfind(uword stringptr, ubyte char) -> ubyte {
ubyte i
for i in strlen(stringptr)-1 downto 0 {
if @(stringptr+i)==char
return i
}
return 0
}
}
palette {
sub set_rgb4(uword palletteptr, uword num_colors) {
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
uword vera_palette_ptr = $fa00
repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr))
palletteptr++
cx16.vpoke(1, vera_palette_ptr, @(palletteptr))
palletteptr++
vera_palette_ptr+=2
}
}
sub set_rgb8(uword palletteptr, uword num_colors) {
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
repeat num_colors {
red = @(palletteptr) >> 4
palletteptr++
greenblue = @(palletteptr) & %11110000
palletteptr++
greenblue |= @(palletteptr) >> 4 ; add Blue
palletteptr++
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_bgra(uword palletteptr, uword num_colors) {
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
; 4 bytes per color entry (BGRA), adjust color depth from 8 to 4 bits per channel.
repeat num_colors {
red = @(palletteptr+2) >> 4
greenblue = @(palletteptr+1) & %11110000
greenblue |= @(palletteptr+0) >> 4 ; add Blue
palletteptr+=4
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_monochrome() {
uword vera_palette_ptr = $fa00
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
repeat 255 {
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
}
}
sub set_grayscale() {
ubyte c = 0
uword vera_palette_ptr = $fa00
repeat 16 {
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
}
c += $11
}
}
}
+86
View File
@@ -0,0 +1,86 @@
%target cx16
%import graphics
%import diskio
%import c64colors
koala_module {
const uword load_location = $6000
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok=false
if diskio.f_open(8, filenameptr) {
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
if size==2 {
size = diskio.f_read(load_location, 10001)
if size == 10001 {
; set a better C64 color palette, the Cx16's default is too saturated
c64colors.set_palette_pepto()
convert_koalapic()
load_ok = true
}
}
diskio.f_close()
}
return load_ok
}
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
graphics.clear_screen(1, 0)
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
}
}
}
}
+180
View File
@@ -0,0 +1,180 @@
%target cx16
%import graphics
%import textio
%import diskio
pcx_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
if diskio.f_open(8, filenameptr) {
ubyte[128] header
uword size = diskio.f_read(header, 128)
if size==128 {
if header[0] == $0a and header[2] == 1 {
ubyte bits_per_pixel = header[3]
if bits_per_pixel==1 or bits_per_pixel==4 or bits_per_pixel==8 {
uword width = mkword(header[$09], header[$08]) - mkword(header[$05], header[$04]) + 1
uword height = mkword(header[$0b], header[$0a]) - mkword(header[$07], header[$06]) + 1
ubyte number_of_planes = header[$41]
uword palette_format = mkword(header[$45], header[$44])
uword num_colors = 2**bits_per_pixel
if number_of_planes == 1 {
if (width & 7) == 0 {
graphics.clear_screen(1, 0)
if palette_format==2
palette.set_grayscale()
else if num_colors == 16
palette.set_rgb8(&header + $10, 16)
else if num_colors == 2
palette.set_monochrome()
when bits_per_pixel {
8 -> load_ok = bitmap.do8bpp(width, height)
4 -> load_ok = bitmap.do4bpp(width, height)
1 -> load_ok = bitmap.do1bpp(width, height)
}
if load_ok {
load_ok = c64.CHRIN()
if load_ok == 12 {
; there is 256 colors of palette data at the end
uword palette_mem = progend()
load_ok = false
size = diskio.f_read(palette_mem, 3*256)
if size==3*256 {
load_ok = true
palette.set_rgb8(palette_mem, num_colors)
}
}
}
} else
txt.print("width not multiple of 8!\n")
} else
txt.print("has >256 colors!\n")
}
}
}
diskio.f_close()
}
return load_ok
}
}
bitmap {
uword offsetx
uword offsety
uword py
uword px
ubyte y_ok
ubyte status
sub start_plot(uword width, uword height) {
offsetx = 0
offsety = 0
y_ok = true
py = 0
px = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
status = (not c64.READST()) or c64.READST()==64
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub next_scanline() {
px = 0
py++
y_ok = py < graphics.HEIGHT-1
set_cursor(0, py)
status = (not c64.READST()) or c64.READST()==64
}
sub do1bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
repeat b {
if y_ok {
cx16.r0 = dat
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
} else {
if y_ok {
cx16.r0 = b
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
if px==width
next_scanline()
}
return status
}
sub do4bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b {
cx16.FB_set_pixel(dat>>4)
cx16.FB_set_pixel(dat & 15)
}
px += b*2
} else {
if y_ok {
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b & 15)
}
px += 2
}
if px==width
next_scanline()
}
return status
}
sub do8bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b
cx16.FB_set_pixel(dat)
px += b
} else {
if y_ok
cx16.FB_set_pixel(b)
px++
}
if px==width
next_scanline()
}
return status
}
}
-40
View File
@@ -1,40 +0,0 @@
; CommanderX16 simple graphics example!
%target cx16
%zeropage basicsafe
main {
sub start() {
void cx16.screen_set_mode($80)
cx16.r0=0
void cx16.screen_set_mode(0)
cx16.FB_init()
cx16.GRAPH_init()
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
ubyte color
repeat 320*199 {
cx16.FB_set_pixel(color)
color++
}
uword xx
for xx in 0 to 319 step 32 {
ubyte q
for q in 0 to 31 {
cx16.GRAPH_set_colors(q, 2, 0)
cx16.r0 = xx+q
cx16.r1=0
cx16.r2=rnd()
cx16.r3=199
cx16.GRAPH_draw_line()
}
}
}
}
+56
View File
@@ -0,0 +1,56 @@
%import textio
%import diskio
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start() {
txt.print("showing the full directory of drive 8:\n")
void diskio.directory(8)
txt.chrout('\n')
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
+5 -2
View File
@@ -1,11 +1,14 @@
%import textio
%import diskio
%import floats
%import graphics
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start () {
sub start() {
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;progend;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>
+11
View File
@@ -0,0 +1,11 @@
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/
(note the version number in the path, adjust accordingly)
+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