added doublebuffering to monogfx (in both lores and hires mode)

This commit is contained in:
Irmen de Jong
2025-05-21 21:28:30 +02:00
parent 322fa7ea69
commit 548e421e27
5 changed files with 125 additions and 78 deletions

View File

@@ -22,18 +22,23 @@ monogfx {
const ubyte MODE_STIPPLE = %00000001 const ubyte MODE_STIPPLE = %00000001
const ubyte MODE_INVERT = %00000010 const ubyte MODE_INVERT = %00000010
uword buffer_visible, buffer_back
sub lores() { sub lores() {
; enable 320*240 bitmap mode ; enable 320*240 bitmap mode
buffer_visible = buffer_back = $0000
cx16.VERA_CTRL=0 cx16.VERA_CTRL=0
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 64 cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64 cx16.VERA_DC_VSCALE = 64
cx16.VERA_L1_CONFIG = %00000100 cx16.VERA_L1_CONFIG = %00000100
cx16.VERA_L1_MAPBASE = 0 cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = 0 cx16.VERA_L1_TILEBASE = 0 ; lores
width = 320 width = 320
height = 240 height = 240
lores_mode = true lores_mode = true
buffer_visible = buffer_back = $0000
mode = MODE_NORMAL mode = MODE_NORMAL
clear_screen(false) clear_screen(false)
} }
@@ -46,14 +51,40 @@ monogfx {
cx16.VERA_DC_VSCALE = 128 cx16.VERA_DC_VSCALE = 128
cx16.VERA_L1_CONFIG = %00000100 cx16.VERA_L1_CONFIG = %00000100
cx16.VERA_L1_MAPBASE = 0 cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = %00000001 cx16.VERA_L1_TILEBASE = %00000001 ; hires
width = 640 width = 640
height = 480 height = 480
lores_mode = false lores_mode = false
buffer_visible = buffer_back = $0000
mode = MODE_NORMAL mode = MODE_NORMAL
clear_screen(false) clear_screen(false)
} }
sub enable_doublebuffer() {
; enable double buffering mode
if lores_mode {
buffer_visible = $0000
buffer_back = $2800
} else {
buffer_visible = $0000
buffer_back = $9800
}
}
sub swap_buffers(bool wait_for_vsync) {
; flip the buffers: make the back buffer visible and the other one now the backbuffer.
; to avoid any screen tearing it is advised to call this during the vertical blank (pass true)
if wait_for_vsync
sys.waitvsync()
cx16.r0 = buffer_back
buffer_back = buffer_visible
buffer_visible = cx16.r0
cx16.VERA_CTRL = 0
cx16.r0 &= %1111110000000000
cx16.VERA_L1_TILEBASE = cx16.VERA_L1_TILEBASE & 1 | (cx16.r0H >>1 )
}
sub textmode() { sub textmode() {
; back to normal text mode ; back to normal text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
@@ -559,6 +590,7 @@ drawmode: ora cx16.r15L
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) { sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen. ; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
; Midpoint algorithm, filled ; Midpoint algorithm, filled
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
if radius==0 if radius==0
return return
ubyte @zp yy = 0 ubyte @zp yy = 0
@@ -597,6 +629,7 @@ drawmode: ora cx16.r15L
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) { sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
; Does bounds checking and clipping. ; Does bounds checking and clipping.
; Midpoint algorithm, filled ; Midpoint algorithm, filled
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
if radius==0 if radius==0
return return
ubyte @zp yy = 0 ubyte @zp yy = 0
@@ -696,7 +729,7 @@ invert:
adc p8v_times40_lsb,y adc p8v_times40_lsb,y
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_times40_msb,y lda p8v_times40_msb,y
adc #0 adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
@@ -708,7 +741,6 @@ invert:
; width=640 (hires) ; width=640 (hires)
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xx lda p8v_xx
and #7 and #7
pha ; xbits pha ; xbits
@@ -723,10 +755,15 @@ invert:
;xx /= 8 ;xx /= 8
xx += yy*(640/8) xx += yy*(640/8)
%asm {{ %asm {{
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xx+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
plx ; xbits plx ; xbits
lda p8v_maskbits,x lda p8v_maskbits,x
}} }}
@@ -768,11 +805,15 @@ invert:
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xx+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
ply ; xbits ply ; xbits
lda p8s_plot.p8v_maskbits,y lda p8s_plot.p8v_maskbits,y
and cx16.VERA_DATA0 and cx16.VERA_DATA0
@@ -855,8 +896,8 @@ skip:
} }
sub fill_scanline_right() { sub fill_scanline_right() {
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations ; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
cx16.r9s = xx cx16.r9s = xx
while xx <= width-1 { while xx <= width-1 {
if pgetset() if pgetset()
break break
@@ -891,11 +932,15 @@ skip:
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xpos+1
sta cx16.VERA_ADDR_M
lda p8v_xpos lda p8v_xpos
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xpos+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
ply ; xbits ply ; xbits
lda p8s_plot.p8v_maskbits,y lda p8s_plot.p8v_maskbits,y
and cx16.VERA_DATA0 and cx16.VERA_DATA0
@@ -942,12 +987,12 @@ _doplot beq +
ror a ror a
lsr a lsr a
lsr a lsr a
clc
ldy p8v_yy ldy p8v_yy
clc
adc p8v_times40_lsb,y adc p8v_times40_lsb,y
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_times40_msb,y lda p8v_times40_msb,y
adc #0 adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda #%00010000 ; autoincr lda #%00010000 ; autoincr
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
@@ -975,8 +1020,11 @@ _doplot beq +
lda cx16.r0L lda cx16.r0L
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda cx16.r0H lda cx16.r0H
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda #%00010000 ; autoincr lda #%00001000 ; autoincr (1 bit shifted)
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
}} }}
} }
@@ -1146,15 +1194,11 @@ cdraw_mod2 ora cx16.VERA_DATA1
cmp #0 cmp #0
beq + beq +
lda #255 lda #255
+ ldy #80 + ldy #40
- sta cx16.VERA_DATA0 -
sta cx16.VERA_DATA0 .rept 16
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
.endrept
dey dey
bne - bne -
rts rts

View File

@@ -816,6 +816,7 @@ Full-screen lores or hires monochrome bitmap graphics routines, available on the
- two resolutions: lores 320*240 or hires 640*480 bitmap mode - two resolutions: lores 320*240 or hires 640*480 bitmap mode
- optimized routines for monochrome (2-color) graphics - optimized routines for monochrome (2-color) graphics
- clearing screen, switching screen mode, also back to text mode - clearing screen, switching screen mode, also back to text mode
- doublebuffering option to avoid flicker
- drawing and reading individual pixels - drawing and reading individual pixels
- drawing lines, rectangles, filled rectangles, circles, discs - drawing lines, rectangles, filled rectangles, circles, discs
- flood fill - flood fill
@@ -823,7 +824,7 @@ Full-screen lores or hires monochrome bitmap graphics routines, available on the
- can draw using a stipple pattern (alternate black/white pixels) and in invert mode (toggle pixels) - can draw using a stipple pattern (alternate black/white pixels) and in invert mode (toggle pixels)
Read the `monogfx source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/monogfx.p8>`_ Read the `monogfx source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/monogfx.p8>`_
to see what's in there. and the `testmonogfx` example program, to see what's in there.
palette (cx16 only) palette (cx16 only)

View File

@@ -1,6 +1,9 @@
TODO TODO
==== ====
Bug: monogfx circle, plot and line drawing with INVERT mode is broken (garbage pixels)
STRUCTS: are being developed in their own separate branch for now, called "structs". STRUCTS: are being developed in their own separate branch for now, called "structs".
Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it. Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it.

View File

@@ -1,6 +1,6 @@
%import monogfx %import monogfx
%import textio
%import math %import math
%import conv
%option no_sysinit %option no_sysinit
%zeropage basicsafe %zeropage basicsafe
@@ -18,8 +18,7 @@ main {
sys.wait(2*60) sys.wait(2*60)
demo2() demo2()
monogfx.textmode() doublebuffer.demo()
txt.print("done!\n")
} }
sub demofill() { sub demofill() {
@@ -214,3 +213,40 @@ main {
} }
} }
doublebuffer {
sub demo() {
monogfx.lores()
monogfx.text_charset(1)
monogfx.enable_doublebuffer()
uword cx = 100
repeat {
monogfx.clear_screen(false)
monogfx.text(50, 10, true, iso:"Double Buffered")
monogfx.circle(160, 120, 100, true)
monogfx.disc(160, 120, 40, true)
monogfx.rect(40, 40, 240, 180, true)
monogfx.drawmode(monogfx.MODE_STIPPLE)
monogfx.fill(50, 50, true, 1)
monogfx.drawmode(monogfx.MODE_NORMAL)
monogfx.fill(250, 50, true, 1)
monogfx.fillrect( 10, 50, 20, 100, true)
monogfx.line(10, 10, 300, 200, true)
repeat 200 {
monogfx.plot($00e0 + math.randrange(64), 20 + math.randrange(20), true)
}
monogfx.circle(cx, 219, 20, true)
monogfx.text(cx+20, 225, true, conv.str_uw(cx))
cx++
monogfx.swap_buffers()
}
}
}

View File

@@ -1,58 +1,21 @@
%import textio %import math
%zeropage basicsafe %import monogfx
main { main {
sub start() { sub start() {
word w1, w2, w3, w4 monogfx.hires()
uword uw1, uw2, uw3 monogfx.fillrect(100, 100, 200, 100, true)
w1 = -111 sys.wait(60)
w2 = 222
w3 = -333
w4 = -20
uw1 = 111 monogfx.drawmode(monogfx.MODE_INVERT)
uw2 = 222 monogfx.circle(150, 120, 80, true) ; TODO INVERT is BROKEN
uw3 = 333 monogfx.line(10, 20, 250, 160, true) ; TODO INVERT is BROKEN
txt.print_w(w2*w3) repeat 500 {
txt.spc() monogfx.plot( math.rnd(), math.rnd(), true) ; TODO INVERT is BROKEN
w1 = w2 * w3 }
txt.print_w(w1)
txt.nl()
txt.print_w(w3*w4)
txt.nl()
txt.print_uw(uw2*uw3) repeat {}
txt.spc()
uw1 = uw2 * uw3
txt.print_uw(uw1)
txt.nl()
txt.nl()
w1 = -111
w2 = 22222
w3 = -333
w4 = -17
uw1 = 111
uw2 = 22222
uw3 = 333
txt.print_w(w2/w3)
txt.spc()
w1 = w2 / w3
txt.print_w(w1)
txt.nl()
txt.print_w(w3/w4)
txt.nl()
txt.print_uw(uw2/uw3)
txt.spc()
uw1 = uw2 / uw3
txt.print_uw(uw1)
txt.nl()
} }
} }