2020-09-20 21:49:36 +00:00
|
|
|
%import textio
|
2020-03-24 23:32:54 +00:00
|
|
|
|
2020-03-28 11:33:16 +00:00
|
|
|
; bitmap pixel graphics module for the C64
|
2021-03-04 00:31:29 +00:00
|
|
|
; only black/white monochrome 320x200 for now
|
2020-08-31 19:38:56 +00:00
|
|
|
; assumes bitmap screen memory is $2000-$3fff
|
2020-03-24 23:32:54 +00:00
|
|
|
|
2020-03-28 11:33:16 +00:00
|
|
|
graphics {
|
2020-09-22 19:50:56 +00:00
|
|
|
const uword BITMAP_ADDRESS = $2000
|
|
|
|
const uword WIDTH = 320
|
|
|
|
const ubyte HEIGHT = 200
|
2020-03-24 23:32:54 +00:00
|
|
|
|
2020-03-28 11:33:16 +00:00
|
|
|
sub enable_bitmap_mode() {
|
2020-03-24 23:32:54 +00:00
|
|
|
; enable bitmap screen, erase it and set colors to black/white.
|
2020-11-23 22:05:51 +00:00
|
|
|
c64.SCROLY = %00111011
|
2020-11-22 17:17:43 +00:00
|
|
|
c64.SCROLX = %00001000
|
2020-03-24 23:32:54 +00:00
|
|
|
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
2020-08-28 19:42:53 +00:00
|
|
|
clear_screen(1, 0)
|
2020-08-22 17:00:03 +00:00
|
|
|
}
|
|
|
|
|
2020-11-22 17:17:43 +00:00
|
|
|
sub disable_bitmap_mode() {
|
|
|
|
; enables text mode, erase the text screen, color white
|
2020-11-23 22:05:51 +00:00
|
|
|
c64.SCROLY = %00011011
|
2020-11-22 17:17:43 +00:00
|
|
|
c64.SCROLX = %00001000
|
|
|
|
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
|
|
|
|
txt.fill_screen(' ', 1)
|
|
|
|
}
|
|
|
|
|
2020-08-28 19:42:53 +00:00
|
|
|
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
2021-01-08 00:05:26 +00:00
|
|
|
sys.memset(BITMAP_ADDRESS, 320*200/8, 0)
|
2020-08-30 17:31:20 +00:00
|
|
|
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
2020-03-24 23:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-08-28 19:42:53 +00:00
|
|
|
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
2020-06-03 21:18:49 +00:00
|
|
|
; Bresenham algorithm.
|
2020-12-21 17:28:10 +00:00
|
|
|
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
2021-11-23 20:51:18 +00:00
|
|
|
; TODO implement this as optimized assembly, for instance https://github.com/EgonOlsen71/bresenham/blob/main/src/asm/graphics.asm ??
|
|
|
|
; or from here https://retro64.altervista.org/blog/an-introduction-to-vector-based-graphics-the-commodore-64-rotating-simple-3d-objects/
|
|
|
|
|
2020-06-03 21:18:49 +00:00
|
|
|
if y1>y2 {
|
2020-12-21 17:28:10 +00:00
|
|
|
; make sure dy is always positive to have only 4 instead of 8 special cases
|
2022-06-06 14:01:11 +00:00
|
|
|
cx16.r0 = x1
|
|
|
|
x1 = x2
|
|
|
|
x2 = cx16.r0
|
|
|
|
cx16.r0L = y1
|
|
|
|
y1 = y2
|
|
|
|
y2 = cx16.r0L
|
2020-06-03 21:18:49 +00:00
|
|
|
}
|
2021-03-10 17:49:40 +00:00
|
|
|
word @zp dx = (x2 as word)-x1
|
|
|
|
word @zp dy = (y2 as word)-y1
|
2020-12-29 23:14:35 +00:00
|
|
|
|
|
|
|
if dx==0 {
|
2021-03-10 17:49:40 +00:00
|
|
|
vertical_line(x1, y1, abs(dy) as ubyte +1)
|
2020-12-29 23:14:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if dy==0 {
|
|
|
|
if x1>x2
|
|
|
|
x1=x2
|
2021-03-10 17:49:40 +00:00
|
|
|
horizontal_line(x1, y1, abs(dx) as uword +1)
|
2020-12-29 23:14:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
word @zp d = 0
|
|
|
|
ubyte positive_ix = true
|
2020-06-02 19:27:38 +00:00
|
|
|
if dx < 0 {
|
|
|
|
dx = -dx
|
|
|
|
positive_ix = false
|
2020-03-24 23:32:54 +00:00
|
|
|
}
|
2021-03-09 19:44:06 +00:00
|
|
|
word @zp dx2 = dx*2
|
|
|
|
word @zp dy2 = dy*2
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plotx = x1
|
2020-03-26 22:33:55 +00:00
|
|
|
|
|
|
|
if dx >= dy {
|
2020-06-02 19:27:38 +00:00
|
|
|
if positive_ix {
|
2020-07-25 14:25:02 +00:00
|
|
|
repeat {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(y1)
|
2021-03-15 23:11:55 +00:00
|
|
|
if internal_plotx==x2
|
|
|
|
return
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plotx++
|
2021-03-09 19:44:06 +00:00
|
|
|
d += dy2
|
2021-03-15 23:11:55 +00:00
|
|
|
if d > dx {
|
2020-06-03 21:18:49 +00:00
|
|
|
y1++
|
2021-03-09 19:44:06 +00:00
|
|
|
d -= dx2
|
2020-03-28 11:33:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-25 14:25:02 +00:00
|
|
|
repeat {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(y1)
|
2021-03-15 23:11:55 +00:00
|
|
|
if internal_plotx==x2
|
|
|
|
return
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plotx--
|
2021-03-09 19:44:06 +00:00
|
|
|
d += dy2
|
2021-03-15 23:11:55 +00:00
|
|
|
if d > dx {
|
2020-06-03 21:18:49 +00:00
|
|
|
y1++
|
2021-03-09 19:44:06 +00:00
|
|
|
d -= dx2
|
2020-03-28 11:33:16 +00:00
|
|
|
}
|
2020-03-26 22:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-02 19:27:38 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-06-03 21:18:49 +00:00
|
|
|
if positive_ix {
|
2020-07-25 14:25:02 +00:00
|
|
|
repeat {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(y1)
|
2021-03-15 23:11:55 +00:00
|
|
|
if y1 == y2
|
|
|
|
return
|
2020-06-03 21:18:49 +00:00
|
|
|
y1++
|
2021-03-09 19:44:06 +00:00
|
|
|
d += dx2
|
2021-03-15 23:11:55 +00:00
|
|
|
if d > dy {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plotx++
|
2021-03-09 19:44:06 +00:00
|
|
|
d -= dy2
|
2020-03-28 11:33:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-25 14:25:02 +00:00
|
|
|
repeat {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(y1)
|
2021-03-15 23:11:55 +00:00
|
|
|
if y1 == y2
|
|
|
|
return
|
2020-06-03 21:18:49 +00:00
|
|
|
y1++
|
2021-03-09 19:44:06 +00:00
|
|
|
d += dx2
|
2021-03-15 23:11:55 +00:00
|
|
|
if d > dy {
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plotx--
|
2021-03-09 19:44:06 +00:00
|
|
|
d -= dy2
|
2020-03-28 11:33:16 +00:00
|
|
|
}
|
2020-03-26 22:33:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-24 23:32:54 +00:00
|
|
|
|
2020-12-29 23:14:35 +00:00
|
|
|
sub rect(uword x, ubyte y, uword width, ubyte height) {
|
|
|
|
if width==0 or height==0
|
|
|
|
return
|
|
|
|
horizontal_line(x, y, width)
|
|
|
|
if height==1
|
|
|
|
return
|
|
|
|
horizontal_line(x, y+height-1, width)
|
|
|
|
vertical_line(x, y+1, height-2)
|
|
|
|
if width==1
|
|
|
|
return
|
|
|
|
vertical_line(x+width-1, y+1, height-2)
|
2020-12-29 23:06:01 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 23:14:35 +00:00
|
|
|
sub fillrect(uword x, ubyte y, uword width, ubyte height) {
|
|
|
|
if width==0
|
|
|
|
return
|
|
|
|
repeat height {
|
|
|
|
horizontal_line(x, y, width)
|
|
|
|
y++
|
|
|
|
}
|
2020-12-29 23:06:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub horizontal_line(uword x, ubyte y, uword length) {
|
2020-12-30 17:02:46 +00:00
|
|
|
if length<8 {
|
|
|
|
internal_plotx=x
|
|
|
|
repeat lsb(length) {
|
|
|
|
internal_plot(y)
|
|
|
|
internal_plotx++
|
|
|
|
}
|
2020-12-29 23:06:01 +00:00
|
|
|
return
|
2020-12-30 17:02:46 +00:00
|
|
|
}
|
2020-12-29 23:06:01 +00:00
|
|
|
|
2020-12-30 17:02:46 +00:00
|
|
|
ubyte separate_pixels = lsb(x) & 7
|
|
|
|
uword addr = get_y_lookup(y) + (x&$fff8)
|
|
|
|
|
|
|
|
if separate_pixels {
|
|
|
|
%asm {{
|
|
|
|
lda addr
|
|
|
|
sta P8ZP_SCRATCH_W1
|
|
|
|
lda addr+1
|
|
|
|
sta P8ZP_SCRATCH_W1+1
|
|
|
|
ldy separate_pixels
|
2022-01-17 21:03:53 +00:00
|
|
|
lda hline_filled_right,y
|
2020-12-30 17:02:46 +00:00
|
|
|
eor #255
|
|
|
|
ldy #0
|
|
|
|
ora (P8ZP_SCRATCH_W1),y
|
|
|
|
sta (P8ZP_SCRATCH_W1),y
|
|
|
|
}}
|
|
|
|
addr += 8
|
|
|
|
length += separate_pixels
|
|
|
|
length -= 8
|
|
|
|
}
|
|
|
|
|
|
|
|
if length {
|
|
|
|
%asm {{
|
|
|
|
lda length
|
|
|
|
and #7
|
|
|
|
sta separate_pixels
|
|
|
|
stx P8ZP_SCRATCH_REG
|
|
|
|
lsr length+1
|
|
|
|
ror length
|
|
|
|
lsr length+1
|
|
|
|
ror length
|
|
|
|
lsr length+1
|
|
|
|
ror length
|
|
|
|
lda addr
|
|
|
|
sta _modified+1
|
|
|
|
lda addr+1
|
|
|
|
sta _modified+2
|
|
|
|
lda length
|
|
|
|
ora length+1
|
|
|
|
beq _zero
|
|
|
|
ldy length
|
|
|
|
ldx #$ff
|
|
|
|
_modified stx $ffff ; modified
|
|
|
|
lda _modified+1
|
|
|
|
clc
|
|
|
|
adc #8
|
|
|
|
sta _modified+1
|
|
|
|
bcc +
|
|
|
|
inc _modified+2
|
|
|
|
+ dey
|
|
|
|
bne _modified
|
|
|
|
_zero ldx P8ZP_SCRATCH_REG
|
|
|
|
|
|
|
|
ldy separate_pixels
|
2022-01-17 21:03:53 +00:00
|
|
|
beq hline_zero2
|
2020-12-30 17:02:46 +00:00
|
|
|
lda _modified+1
|
|
|
|
sta P8ZP_SCRATCH_W1
|
|
|
|
lda _modified+2
|
|
|
|
sta P8ZP_SCRATCH_W1+1
|
2022-01-17 21:03:53 +00:00
|
|
|
lda hline_filled_right,y
|
2020-12-30 17:02:46 +00:00
|
|
|
ldy #0
|
|
|
|
ora (P8ZP_SCRATCH_W1),y
|
|
|
|
sta (P8ZP_SCRATCH_W1),y
|
2022-01-17 21:03:53 +00:00
|
|
|
jmp hline_zero2
|
|
|
|
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
|
|
|
hline_zero2
|
2020-12-30 17:02:46 +00:00
|
|
|
}}
|
2020-12-29 23:06:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub vertical_line(uword x, ubyte y, ubyte height) {
|
|
|
|
internal_plotx = x
|
|
|
|
repeat height {
|
|
|
|
internal_plot(y)
|
|
|
|
y++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-26 22:33:55 +00:00
|
|
|
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
2020-03-24 23:32:54 +00:00
|
|
|
; Midpoint algorithm
|
2020-12-27 16:34:25 +00:00
|
|
|
if radius==0
|
|
|
|
return
|
2020-08-28 19:42:53 +00:00
|
|
|
ubyte @zp ploty
|
|
|
|
ubyte @zp yy = 0
|
2020-12-29 23:06:01 +00:00
|
|
|
word @zp decisionOver2 = (1 as word)-radius
|
2020-03-27 22:45:01 +00:00
|
|
|
|
2020-12-29 23:06:01 +00:00
|
|
|
while radius>=yy {
|
|
|
|
internal_plotx = xcenter + radius
|
2020-03-27 22:45:01 +00:00
|
|
|
ploty = ycenter + yy
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
2020-12-29 23:06:01 +00:00
|
|
|
internal_plotx = xcenter - radius
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
2020-12-29 23:06:01 +00:00
|
|
|
internal_plotx = xcenter + radius
|
2020-03-27 22:45:01 +00:00
|
|
|
ploty = ycenter - yy
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
2020-12-29 23:06:01 +00:00
|
|
|
internal_plotx = xcenter - radius
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
|
|
|
internal_plotx = xcenter + yy
|
2020-12-29 23:06:01 +00:00
|
|
|
ploty = ycenter + radius
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
|
|
|
internal_plotx = xcenter - yy
|
|
|
|
internal_plot(ploty)
|
|
|
|
internal_plotx = xcenter + yy
|
2020-12-29 23:06:01 +00:00
|
|
|
ploty = ycenter - radius
|
2020-08-30 22:36:40 +00:00
|
|
|
internal_plot(ploty)
|
|
|
|
internal_plotx = xcenter - yy
|
|
|
|
internal_plot(ploty)
|
2020-03-27 22:45:01 +00:00
|
|
|
yy++
|
2020-03-24 23:32:54 +00:00
|
|
|
if decisionOver2<=0
|
2020-12-29 23:06:01 +00:00
|
|
|
decisionOver2 += (yy as word)*2+1
|
2020-03-24 23:32:54 +00:00
|
|
|
else {
|
2020-12-29 23:06:01 +00:00
|
|
|
radius--
|
|
|
|
decisionOver2 += (yy as word -radius)*2+1
|
2020-03-24 23:32:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-22 00:12:01 +00:00
|
|
|
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
2020-03-26 22:18:51 +00:00
|
|
|
; Midpoint algorithm, filled
|
2020-12-27 16:34:25 +00:00
|
|
|
if radius==0
|
|
|
|
return
|
|
|
|
ubyte @zp yy = 0
|
2020-12-29 23:06:01 +00:00
|
|
|
word decisionOver2 = (1 as word)-radius
|
2020-03-27 22:45:01 +00:00
|
|
|
|
2020-12-29 23:06:01 +00:00
|
|
|
while radius>=yy {
|
2020-12-29 23:14:35 +00:00
|
|
|
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
|
|
|
|
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
|
|
|
|
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
|
|
|
|
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
|
2020-03-27 22:45:01 +00:00
|
|
|
yy++
|
2020-03-26 22:18:51 +00:00
|
|
|
if decisionOver2<=0
|
2020-12-29 23:06:01 +00:00
|
|
|
decisionOver2 += (yy as word)*2+1
|
2020-03-26 22:18:51 +00:00
|
|
|
else {
|
2020-12-29 23:06:01 +00:00
|
|
|
radius--
|
|
|
|
decisionOver2 += (yy as word -radius)*2+1
|
2020-03-26 22:18:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-24 23:32:54 +00:00
|
|
|
|
2020-03-27 22:45:01 +00:00
|
|
|
|
2020-03-28 11:33:16 +00:00
|
|
|
; here is the non-asm code for the plot routine below:
|
|
|
|
; sub plot_nonasm(uword px, ubyte py) {
|
|
|
|
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
2020-09-22 19:50:56 +00:00
|
|
|
; uword addr = BITMAP_ADDRESS + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
2020-03-28 11:33:16 +00:00
|
|
|
; @(addr) |= ormask[lsb(px) & 7]
|
|
|
|
; }
|
|
|
|
|
2020-12-26 04:33:00 +00:00
|
|
|
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
2020-08-30 22:36:40 +00:00
|
|
|
%asm {{
|
2020-12-26 04:33:00 +00:00
|
|
|
stx graphics.internal_plotx
|
|
|
|
sty graphics.internal_plotx+1
|
|
|
|
jsr graphics.internal_plot
|
2020-08-30 22:36:40 +00:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-09-11 22:59:57 +00:00
|
|
|
; for efficiency of internal algorithms here is the internal plot routine
|
|
|
|
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
|
|
|
|
2020-08-30 22:36:40 +00:00
|
|
|
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
2020-03-27 22:45:01 +00:00
|
|
|
|
2020-09-11 22:59:57 +00:00
|
|
|
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
2020-03-27 22:45:01 +00:00
|
|
|
%asm {{
|
|
|
|
tay
|
2020-08-30 22:36:40 +00:00
|
|
|
lda internal_plotx+1
|
2020-08-25 17:44:08 +00:00
|
|
|
sta P8ZP_SCRATCH_W2+1
|
2020-03-27 22:45:01 +00:00
|
|
|
lsr a ; 0
|
2020-08-25 17:44:08 +00:00
|
|
|
sta P8ZP_SCRATCH_W2
|
2020-08-30 22:36:40 +00:00
|
|
|
lda internal_plotx
|
2020-03-27 22:45:01 +00:00
|
|
|
pha
|
|
|
|
and #7
|
|
|
|
tax
|
|
|
|
|
|
|
|
lda _y_lookup_lo,y
|
|
|
|
clc
|
2020-08-25 17:44:08 +00:00
|
|
|
adc P8ZP_SCRATCH_W2
|
|
|
|
sta P8ZP_SCRATCH_W2
|
2020-03-27 22:45:01 +00:00
|
|
|
lda _y_lookup_hi,y
|
2020-08-25 17:44:08 +00:00
|
|
|
adc P8ZP_SCRATCH_W2+1
|
|
|
|
sta P8ZP_SCRATCH_W2+1
|
2020-03-27 22:45:01 +00:00
|
|
|
|
2020-08-30 22:36:40 +00:00
|
|
|
pla ; internal_plotx
|
2020-03-27 22:45:01 +00:00
|
|
|
and #%11111000
|
|
|
|
tay
|
2020-08-25 17:44:08 +00:00
|
|
|
lda (P8ZP_SCRATCH_W2),y
|
2020-03-27 22:45:01 +00:00
|
|
|
ora _ormask,x
|
2020-08-25 17:44:08 +00:00
|
|
|
sta (P8ZP_SCRATCH_W2),y
|
2020-03-27 22:45:01 +00:00
|
|
|
rts
|
|
|
|
|
|
|
|
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
|
|
|
|
|
|
|
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
|
|
|
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
2020-09-22 19:50:56 +00:00
|
|
|
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
|
2020-08-31 19:38:56 +00:00
|
|
|
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
|
|
|
|
|
|
|
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
|
|
|
|
|
|
|
_y_lookup_lo .byte <_plot_y_values
|
|
|
|
_y_lookup_hi .byte >_plot_y_values
|
2020-03-27 22:45:01 +00:00
|
|
|
|
|
|
|
}}
|
2020-03-24 23:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 23:14:35 +00:00
|
|
|
asmsub get_y_lookup(ubyte y @Y) -> uword @AY {
|
|
|
|
%asm {{
|
2020-12-30 17:02:46 +00:00
|
|
|
lda internal_plot._y_lookup_lo,y
|
2020-12-29 23:14:35 +00:00
|
|
|
pha
|
2020-12-30 17:02:46 +00:00
|
|
|
lda internal_plot._y_lookup_hi,y
|
2020-12-29 23:14:35 +00:00
|
|
|
tay
|
|
|
|
pla
|
|
|
|
rts
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-03-24 23:32:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|