prog8/compiler/res/prog8lib/cx16/gfx2.p8

916 lines
31 KiB
Plaintext
Raw Normal View History

2021-02-01 21:18:00 +00:00
; Bitmap pixel graphics routines for the CommanderX16
2020-12-25 01:59:19 +00:00
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
2021-02-21 21:48:06 +00:00
; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
2021-02-01 21:18:00 +00:00
;
; No text layer is currently shown, text can be drawn as part of the bitmap itself.
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
; Note: for identical routines for a monochrome 1 bpp screen, use the "monogfx" module instead.
; Note: for color palette manipulation, use the "palette" module or write Vera registers yourself.
2021-02-01 21:18:00 +00:00
; Note: this library implements code for various resolutions and color depths. This takes up memory.
; If you're memory constrained you should probably not use this built-in library,
; but make a copy in your project only containing the code for the required resolution.
;
;
; SCREEN MODE LIST:
; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 x 256c (8 bpp)
; mode 2 = bitmap 640 x 480 x 4c (2 bpp. there's not enough vram for more colors in hires mode.)
2023-07-26 19:43:58 +00:00
; mode 3 = bitmap 320 x 240 x 16c (not yet implemented: just use 256c, there's enough vram for that)
; mode 4 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that)
; higher color depths in highres are not supported due to lack of VRAM
gfx2 {
2023-06-29 22:29:50 +00:00
%option no_symbol_prefixing
; read-only control variables:
ubyte active_mode = 0
uword width = 0
uword height = 0
ubyte bpp = 0
2020-12-26 00:25:52 +00:00
sub screen_mode(ubyte mode) {
cx16.VERA_CTRL=0
when mode {
1 -> {
; lores 256c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64
cx16.VERA_L1_CONFIG = %00000111
cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = 0
width = 320
height = 240
bpp = 8
}
2 -> {
2021-01-27 01:31:20 +00:00
; highres 4c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 128
cx16.VERA_DC_VSCALE = 128
cx16.VERA_L1_CONFIG = %00000101
cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = %00000001
width = 640
height = 480
bpp = 2
}
else -> {
; back to default text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
cbm.CINT()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
width = 0
height = 0
bpp = 0
mode = 0
}
}
2020-12-26 00:25:52 +00:00
active_mode = mode
if bpp
clear_screen(0)
}
sub clear_screen(ubyte color) {
position(0, 0)
when active_mode {
1 -> {
; lores 256c
repeat 240/2
cs_innerloop640(color)
}
2 -> {
2021-01-27 01:31:20 +00:00
; highres 4c
ubyte[] colors = [%00000000, %01010101, %10101010, %11111111]
color = colors[color&3]
2021-01-27 01:31:20 +00:00
repeat 480/4
cs_innerloop640(color)
2021-01-27 01:31:20 +00:00
}
}
position(0, 0)
}
sub rect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
if rwidth==0 or rheight==0
2020-12-27 16:34:25 +00:00
return
horizontal_line(xx, yy, rwidth, color)
if rheight==1
2020-12-27 16:34:25 +00:00
return
horizontal_line(xx, yy+rheight-1, rwidth, color)
vertical_line(xx, yy+1, rheight-2, color)
if rwidth==1
2020-12-27 16:34:25 +00:00
return
vertical_line(xx+rwidth-1, yy+1, rheight-2, color)
2020-12-27 16:34:25 +00:00
}
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, ubyte color) {
; Draw a filled rectangle of the given size and color.
; To fill the whole screen, use clear_screen(color) instead - it is much faster.
if rwidth==0
2020-12-27 16:34:25 +00:00
return
repeat rheight {
horizontal_line(xx, yy, rwidth, color)
yy++
2020-12-27 16:34:25 +00:00
}
}
sub horizontal_line(uword xx, uword yy, uword length, ubyte color) {
2020-12-27 16:34:25 +00:00
if length==0
return
2020-12-27 14:14:44 +00:00
when active_mode {
1 -> {
2021-01-27 01:31:20 +00:00
; lores 256c
position(xx, yy)
2021-01-27 01:31:20 +00:00
%asm {{
lda color
ldx length+1
beq +
ldy #0
- sta cx16.VERA_DATA0
iny
bne -
dex
bne -
+ ldy length ; remaining
beq +
- sta cx16.VERA_DATA0
dey
bne -
+
2021-01-27 01:31:20 +00:00
}}
}
2 -> {
; highres 4c ....also mostly usable for lores 4c?
color &= 3
ubyte[4] colorbits
ubyte ii
for ii in 3 downto 0 {
colorbits[ii] = color
color <<= 2
}
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
lda cx16.VERA_ADDR_H
and #%00000111 ; no auto advance
sta cx16.VERA_ADDR_H
stz cx16.VERA_CTRL ; setup vera addr 0
lda cx16.r1
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
ldx xx
}}
2021-01-29 22:52:29 +00:00
repeat length {
%asm {{
txa
and #3
tay
lda cx16.VERA_DATA0
and gfx2.plot.mask4c,y
ora colorbits,y
sta cx16.VERA_DATA0
cpy #%00000011 ; next vera byte?
2021-04-28 00:55:49 +00:00
bne ++
inc cx16.VERA_ADDR_L
2021-04-28 00:55:49 +00:00
bne ++
inc cx16.VERA_ADDR_M
2021-02-04 16:47:52 +00:00
+ bne +
inc cx16.VERA_ADDR_H
+ inx ; next pixel
}}
}
2021-01-27 01:31:20 +00:00
}
2020-12-27 14:14:44 +00:00
}
}
sub vertical_line(uword xx, uword yy, uword lheight, ubyte color) {
2021-01-27 01:31:20 +00:00
when active_mode {
1 -> {
2021-01-27 01:31:20 +00:00
; lores 256c
; set vera auto-increment to 320 pixel increment (=next line)
position(xx,yy)
2021-01-29 22:52:29 +00:00
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
2020-12-29 01:13:38 +00:00
%asm {{
ldy lheight
2020-12-29 21:53:53 +00:00
beq +
2021-01-27 01:31:20 +00:00
lda color
- sta cx16.VERA_DATA0
2020-12-29 01:13:38 +00:00
dey
bne -
2020-12-29 21:53:53 +00:00
+
2020-12-29 01:13:38 +00:00
}}
}
2 -> {
2021-01-27 01:31:20 +00:00
; highres 4c
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
if lheight==0
return
position2(xx,yy,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
;; color &= 3
;; color <<= gfx2.plot.shift4c[lsb(xx) & 3]
cx16.r2L = lsb(xx) & 3
when color & 3 {
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
}
ubyte @shared mask = gfx2.plot.mask4c[lsb(xx) & 3]
repeat lheight {
2021-02-05 17:09:21 +00:00
%asm {{
lda cx16.VERA_DATA0
and mask
ora color
sta cx16.VERA_DATA1
2021-02-05 17:09:21 +00:00
}}
2021-01-27 01:31:20 +00:00
}
2020-12-29 01:13:38 +00:00
}
2020-12-27 16:34:25 +00:00
}
2021-01-27 01:31:20 +00:00
sub set_both_strides(ubyte stride) {
stride <<= 4
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
}
2020-12-27 16:34:25 +00:00
}
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
; Bresenham algorithm.
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases
cx16.r0 = x1
x1 = x2
x2 = cx16.r0
cx16.r0 = y1
y1 = y2
y2 = cx16.r0
2020-12-27 16:34:25 +00:00
}
word @zp dx = (x2 as word)-x1
word @zp dy = (y2 as word)-y1
2020-12-27 16:34:25 +00:00
if dx==0 {
vertical_line(x1, y1, abs(dy) as uword +1, color)
2020-12-27 16:34:25 +00:00
return
}
if dy==0 {
if x1>x2
x1=x2
horizontal_line(x1, y1, abs(dx) as uword +1, color)
2020-12-27 16:34:25 +00:00
return
}
word @zp d = 0
2023-07-29 13:16:48 +00:00
cx16.r1L = true ; 'positive_ix'
2020-12-27 16:34:25 +00:00
if dx < 0 {
dx = -dx
2023-07-29 13:16:48 +00:00
cx16.r1L = false
2020-12-27 16:34:25 +00:00
}
2021-03-09 20:54:31 +00:00
word @zp dx2 = dx*2
word @zp dy2 = dy*2
2020-12-27 16:34:25 +00:00
cx16.r14 = x1 ; internal plot X
if dx >= dy {
2023-07-29 13:16:48 +00:00
if cx16.r1L {
2020-12-27 16:34:25 +00:00
repeat {
plot(cx16.r14, y1, color)
if cx16.r14==x2
return
cx16.r14++
2021-03-09 20:54:31 +00:00
d += dy2
2020-12-27 16:34:25 +00:00
if d > dx {
y1++
2021-03-09 20:54:31 +00:00
d -= dx2
2020-12-27 16:34:25 +00:00
}
}
} else {
repeat {
plot(cx16.r14, y1, color)
if cx16.r14==x2
return
cx16.r14--
2021-03-09 20:54:31 +00:00
d += dy2
2020-12-27 16:34:25 +00:00
if d > dx {
y1++
2021-03-09 20:54:31 +00:00
d -= dx2
2020-12-27 16:34:25 +00:00
}
}
}
}
else {
2023-07-29 13:16:48 +00:00
if cx16.r1L {
2020-12-27 16:34:25 +00:00
repeat {
plot(cx16.r14, y1, color)
if y1 == y2
return
y1++
2021-03-09 20:54:31 +00:00
d += dx2
2020-12-27 16:34:25 +00:00
if d > dy {
cx16.r14++
2021-03-09 20:54:31 +00:00
d -= dy2
2020-12-27 16:34:25 +00:00
}
}
} else {
repeat {
plot(cx16.r14, y1, color)
if y1 == y2
return
y1++
2021-03-09 20:54:31 +00:00
d += dx2
2020-12-27 16:34:25 +00:00
if d > dy {
cx16.r14--
2021-03-09 20:54:31 +00:00
d -= dy2
2020-12-27 16:34:25 +00:00
}
}
}
}
}
sub circle(uword @zp xcenter, uword @zp ycenter, ubyte radius, ubyte color) {
2020-12-27 14:14:44 +00:00
; Midpoint algorithm.
2020-12-27 16:34:25 +00:00
if radius==0
return
2020-12-27 14:14:44 +00:00
ubyte @zp xx = radius
ubyte @zp yy = 0
word @zp decisionOver2 = (1 as word)-xx
; R14 = internal plot X
; R15 = internal plot Y
while xx>=yy {
cx16.r14 = xcenter + xx
cx16.r15 = ycenter + yy
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter - xx
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter + xx
cx16.r15 = ycenter - yy
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter - xx
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter + yy
cx16.r15 = ycenter + xx
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter - yy
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter + yy
cx16.r15 = ycenter - xx
plot(cx16.r14, cx16.r15, color)
cx16.r14 = xcenter - yy
plot(cx16.r14, cx16.r15, color)
yy++
if decisionOver2<=0
decisionOver2 += (yy as word)*2+1
else {
xx--
decisionOver2 += (yy as word -xx)*2+1
}
}
}
2020-12-27 16:34:25 +00:00
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) {
2020-12-27 14:14:44 +00:00
; Midpoint algorithm, filled
2020-12-27 16:34:25 +00:00
if radius==0
return
2020-12-27 14:14:44 +00:00
ubyte @zp yy = 0
2020-12-27 16:34:25 +00:00
word @zp decisionOver2 = (1 as word)-radius
2020-12-27 14:14:44 +00:00
2020-12-27 16:34:25 +00:00
while radius>=yy {
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
2020-12-27 14:14:44 +00:00
yy++
if decisionOver2<=0
decisionOver2 += (yy as word)*2+1
else {
2020-12-27 16:34:25 +00:00
radius--
decisionOver2 += (yy as word -radius)*2+1
2020-12-27 14:14:44 +00:00
}
}
}
sub plot(uword @zp xx, uword @zp yy, ubyte @zp color) {
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] @shared shift4c = [6,4,2,0]
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
when active_mode {
1 -> {
2021-01-27 01:31:20 +00:00
; lores 256c
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
2021-04-28 00:22:21 +00:00
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1
ora #%00010000 ; enable auto-increment so next_pixel() can be used after this
sta cx16.VERA_ADDR_H
lda cx16.r0+1
sta cx16.VERA_ADDR_M
lda cx16.r0
sta cx16.VERA_ADDR_L
lda color
sta cx16.VERA_DATA0
}}
2021-01-27 01:31:20 +00:00
}
2 -> {
; highres 4c ....also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(xx) & 3 ; xbits
; color &= 3
; color <<= shift4c[cx16.r2L]
when color & 3 {
1 -> color = shiftedleft_4c_1[cx16.r2L]
2 -> color = shiftedleft_4c_2[cx16.r2L]
3 -> color = shiftedleft_4c_3[cx16.r2L]
}
2021-04-28 00:39:35 +00:00
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1L
sta cx16.VERA_ADDR_H
lda cx16.r0H
sta cx16.VERA_ADDR_M
lda cx16.r0L
sta cx16.VERA_ADDR_L
ldy cx16.r2L ; xbits
lda mask4c,y
and cx16.VERA_DATA0
ora color
sta cx16.VERA_DATA0
}}
}
}
}
sub pget(uword @zp xx, uword yy) -> ubyte {
when active_mode {
1 -> {
; lores 256c
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1
sta cx16.VERA_ADDR_H
lda cx16.r0+1
sta cx16.VERA_ADDR_M
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
}}
}
2 -> {
; hires 4c
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
%asm {{
stz cx16.VERA_CTRL
lda cx16.r1L
sta cx16.VERA_ADDR_H
lda cx16.r0H
sta cx16.VERA_ADDR_M
lda cx16.r0L
sta cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
pha
lda xx
and #3
tay
lda gfx2.plot.shift4c,y
tay
pla
cpy #0
beq +
- lsr a
dey
bne -
+ and #3
}}
}
else -> return 0
}
2020-12-23 04:04:19 +00:00
}
2023-05-22 22:27:42 +00:00
sub fill(word @zp xx, word @zp yy, ubyte new_color) {
2023-05-22 22:27:42 +00:00
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 48
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
2023-05-22 22:27:42 +00:00
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=height-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
2023-05-22 22:27:42 +00:00
lda sxl
sta stack_xl_lsb,y
2023-05-22 22:27:42 +00:00
lda sxl+1
sta stack_xl_msb,y
2023-05-22 22:27:42 +00:00
lda sxr
sta stack_xr_lsb,y
2023-05-22 22:27:42 +00:00
lda sxr+1
sta stack_xr_msb,y
2023-05-22 22:27:42 +00:00
lda sy
sta stack_y_lsb,y
2023-05-22 22:27:42 +00:00
lda sy+1
sta stack_y_msb,y
2023-05-22 22:27:42 +00:00
ldy cx16.r12L
lda sdy
sta stack_dy,y
inc cx16.r12L
}}
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
2023-05-22 22:27:42 +00:00
sta x1
lda stack_xl_msb,y
2023-05-22 22:27:42 +00:00
sta x1+1
lda stack_xr_lsb,y
2023-05-22 22:27:42 +00:00
sta x2
lda stack_xr_msb,y
2023-05-22 22:27:42 +00:00
sta x2+1
lda stack_y_lsb,y
sta yy
lda stack_y_msb,y
sta yy+1
2023-05-22 22:27:42 +00:00
ldy cx16.r12L
lda stack_dy,y
sta dy
}}
yy+=dy
2023-05-22 22:27:42 +00:00
}
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
2023-05-22 22:27:42 +00:00
if cx16.r11L == cx16.r10L
return
if xx<0 or xx>width-1 or yy<0 or yy>height-1
2023-05-22 22:27:42 +00:00
return
push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1)
2023-05-22 22:27:42 +00:00
word left = 0
while cx16.r12L {
pop_stack()
xx = x1
2023-10-18 21:04:11 +00:00
; TODO: if mode==1 (256c) use vera autodecrement instead of pget(), but code bloat not worth it?
while xx >= 0 and pget(xx as uword, yy as uword) == cx16.r11L
xx--
2023-10-18 21:04:11 +00:00
if x1!=xx
horizontal_line(xx as uword+1, yy as uword, x1-xx as uword, cx16.r10L)
else
2023-05-22 22:27:42 +00:00
goto skip
left = xx + 1
2023-05-22 22:27:42 +00:00
if left < x1
push_stack(left, x1 - 1, yy, -dy)
xx = x1 + 1
2023-05-22 22:27:42 +00:00
do {
cx16.r9 = xx
2023-10-18 21:04:11 +00:00
; TODO: if mode==1 (256c) use vera autoincrement instead of pget(), but code bloat not worth it?
while xx <= width-1 and pget(xx as uword, yy as uword) == cx16.r11L
xx++
if cx16.r9!=xx
horizontal_line(cx16.r9, yy as uword, (xx as uword)-cx16.r9, cx16.r10L)
push_stack(left, xx - 1, yy, dy)
if xx > x2 + 1
push_stack(x2 + 1, xx - 1, yy, -dy)
2023-05-22 22:27:42 +00:00
skip:
xx++
while xx <= x2 and pget(xx as uword, yy as uword) != cx16.r11L
xx++
left = xx
} until xx>x2
2023-05-22 22:27:42 +00:00
}
}
2020-12-23 04:04:19 +00:00
sub position(uword @zp xx, uword yy) {
2020-12-23 04:04:19 +00:00
when active_mode {
1 -> {
2021-01-27 01:31:20 +00:00
; lores 256c
void addr_mul_24_for_lores_256c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
2021-01-27 01:31:20 +00:00
}
2 -> {
2021-01-27 01:31:20 +00:00
; highres 4c
void addr_mul_24_for_highres_4c(yy, xx) ; 24 bits result is in r0 and r1L (highest byte)
2020-12-23 04:04:19 +00:00
}
}
cx16.r2L = cx16.r1L
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
2020-12-23 04:04:19 +00:00
}
sub position2(uword @zp xx, uword yy, bool also_port_1) {
position(xx, yy)
if also_port_1
cx16.vaddr_clone(0)
}
inline asmsub next_pixel(ubyte color @A) {
2020-12-23 04:04:19 +00:00
; -- sets the next pixel byte to the graphics chip.
; for 8 bpp screens this will plot 1 pixel.
2021-01-27 01:56:25 +00:00
; for 2 bpp screens it will plot 4 pixels at once (color = bit pattern).
2020-12-23 04:04:19 +00:00
%asm {{
sta cx16.VERA_DATA0
}}
}
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, X, Y) {
2020-12-26 02:10:53 +00:00
; -- sets the next bunch of pixels from a prepared array of bytes.
; for 8 bpp screens this will plot 1 pixel per byte.
2021-01-27 01:56:25 +00:00
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldx cx16.r0+1
beq +
ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta cx16.VERA_DATA0
iny
bne -
inc P8ZP_SCRATCH_W1+1 ; next page of 256 pixels
dex
bne -
+ ldx cx16.r0 ; remaining pixels
beq +
ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta cx16.VERA_DATA0
iny
dex
bne -
+ rts
}}
2020-12-23 04:04:19 +00:00
}
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) clobbers(X) {
2020-12-26 02:10:53 +00:00
; this is only useful in 256 color mode where one pixel equals one byte value.
%asm {{
ldx #8
- asl cx16.r0
bcc +
sta cx16.VERA_DATA0
bra ++
+ sty cx16.VERA_DATA0
+ dex
bne -
rts
}}
}
2020-12-26 02:10:53 +00:00
const ubyte charset_bank = $1
const uword charset_addr = $f000 ; in bank 1, so $1f000
sub text_charset(ubyte charset) {
; -- select the text charset to use with the text() routine
2020-12-26 02:10:53 +00:00
; the charset number is the same as for the cx16.screen_set_charset() ROM function.
; 1 = ISO charset, 2 = PETSCII uppercase+graphs, 3= PETSCII uppercase+lowercase.
cx16.screen_set_charset(charset, 0)
}
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) {
2020-12-26 02:10:53 +00:00
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use.
2020-12-26 00:25:52 +00:00
uword chardataptr
ubyte[8] @shared char_bitmap_bytes_left
ubyte[8] @shared char_bitmap_bytes_right
2020-12-26 00:25:52 +00:00
when active_mode {
1 -> {
; lores 256c
2020-12-26 00:25:52 +00:00
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1)
repeat 8 {
position(xx,yy)
yy++
2020-12-26 00:25:52 +00:00
%asm {{
ldx color
2020-12-26 00:25:52 +00:00
lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1
ldy #8
2020-12-26 02:10:53 +00:00
- asl P8ZP_SCRATCH_B1
bcc +
stx cx16.VERA_DATA0 ; write a pixel
bra ++
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
+ dey
2020-12-26 00:25:52 +00:00
bne -
}}
}
xx+=8
yy-=8
2020-12-26 00:25:52 +00:00
sctextptr++
}
}
2 -> {
2021-02-01 21:18:00 +00:00
; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color
2021-02-01 21:18:00 +00:00
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
2023-07-03 19:45:09 +00:00
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
position(xx, yy) ; only calculated once, we update vera address in the loop instead
cx16.VERA_ADDR_H &= $0f ; no auto increment
2021-02-01 21:18:00 +00:00
repeat 8 {
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
cx16.r7 = xx
2021-02-01 21:18:00 +00:00
repeat 8 {
cx16.r10L <<= 1
if_cs {
cx16.r2L = cx16.r7L & 3 ; xbits
when cx16.r11L & 3 {
1 -> cx16.r12L = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> cx16.r12L = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> cx16.r12L = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
else -> cx16.r12L = 0
}
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx2.plot.mask4c[cx16.r2L] | cx16.r12L
}
cx16.r7++
if (cx16.r7 & 3) == 0 {
; increment the pixel address by one
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #1
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
2021-02-01 21:18:00 +00:00
}
; increment pixel address to the next line
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #(640-8)/4
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
2021-02-01 21:18:00 +00:00
}
xx+=8
2021-02-01 21:18:00 +00:00
sctextptr++
}
}
2020-12-26 00:25:52 +00:00
}
}
asmsub cs_innerloop640(ubyte color @A) clobbers(Y) {
2020-12-23 04:04:19 +00:00
%asm {{
ldy #80
- sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
2020-12-23 04:04:19 +00:00
dey
bne -
rts
}}
}
asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
; yy * 160 + xx/4 (24 bits calculation)
2021-01-27 01:31:20 +00:00
; 24 bits result is in r0 and r1L (highest byte)
2021-02-04 16:47:52 +00:00
%asm {{
ldy #5
- asl cx16.r2
rol cx16.r2+1
dey
bne -
lda cx16.r2
sta cx16.r0
lda cx16.r2+1
sta cx16.r0+1
asl cx16.r0
rol cx16.r0+1
asl cx16.r0
rol cx16.r0+1
; xx >>= 2 (xx=R3)
lsr cx16.r3+1
ror cx16.r3
lsr cx16.r3+1
ror cx16.r3
; add r2 and xx (r3) to r0 (24-bits)
2021-02-04 16:47:52 +00:00
stz cx16.r1
clc
lda cx16.r0
adc cx16.r2
sta cx16.r0
lda cx16.r0+1
adc cx16.r2+1
sta cx16.r0+1
bcc +
inc cx16.r1
+ clc
lda cx16.r0
adc cx16.r3
2021-02-04 16:47:52 +00:00
sta cx16.r0
lda cx16.r0+1
adc cx16.r3+1
2021-02-04 16:47:52 +00:00
sta cx16.r0+1
bcc +
inc cx16.r1
+
rts
2021-02-04 16:47:52 +00:00
}}
2021-01-27 01:31:20 +00:00
}
asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 {
2021-02-27 02:30:21 +00:00
; yy * 320 + xx (24 bits calculation)
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
sta P8ZP_SCRATCH_B1
lda cx16.r0+1
sta cx16.r1
sta P8ZP_SCRATCH_REG
lda cx16.r0
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
sta cx16.r0
lda P8ZP_SCRATCH_B1
clc
adc P8ZP_SCRATCH_REG
sta cx16.r0+1
bcc +
inc cx16.r1
+ ; now add the value to this 24-bits number
lda cx16.r0
clc
adc P8ZP_SCRATCH_W1
sta cx16.r0
lda cx16.r0+1
adc P8ZP_SCRATCH_W1+1
sta cx16.r0+1
bcc +
inc cx16.r1
+ lda cx16.r1
rts
}}
}
}