mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
52e8a44517 | |||
59f33658ad | |||
e0315bffdc | |||
cd28d0c0e0 | |||
0baa2c8b23 | |||
4977d1fbd5 | |||
3b7a92f1b4 | |||
f6920172dd | |||
93bfc8f5f4 | |||
39b7655264 | |||
8b75ceb412 | |||
c39fc4010d | |||
8df778a515 | |||
5134ea76bf | |||
3ba37df29d | |||
e221d674d9 | |||
251f947293 | |||
41e1e1cbb0 | |||
da1bc351d2 | |||
43c0afdea0 | |||
add5bfa2ec | |||
34babfb5de | |||
4f6c45c86c | |||
e6220a464c | |||
8dcd49934a | |||
bedc3bdb56 | |||
83ceb0fde9 | |||
1d299c56e0 | |||
0d735c2ccc | |||
4094f89d4a | |||
cf1e8b194a | |||
74e5644f55 | |||
b5dc5fc615 | |||
7a7270d769 | |||
7549ddcd2b | |||
08f0303178 |
@ -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() {
|
asmsub disable_runstop_and_charsetswitch() {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #$80
|
lda #$80
|
||||||
|
@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
|
|||||||
|
|
||||||
|
|
||||||
sub clear_screen() {
|
sub clear_screen() {
|
||||||
clear_screenchars(' ')
|
txt.chrout(147)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
|
@ -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 $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||||
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||||
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** 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 $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
||||||
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** 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
|
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
||||||
|
@ -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 $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 $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 $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 $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 $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
|
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
|
||||||
@ -245,7 +245,7 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
|||||||
; ---- end of kernal routines ----
|
; ---- end of kernal routines ----
|
||||||
|
|
||||||
; ---- utilities -----
|
; ---- utilities -----
|
||||||
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||||
; -- get a byte from VERA's video memory
|
; -- get a byte from VERA's video memory
|
||||||
; note: inefficient when reading multiple sequential bytes!
|
; note: inefficient when reading multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -257,9 +257,9 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
|||||||
lda cx16.VERA_DATA0
|
lda cx16.VERA_DATA0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub vpoke(ubyte bank, uword address, ubyte value) {
|
sub vpoke(ubyte bank, uword address, ubyte value) {
|
||||||
; -- write a single byte to VERA's video memory
|
; -- write a single byte to VERA's video memory
|
||||||
; note: inefficient when writing multiple sequential bytes!
|
; note: inefficient when writing multiple sequential bytes!
|
||||||
%asm {{
|
%asm {{
|
||||||
@ -275,7 +275,59 @@ romsub $fecc = monitor() clobbers(A,X,Y)
|
|||||||
sta cx16.VERA_DATA0
|
sta cx16.VERA_DATA0
|
||||||
rts
|
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 -----
|
; ---- system stuff -----
|
||||||
|
@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
|
|||||||
|
|
||||||
|
|
||||||
sub clear_screen() {
|
sub clear_screen() {
|
||||||
clear_screenchars(' ')
|
txt.chrout(147)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||||
|
@ -7,14 +7,14 @@ diskio {
|
|||||||
|
|
||||||
|
|
||||||
sub directory(ubyte drivenumber) -> ubyte {
|
sub directory(ubyte drivenumber) -> ubyte {
|
||||||
; -- Shows the directory contents of disk drive 8-11 (provide as argument). Returns success flag.
|
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
c64.SETNAM(1, "$")
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
c64.SETLFS(13, drivenumber, 0)
|
||||||
void c64.OPEN() ; open 1,8,0,"$"
|
void c64.OPEN() ; open 13,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(1) ; use #1 as input channel
|
void c64.CHKIN(13) ; use #13 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
@ -39,14 +39,14 @@ diskio {
|
|||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
status = c64.READST()
|
status = c64.READST()
|
||||||
void c64.STOP()
|
void c64.STOP()
|
||||||
if_nz
|
if_z
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
status = c64.READST()
|
status = c64.READST()
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(1)
|
c64.CLOSE(13)
|
||||||
|
|
||||||
if status and status != 64 { ; 64=end of file
|
if status and status != 64 { ; 64=end of file
|
||||||
txt.print("\ni/o error, status: ")
|
txt.print("\ni/o error, status: ")
|
||||||
@ -59,33 +59,63 @@ io_error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
; internal variables for the iterative file lister
|
; internal variables for the iterative file lister / loader
|
||||||
ubyte list_suffixmatch
|
ubyte list_suffixmatch
|
||||||
ubyte list_in_progress = false
|
|
||||||
ubyte list_pattern_size
|
ubyte list_pattern_size
|
||||||
ubyte list_skip_disk_name
|
ubyte list_skip_disk_name
|
||||||
uword list_pattern
|
uword list_pattern
|
||||||
uword list_blocks
|
uword list_blocks
|
||||||
str list_filename = "????????????????"
|
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 {
|
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
|
||||||
; -- start an iterative file listing with optional prefix or suffix name matching.
|
; -- 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()
|
lf_end_list()
|
||||||
list_pattern = pattern
|
list_pattern = pattern
|
||||||
list_suffixmatch = suffixmatch
|
list_suffixmatch = suffixmatch
|
||||||
list_skip_disk_name = true
|
list_skip_disk_name = true
|
||||||
list_in_progress = true
|
iteration_in_progress = true
|
||||||
if pattern==0
|
if pattern==0
|
||||||
list_pattern_size = 0
|
list_pattern_size = 0
|
||||||
else
|
else
|
||||||
list_pattern_size = strlen(pattern)
|
list_pattern_size = strlen(pattern)
|
||||||
|
|
||||||
c64.SETNAM(1, "$")
|
c64.SETNAM(1, "$")
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
c64.SETLFS(12, drivenumber, 0)
|
||||||
void c64.OPEN() ; open 1,8,0,"$"
|
void c64.OPEN() ; open 12,8,0,"$"
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
void c64.CHKIN(1) ; use #1 as input channel
|
void c64.CHKIN(12) ; use #12 as input channel
|
||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
@ -106,13 +136,19 @@ io_error:
|
|||||||
; results will be found in list_blocks and list_filename.
|
; 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 it returns false though, there are no more entries (or an error occurred).
|
||||||
|
|
||||||
if not list_in_progress
|
if not iteration_in_progress
|
||||||
return false
|
return false
|
||||||
|
|
||||||
while not c64.READST() {
|
repeat {
|
||||||
|
void c64.CHKIN(12) ; use #12 as input channel again
|
||||||
|
|
||||||
uword nameptr = &list_filename
|
uword nameptr = &list_filename
|
||||||
ubyte blocks_lsb = c64.CHRIN()
|
ubyte blocks_lsb = c64.CHRIN()
|
||||||
ubyte blocks_msb = c64.CHRIN()
|
ubyte blocks_msb = c64.CHRIN()
|
||||||
|
|
||||||
|
if c64.READST()
|
||||||
|
goto close_end
|
||||||
|
|
||||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||||
|
|
||||||
; read until the filename starts after the first "
|
; read until the filename starts after the first "
|
||||||
@ -163,16 +199,67 @@ close_end:
|
|||||||
|
|
||||||
sub lf_end_list() {
|
sub lf_end_list() {
|
||||||
; -- end an iterative file listing session (close channels).
|
; -- end an iterative file listing session (close channels).
|
||||||
if list_in_progress {
|
if iteration_in_progress {
|
||||||
c64.CLRCHN()
|
c64.CLRCHN()
|
||||||
c64.CLOSE(1)
|
c64.CLOSE(12)
|
||||||
list_in_progress = false
|
iteration_in_progress = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub status(ubyte drivenumber) {
|
; ----- iterative file loader functions -----
|
||||||
; -- display the disk drive's current status message
|
|
||||||
|
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.SETNAM(0, filename)
|
||||||
c64.SETLFS(15, drivenumber, 15)
|
c64.SETLFS(15, drivenumber, 15)
|
||||||
void c64.OPEN() ; open 15,8,15
|
void c64.OPEN() ; open 15,8,15
|
||||||
@ -182,12 +269,16 @@ close_end:
|
|||||||
if_cs
|
if_cs
|
||||||
goto io_error
|
goto io_error
|
||||||
|
|
||||||
while not c64.READST()
|
while not c64.READST() {
|
||||||
txt.chrout(c64.CHRIN())
|
@(messageptr) = c64.CHRIN()
|
||||||
|
messageptr++
|
||||||
|
}
|
||||||
|
|
||||||
io_error:
|
io_error:
|
||||||
|
@(messageptr) = 0
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
c64.CLOSE(15)
|
c64.CLOSE(15)
|
||||||
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1352,6 +1352,33 @@ shift_left_w_3 .proc
|
|||||||
jmp shift_left_w_7._shift3
|
jmp shift_left_w_7._shift3
|
||||||
.pend
|
.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
|
shift_right_uw_7 .proc
|
||||||
lda P8ESTACK_LO+1,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta P8ZP_SCRATCH_B1
|
sta P8ZP_SCRATCH_B1
|
||||||
@ -1482,6 +1509,21 @@ shift_right_w_3 .proc
|
|||||||
.pend
|
.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:
|
; support for bit shifting that is too large to be unrolled:
|
||||||
|
|
||||||
|
@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
|
|||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.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
|
||||||
|
@ -993,6 +993,7 @@ _arg_index .byte 0
|
|||||||
strcpy .proc
|
strcpy .proc
|
||||||
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
||||||
; it is assumed the target string is large enough.
|
; it is assumed the target string is large enough.
|
||||||
|
; returns the length of the string that was copied in Y.
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy #$ff
|
ldy #$ff
|
||||||
|
@ -1 +1 @@
|
|||||||
5.3
|
5.4
|
||||||
|
@ -4,6 +4,7 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
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> {
|
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||||
val modifications = mutableListOf<IAstModification>()
|
val modifications = mutableListOf<IAstModification>()
|
||||||
val subroutine = expr.definingSubroutine()!!
|
val subroutine = expr.definingSubroutine()!!
|
||||||
|
@ -160,6 +160,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
|
out("prog8_program_end\t; end of program label for progend()")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun block2asm(block: Block) {
|
private fun block2asm(block: Block) {
|
||||||
|
@ -5,10 +5,7 @@ import prog8.ast.Node
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.ArrayIndex
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
@ -84,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"set_irqd" -> asmgen.out(" sei")
|
"set_irqd" -> asmgen.out(" sei")
|
||||||
"strlen" -> funcStrlen(fcall, resultToStack)
|
"strlen" -> funcStrlen(fcall, resultToStack)
|
||||||
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
|
"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)
|
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
|
||||||
"substr", "leftstr", "rightstr" -> {
|
"substr", "leftstr", "rightstr" -> {
|
||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall.args, func, scope)
|
||||||
@ -93,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
translateArguments(fcall.args, func, scope)
|
translateArguments(fcall.args, func, scope)
|
||||||
asmgen.out(" jmp prog8_lib.func_exit")
|
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}")
|
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) {
|
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
|
||||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
if (fcall.args[0] is IdentifierReference) {
|
||||||
val type = fcall.args[0].inferType(program)
|
// use the address of the variable
|
||||||
when {
|
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||||
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
|
val type = fcall.args[0].inferType(program)
|
||||||
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
|
when {
|
||||||
else -> throw AssemblyError("strlen requires str or uword arg")
|
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")
|
asmgen.out(" jsr prog8_lib.func_strlen_stack")
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
|
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
|
||||||
|
@ -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) {
|
when(operator) {
|
||||||
"**" -> throw AssemblyError("** operator requires floats")
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
"*" -> 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")
|
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.remainder_uw")
|
||||||
}
|
}
|
||||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||||
"<<" -> throw AssemblyError("<< should not operate via stack")
|
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||||
">>" -> throw AssemblyError(">> should not operate via stack")
|
">>" -> {
|
||||||
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
if(dt==DataType.UWORD)
|
||||||
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
asmgen.out(" jsr math.shift_right_uw")
|
||||||
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
else
|
||||||
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
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.equal_w")
|
||||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||||
|
@ -20,7 +20,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
||||||
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
|
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
|
||||||
if(preserveStatusRegisterAfterCall)
|
if(preserveStatusRegisterAfterCall)
|
||||||
asmgen.out(" plp\t; restore status flags from call")
|
asmgen.out(" plp") // restore status flags from call
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
if(preserveStatusRegisterAfterCall) {
|
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)
|
// 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!
|
// must take care of popping this value again at the end!
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (preserveStatusRegisterAfterCall)
|
if (preserveStatusRegisterAfterCall)
|
||||||
asmgen.out(" plp\t; restore status flags from call")
|
asmgen.out(" plp") // restore status flags from call
|
||||||
}
|
}
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
val signature = BuiltinFunctions.getValue(sub.name)
|
val signature = BuiltinFunctions.getValue(sub.name)
|
||||||
|
@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
|
|||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
@ -317,9 +318,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||||
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||||
"|" -> 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -357,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&" -> asmgen.out(" and $otherName")
|
"&", "and" -> asmgen.out(" and $otherName")
|
||||||
"^" -> asmgen.out(" eor $otherName")
|
"|", "or" -> asmgen.out(" ora $otherName")
|
||||||
"|" -> 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -438,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> {
|
"&", "and" -> {
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" and #$value")
|
asmgen.out(" and #$value")
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -446,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else
|
else
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
"^" -> {
|
"|", "or" -> {
|
||||||
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")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
asmgen.out(" ora #$value")
|
asmgen.out(" ora #$value")
|
||||||
if(ptrOnZp)
|
if(ptrOnZp)
|
||||||
@ -462,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else
|
else
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
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")
|
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.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
}
|
}
|
||||||
"^" -> {
|
"|", "or" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
|
||||||
asmgen.out(" eor $name | sta $name")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" ora $name | sta $name")
|
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")
|
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")
|
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||||
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||||
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||||
"and" -> asmgen.out("""
|
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||||
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""")
|
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -680,9 +668,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
|
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||||
"|" -> 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -949,7 +938,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> {
|
"&", "and" -> {
|
||||||
when {
|
when {
|
||||||
value == 0 -> {
|
value == 0 -> {
|
||||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||||
@ -974,15 +963,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
when {
|
when {
|
||||||
value == 0 -> {}
|
value == 0 -> {}
|
||||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
||||||
@ -990,6 +971,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
|
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 | 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1100,7 +1090,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> {
|
"&", "and" -> {
|
||||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||||
@ -1109,8 +1099,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||||
"|" -> 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1181,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
"<<", ">>" -> 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")
|
"&", "and" -> 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")
|
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||||
"|" -> 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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1356,7 +1348,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bne -
|
bne -
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
"&" -> {
|
"&", "and" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" and $name | sta $name")
|
asmgen.out(" and $name | sta $name")
|
||||||
if(dt in WordDatatypes) {
|
if(dt in WordDatatypes) {
|
||||||
@ -1366,14 +1358,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" lda #0 | sta $name+1")
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"^" -> {
|
"|", "or" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
|
||||||
asmgen.out(" eor $name | sta $name")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
asmgen.out(" ora $name | sta $name")
|
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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1402,18 +1395,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
remainderVarByWordInAY()
|
remainderVarByWordInAY()
|
||||||
}
|
}
|
||||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||||
"&" -> {
|
"&", "and" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
||||||
}
|
}
|
||||||
"^" -> {
|
"|", "or" -> {
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
|
||||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
|
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")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1461,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1480,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
if(CompilationTarget.instance is Cx16Target) {
|
||||||
lda #<$name
|
// cx16 doesn't have FPWR() only FPWRT()
|
||||||
ldy #>$name
|
asmgen.out("""
|
||||||
jsr floats.CONUPK
|
lda #<$name
|
||||||
lda #<$otherName
|
ldy #>$name
|
||||||
ldy #>$otherName
|
jsr floats.CONUPK
|
||||||
jsr floats.FPWR
|
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("""
|
asmgen.out("""
|
||||||
@ -1529,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
@ -1545,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
if(CompilationTarget.instance is Cx16Target) {
|
||||||
lda #<$name
|
// cx16 doesn't have FPWR() only FPWRT()
|
||||||
ldy #>$name
|
asmgen.out("""
|
||||||
jsr floats.CONUPK
|
lda #<$name
|
||||||
lda #<$constValueName
|
ldy #>$name
|
||||||
ldy #>$constValueName
|
jsr floats.CONUPK
|
||||||
jsr floats.FPWR
|
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)
|
if (value == 0.0)
|
||||||
@ -1601,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
jsr floats.FDIV
|
jsr floats.FDIV
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||||
}
|
}
|
||||||
// store Fac1 back into memory
|
// store Fac1 back into memory
|
||||||
|
@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
override fun launchEmulator(programName: String) {
|
override fun launchEmulator(programName: String) {
|
||||||
for(emulator in listOf("x16emu")) {
|
for(emulator in listOf("x16emu")) {
|
||||||
println("\nStarting Commander X16 emulator $emulator...")
|
println("\nStarting Commander X16 emulator $emulator...")
|
||||||
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
|
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
|
||||||
"-run", "-prg", programName + ".prg")
|
|
||||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||||
val process: Process
|
val process: Process
|
||||||
try {
|
try {
|
||||||
|
@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
FSignature("set_irqd" , false, emptyList(), null),
|
FSignature("set_irqd" , false, emptyList(), null),
|
||||||
FSignature("clear_irqd" , false, emptyList(), null),
|
FSignature("clear_irqd" , false, emptyList(), null),
|
||||||
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
|
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("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
||||||
FSignature("memcopy" , false, listOf(
|
FSignature("memcopy" , false, listOf(
|
||||||
FParam("from", IterableDatatypes + DataType.UWORD),
|
FParam("from", IterableDatatypes + DataType.UWORD),
|
||||||
@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
|||||||
FParam("numwords", setOf(DataType.UWORD)),
|
FParam("numwords", setOf(DataType.UWORD)),
|
||||||
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||||
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
|
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(
|
FSignature("substr" , false, listOf(
|
||||||
FParam("source", IterableDatatypes + DataType.UWORD),
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
FParam("target", 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]
|
val argument=args[0]
|
||||||
if(argument is StringLiteralValue)
|
if(argument is StringLiteralValue)
|
||||||
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
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!=null) {
|
||||||
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
||||||
throw SyntaxError("strlen must have string argument", position)
|
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.AstWalker
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
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> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val leftconst = expr.left.constValue(program)
|
val leftconst = expr.left.constValue(program)
|
||||||
val rightconst = expr.right.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 {
|
val subExpr: BinaryExpression? = when {
|
||||||
leftconst!=null -> expr.right as? BinaryExpression
|
leftconst!=null -> expr.right as? BinaryExpression
|
||||||
@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
val change = groupTwoConstsTogether(expr, subExpr,
|
val change = groupTwoConstsTogether(expr, subExpr,
|
||||||
leftconst != null, rightconst != null,
|
leftconst != null, rightconst != null,
|
||||||
subleftconst != null, subrightconst != 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) {
|
if(leftconst != null && rightconst != null) {
|
||||||
val evaluator = ConstExprEvaluator()
|
val evaluator = ConstExprEvaluator()
|
||||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
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> {
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
@ -6,6 +6,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
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)
|
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
||||||
return listOf(IAstModification.SwapOperands(expr))
|
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
|
// X + (-A) --> X - A
|
||||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (leftVal == null && rightVal == null)
|
if (leftVal == null && rightVal == null)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||||
if (rightVal2 != null) {
|
if (rightVal2 != null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: NumericLiteralValue = rightVal2
|
val rightConst: NumericLiteralValue = rightVal2
|
||||||
@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
if (leftVal == null && rightVal == null)
|
if (leftVal == null && rightVal == null)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||||
if (rightVal2 != null) {
|
if (rightVal2 != null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val leftValue: Expression = expr2.left
|
val leftValue: Expression = expr2.left
|
||||||
@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
return null
|
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) {
|
if (expr.operator in associativeOperators && leftVal != null) {
|
||||||
// swap left and right so that right is always the constant
|
// swap left and right so that right is always the constant
|
||||||
val tmp = expr.left
|
val tmp = expr.left
|
||||||
expr.left = expr.right
|
expr.left = expr.right
|
||||||
expr.right = tmp
|
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 op1 = binExpr.operator
|
||||||
val op2 = rExpr.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)
|
// associative operator, make sure the constant numeric value is second (right)
|
||||||
return listOf(IAstModification.SwapOperands(rExpr))
|
return listOf(IAstModification.SwapOperands(rExpr))
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ internal class ModuleImporter {
|
|||||||
|
|
||||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||||
val fileName = "$name.p8"
|
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")
|
val propPath = System.getProperty("prog8.libdir")
|
||||||
if(propPath!=null)
|
if(propPath!=null)
|
||||||
|
@ -814,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.
|
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).
|
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
|
Miscellaneous
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
exit(returncode)
|
exit(returncode)
|
||||||
@ -830,6 +836,12 @@ mkword(msb, lsb)
|
|||||||
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
||||||
So mkword($80, $22) results in $8022.
|
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()
|
rnd()
|
||||||
returns a pseudo-random byte from 0..255
|
returns a pseudo-random byte from 0..255
|
||||||
|
|
||||||
@ -894,6 +906,10 @@ set_irqd() / clear_irqd()
|
|||||||
swap(x, y)
|
swap(x, y)
|
||||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
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
|
Library routines
|
||||||
----------------
|
----------------
|
||||||
|
@ -324,6 +324,10 @@ main {
|
|||||||
txt.print(result)
|
txt.print(result)
|
||||||
txt.chrout('\n')
|
txt.chrout('\n')
|
||||||
|
|
||||||
|
void strcopy(s2, s1)
|
||||||
|
txt.print_ub(99+strcopy(s2,s1))
|
||||||
|
txt.chrout('\n')
|
||||||
|
|
||||||
test_stack.test()
|
test_stack.test()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,12 @@
|
|||||||
%import test_stack
|
%import test_stack
|
||||||
%zeropage basicsafe
|
%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 {
|
main {
|
||||||
|
|
||||||
ubyte perform_scroll = false
|
ubyte perform_scroll = false
|
||||||
@ -17,7 +23,7 @@ main {
|
|||||||
|
|
||||||
c64.SCROLX &= %11110111 ; 38 column mode
|
c64.SCROLX &= %11110111 ; 38 column mode
|
||||||
|
|
||||||
c64.set_rasterirq(40) ; enable animation
|
c64.set_rasterirq(200) ; enable animation
|
||||||
|
|
||||||
ubyte target_height = 10
|
ubyte target_height = 10
|
||||||
ubyte active_height = 24
|
ubyte active_height = 24
|
||||||
@ -123,10 +129,10 @@ spritedata $0f00 {
|
|||||||
|
|
||||||
|
|
||||||
irq {
|
irq {
|
||||||
ubyte smoothx=7
|
ubyte smoothx=0
|
||||||
sub irq() {
|
sub irq() {
|
||||||
smoothx = (smoothx-1) & 7
|
smoothx = (smoothx-1) & 7
|
||||||
main.perform_scroll = smoothx==0
|
main.perform_scroll = smoothx==7
|
||||||
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
|
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
125
examples/cx16/imageviewer/bmp_module.p8
Normal file
125
examples/cx16/imageviewer/bmp_module.p8
Normal 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
examples/cx16/imageviewer/ci_module.p8
Normal file
140
examples/cx16/imageviewer/ci_module.p8
Normal 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
examples/cx16/imageviewer/iff_module.p8
Normal file
214
examples/cx16/imageviewer/iff_module.p8
Normal 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
examples/cx16/imageviewer/imageviewer.p8
Normal file
197
examples/cx16/imageviewer/imageviewer.p8
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +1,28 @@
|
|||||||
%target cx16
|
%target cx16
|
||||||
%import graphics
|
%import graphics
|
||||||
%import textio
|
|
||||||
%import diskio
|
%import diskio
|
||||||
%import c64colors
|
%import c64colors
|
||||||
|
|
||||||
main {
|
koala_module {
|
||||||
const uword load_location = $4000
|
const uword load_location = $6000
|
||||||
|
|
||||||
sub start() {
|
sub show_image(uword filenameptr) -> ubyte {
|
||||||
|
ubyte load_ok=false
|
||||||
str[] pictures = [
|
if diskio.f_open(8, filenameptr) {
|
||||||
"i01-blubb-sphinx.bin",
|
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
|
||||||
"i02-bugjam-jsl.bin",
|
if size==2 {
|
||||||
"i03-dinothawr-ar.bin",
|
size = diskio.f_read(load_location, 10001)
|
||||||
"i04-fox-leon.bin",
|
if size == 10001 {
|
||||||
"i05-hunter-agod.bin",
|
; set a better C64 color palette, the Cx16's default is too saturated
|
||||||
"i06-jazzman-jds.bin",
|
c64colors.set_palette_pepto()
|
||||||
"i07-katakis-jegg.bin"
|
convert_koalapic()
|
||||||
]
|
load_ok = true
|
||||||
|
}
|
||||||
; set a better C64 color palette, the Cx16's default is too saturated
|
|
||||||
c64colors.set_palette_pepto()
|
|
||||||
graphics.enable_bitmap_mode()
|
|
||||||
repeat {
|
|
||||||
ubyte file_idx
|
|
||||||
for file_idx in 0 to len(pictures)-1 {
|
|
||||||
load_image(pictures[file_idx])
|
|
||||||
wait()
|
|
||||||
}
|
}
|
||||||
|
diskio.f_close()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sub wait() {
|
return load_ok
|
||||||
uword jiffies = 0
|
|
||||||
c64.SETTIM(0,0,0)
|
|
||||||
|
|
||||||
while jiffies < 180 {
|
|
||||||
; read clock
|
|
||||||
%asm {{
|
|
||||||
stx P8ZP_SCRATCH_REG
|
|
||||||
jsr c64.RDTIM
|
|
||||||
sta jiffies
|
|
||||||
stx jiffies+1
|
|
||||||
ldx P8ZP_SCRATCH_REG
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load_image(uword filenameptr) {
|
|
||||||
uword length = diskio.load(8, filenameptr, load_location)
|
|
||||||
|
|
||||||
if length != 10001 {
|
|
||||||
txt.print_uw(length)
|
|
||||||
txt.print("\nload error\n")
|
|
||||||
diskio.status(8)
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
convert_koalapic()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub convert_koalapic() {
|
sub convert_koalapic() {
|
||||||
@ -69,6 +35,8 @@ main {
|
|||||||
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
|
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
|
||||||
ubyte[8] pixels
|
ubyte[8] pixels
|
||||||
|
|
||||||
|
graphics.clear_screen(1, 0)
|
||||||
|
|
||||||
for cy in 0 to 24*8 step 8 {
|
for cy in 0 to 24*8 step 8 {
|
||||||
for cx in 0 to 39 {
|
for cx in 0 to 39 {
|
||||||
for d in 0 to 7 {
|
for d in 0 to 7 {
|
180
examples/cx16/imageviewer/pcx_module.p8
Normal file
180
examples/cx16/imageviewer/pcx_module.p8
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
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) {
|
if diskio.lf_start_list(8, "cub", false) {
|
||||||
txt.print("\nfiles starting with 'cub':\n")
|
txt.print("\nfiles starting with 'cub':\n")
|
||||||
|
@ -1,23 +1,13 @@
|
|||||||
%import textio
|
%import textio
|
||||||
%import diskio
|
%import diskio
|
||||||
%import floats
|
%import floats
|
||||||
|
%import graphics
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%import test_stack
|
%import test_stack
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start () {
|
||||||
ubyte[] barr = [0,0,0]
|
|
||||||
uword[] warr = [0,0,0]
|
|
||||||
|
|
||||||
ubyte xx = 0
|
|
||||||
barr[1] = xx+9
|
|
||||||
warr[1] = &warr
|
|
||||||
|
|
||||||
txt.print_ub(barr[1])
|
|
||||||
txt.chrout('\n')
|
|
||||||
txt.print_uwhex(warr[1],1 )
|
|
||||||
txt.chrout('\n')
|
|
||||||
|
|
||||||
test_stack.test()
|
test_stack.test()
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<keywords keywords="&;->;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
|
<keywords keywords="&;->;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
|
||||||
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
|
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
|
||||||
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
|
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
|
||||||
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
|
<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>
|
</highlighting>
|
||||||
<extensionMap>
|
<extensionMap>
|
||||||
<mapping ext="p8" />
|
<mapping ext="p8" />
|
||||||
|
@ -6,3 +6,6 @@ The exact path may vary with the version of the IDE,
|
|||||||
but for me it is currently this on Linux:
|
but for me it is currently this on Linux:
|
||||||
|
|
||||||
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
|
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
|
||||||
|
|
||||||
|
|
||||||
|
(note the version number in the path, adjust accordingly)
|
||||||
|
Reference in New Issue
Block a user