2020-12-25 01:59:19 +00:00
|
|
|
%target cx16
|
2020-12-23 00:55:15 +00:00
|
|
|
|
2020-12-25 01:59:19 +00:00
|
|
|
; Bitmap pixel graphics module for the CommanderX16
|
|
|
|
; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
|
2020-12-26 02:10:53 +00:00
|
|
|
; This only works on the Cx16. No text layer is currently shown, text can be drawn as part of the bitmap itself.
|
|
|
|
; Note: for compatible graphics code that words on C64 too, use the "graphics" module instead.
|
2020-12-26 12:38:14 +00:00
|
|
|
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.
|
2020-12-25 01:59:19 +00:00
|
|
|
|
2020-12-25 16:00:11 +00:00
|
|
|
; TODO this is in development. Add line drawing, circles and discs (like the graphics module has)
|
2020-12-23 00:55:15 +00:00
|
|
|
|
|
|
|
gfx2 {
|
|
|
|
|
2020-12-23 01:30:46 +00:00
|
|
|
; read-only control variables:
|
2020-12-23 00:55:15 +00:00
|
|
|
ubyte active_mode = 255
|
|
|
|
uword width = 0
|
|
|
|
uword height = 0
|
|
|
|
ubyte bpp = 0
|
|
|
|
|
2020-12-26 00:25:52 +00:00
|
|
|
sub screen_mode(ubyte mode) {
|
2020-12-23 00:55:15 +00:00
|
|
|
; mode 0 = bitmap 320 x 240 x 1c monochrome
|
|
|
|
; mode 1 = bitmap 320 x 240 x 256c
|
|
|
|
; mode 128 = bitmap 640 x 480 x 1c monochrome
|
|
|
|
; ...other modes?
|
|
|
|
|
2020-12-26 00:25:52 +00:00
|
|
|
; copy the lower-case charset to the upper part of the vram, so we can use it later to plot text
|
|
|
|
|
2020-12-23 00:55:15 +00:00
|
|
|
when mode {
|
|
|
|
0 -> {
|
|
|
|
; 320 x 240 x 1c
|
|
|
|
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 = %00000100
|
|
|
|
cx16.VERA_L1_MAPBASE = 0
|
|
|
|
cx16.VERA_L1_TILEBASE = 0
|
|
|
|
width = 320
|
|
|
|
height = 240
|
|
|
|
bpp = 1
|
|
|
|
}
|
|
|
|
1 -> {
|
|
|
|
; 320 x 240 x 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
|
|
|
|
}
|
|
|
|
128 -> {
|
|
|
|
; 640 x 480 x 1c
|
|
|
|
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 = %00000100
|
|
|
|
cx16.VERA_L1_MAPBASE = 0
|
|
|
|
cx16.VERA_L1_TILEBASE = %00000001
|
|
|
|
width = 640
|
|
|
|
height = 480
|
|
|
|
bpp = 1
|
|
|
|
}
|
2020-12-25 16:00:11 +00:00
|
|
|
255 -> {
|
|
|
|
; back to default text mode and colors
|
|
|
|
cx16.VERA_CTRL = %10000000 ; reset VERA and palette
|
|
|
|
c64.CINT() ; back to text mode
|
|
|
|
width = 0
|
|
|
|
height = 0
|
|
|
|
bpp = 0
|
|
|
|
}
|
2020-12-23 00:55:15 +00:00
|
|
|
}
|
2020-12-26 00:25:52 +00:00
|
|
|
|
2020-12-23 00:55:15 +00:00
|
|
|
active_mode = mode
|
2020-12-25 16:00:11 +00:00
|
|
|
if bpp
|
|
|
|
clear_screen()
|
2020-12-23 00:55:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub clear_screen() {
|
2020-12-25 16:00:11 +00:00
|
|
|
position(0, 0)
|
2020-12-23 00:55:15 +00:00
|
|
|
when active_mode {
|
|
|
|
0 -> {
|
|
|
|
; 320 x 240 x 1c
|
|
|
|
repeat 240/2/8
|
|
|
|
cs_innerloop640()
|
|
|
|
}
|
|
|
|
1 -> {
|
|
|
|
; 320 x 240 x 256c
|
|
|
|
repeat 240/2
|
|
|
|
cs_innerloop640()
|
|
|
|
}
|
|
|
|
128 -> {
|
|
|
|
; 640 x 480 x 1c
|
|
|
|
repeat 480/8
|
|
|
|
cs_innerloop640()
|
|
|
|
}
|
|
|
|
}
|
2020-12-25 16:00:11 +00:00
|
|
|
position(0, 0)
|
2020-12-23 00:55:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub plot(uword x, uword y, ubyte color) {
|
|
|
|
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
2020-12-24 06:12:59 +00:00
|
|
|
uword addr
|
|
|
|
ubyte value
|
2020-12-23 00:55:15 +00:00
|
|
|
when active_mode {
|
2020-12-24 06:12:59 +00:00
|
|
|
0 -> {
|
|
|
|
addr = x/8 + y*(320/8)
|
|
|
|
value = bits[lsb(x)&7]
|
2020-12-25 16:00:11 +00:00
|
|
|
cx16.vpoke_or(0, addr, value)
|
2020-12-24 06:12:59 +00:00
|
|
|
}
|
|
|
|
128 -> {
|
|
|
|
addr = x/8 + y*(640/8)
|
|
|
|
value = bits[lsb(x)&7]
|
2020-12-25 16:00:11 +00:00
|
|
|
cx16.vpoke_or(0, addr, value)
|
2020-12-24 06:12:59 +00:00
|
|
|
}
|
2020-12-23 00:55:15 +00:00
|
|
|
1 -> {
|
|
|
|
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L
|
2020-12-26 02:10:53 +00:00
|
|
|
value = lsb(cx16.r1)
|
|
|
|
cx16.vpoke(value, cx16.r0, color)
|
2020-12-23 00:55:15 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-23 04:04:19 +00:00
|
|
|
; activate vera auto-increment mode so next_pixel() can be used after this
|
|
|
|
cx16.VERA_ADDR_H = (cx16.VERA_ADDR_H & %00000111) | %00010000
|
|
|
|
}
|
|
|
|
|
2020-12-25 16:00:11 +00:00
|
|
|
sub position(uword x, uword y) {
|
2020-12-23 04:04:19 +00:00
|
|
|
when active_mode {
|
2020-12-24 06:12:59 +00:00
|
|
|
0 -> {
|
2020-12-26 00:25:52 +00:00
|
|
|
cx16.r0 = y*(320/8) + x/8
|
|
|
|
cx16.vaddr(0, cx16.r0, 0, 1)
|
2020-12-24 06:12:59 +00:00
|
|
|
}
|
|
|
|
128 -> {
|
2020-12-26 00:25:52 +00:00
|
|
|
cx16.r0 = y*(640/8) + x/8
|
|
|
|
cx16.vaddr(0, cx16.r0, 0, 1)
|
2020-12-24 06:12:59 +00:00
|
|
|
}
|
2020-12-23 04:04:19 +00:00
|
|
|
1 -> {
|
|
|
|
void addr_mul_320_add_24(y, x) ; 24 bits result is in r0 and r1L
|
2020-12-24 06:12:59 +00:00
|
|
|
ubyte bank = lsb(cx16.r1)
|
2020-12-25 16:00:11 +00:00
|
|
|
cx16.vaddr(bank, cx16.r0, 0, 1)
|
2020-12-23 04:04:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-23 00:55:15 +00:00
|
|
|
|
2020-12-26 04:33:00 +00:00
|
|
|
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. for 1 bpp screens it will actually plot 8 pixels at once (bitmask).
|
2020-12-26 04:33:00 +00:00
|
|
|
; For super fast pixel plotting, don't call this subroutine but instead just use the assignment: cx16.VERA_DATA0 = color
|
2020-12-23 04:04:19 +00:00
|
|
|
%asm {{
|
|
|
|
sta cx16.VERA_DATA0
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub next_pixels(uword pixels, uword amount) {
|
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, but for 1 bpp screens the bytes contain 8 pixels each.
|
2020-12-23 04:04:19 +00:00
|
|
|
repeat msb(amount) {
|
|
|
|
repeat 256 {
|
|
|
|
cx16.VERA_DATA0 = @(pixels)
|
|
|
|
pixels++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
repeat lsb(amount) {
|
|
|
|
cx16.VERA_DATA0 = @(pixels)
|
|
|
|
pixels++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-25 16:00:11 +00:00
|
|
|
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) {
|
2020-12-26 02:10:53 +00:00
|
|
|
; this is only useful in 256 color mode where one pixel equals one byte value.
|
2020-12-25 16:00:11 +00:00
|
|
|
%asm {{
|
|
|
|
phx
|
|
|
|
ldx #8
|
|
|
|
- asl cx16.r0
|
|
|
|
bcc +
|
|
|
|
sta cx16.VERA_DATA0
|
|
|
|
bra ++
|
|
|
|
+ sty cx16.VERA_DATA0
|
|
|
|
+ dex
|
|
|
|
bne -
|
|
|
|
plx
|
|
|
|
rts
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-12-26 02:10:53 +00:00
|
|
|
const ubyte charset_orig_bank = $0
|
|
|
|
const uword charset_orig_addr = $f800 ; in bank 0, so $0f800
|
|
|
|
const ubyte charset_bank = $1
|
|
|
|
const uword charset_addr = $f000 ; in bank 1, so $1f000
|
|
|
|
|
|
|
|
sub text_charset(ubyte charset) {
|
|
|
|
; -- make a copy of the selected character set to use with text()
|
|
|
|
; 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)
|
|
|
|
cx16.vaddr(charset_orig_bank, charset_orig_addr, 0, 1)
|
|
|
|
cx16.vaddr(charset_bank, charset_addr, 1, 1)
|
|
|
|
repeat 256*8 {
|
|
|
|
cx16.VERA_DATA1 = cx16.VERA_DATA0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-26 00:25:52 +00:00
|
|
|
sub text(uword x, uword y, 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
|
|
|
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 !
|
|
|
|
uword chardataptr
|
|
|
|
when active_mode {
|
|
|
|
0, 128 -> {
|
|
|
|
; 1-bitplane modes
|
2020-12-26 02:10:53 +00:00
|
|
|
cx16.r2 = 40
|
2020-12-26 00:25:52 +00:00
|
|
|
if active_mode>=128
|
2020-12-26 02:10:53 +00:00
|
|
|
cx16.r2 = 80
|
2020-12-26 00:25:52 +00:00
|
|
|
while @(sctextptr) {
|
|
|
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
|
|
|
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
|
|
|
position(x,y)
|
2020-12-26 02:10:53 +00:00
|
|
|
%asm {{
|
|
|
|
lda cx16.VERA_ADDR_H
|
|
|
|
and #%111 ; don't auto-increment, we have to do that manually because of the ora
|
|
|
|
sta cx16.VERA_ADDR_H
|
|
|
|
ldy #8
|
|
|
|
- lda cx16.VERA_DATA0
|
|
|
|
ora cx16.VERA_DATA1
|
|
|
|
sta cx16.VERA_DATA0
|
|
|
|
lda cx16.VERA_ADDR_L
|
|
|
|
clc
|
|
|
|
adc cx16.r2
|
|
|
|
sta cx16.VERA_ADDR_L
|
|
|
|
bcc +
|
|
|
|
inc cx16.VERA_ADDR_M
|
|
|
|
+ lda x
|
|
|
|
clc
|
|
|
|
adc #1
|
|
|
|
sta x
|
|
|
|
bcc +
|
|
|
|
inc x+1
|
|
|
|
+ dey
|
|
|
|
bne -
|
|
|
|
}}
|
2020-12-26 00:25:52 +00:00
|
|
|
sctextptr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
1 -> {
|
|
|
|
; 320 x 240 x 256c
|
|
|
|
while @(sctextptr) {
|
|
|
|
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
|
|
|
cx16.vaddr(charset_bank, chardataptr, 1, 1)
|
|
|
|
repeat 8 {
|
|
|
|
position(x,y)
|
|
|
|
y++
|
|
|
|
%asm {{
|
2020-12-26 02:10:53 +00:00
|
|
|
phx
|
|
|
|
ldx #1
|
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 -
|
2020-12-26 02:10:53 +00:00
|
|
|
plx
|
2020-12-26 00:25:52 +00:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
x+=8
|
|
|
|
y-=8
|
|
|
|
sctextptr++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-23 04:04:19 +00:00
|
|
|
asmsub cs_innerloop640() {
|
|
|
|
%asm {{
|
|
|
|
ldy #80
|
|
|
|
- stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
stz cx16.VERA_DATA0
|
|
|
|
dey
|
|
|
|
bne -
|
|
|
|
rts
|
|
|
|
}}
|
|
|
|
}
|
2020-12-23 00:55:15 +00:00
|
|
|
|
2020-12-23 04:04:19 +00:00
|
|
|
asmsub addr_mul_320_add_24(uword address @R0, uword value @AY) -> uword @R0, ubyte @R1 {
|
2020-12-23 00:55:15 +00:00
|
|
|
%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
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|