diff --git a/compiler/res/prog8lib/cx16/monogfx.p8 b/compiler/res/prog8lib/cx16/monogfx.p8 index d4f4b21ac..c920895c6 100644 --- a/compiler/res/prog8lib/cx16/monogfx.p8 +++ b/compiler/res/prog8lib/cx16/monogfx.p8 @@ -22,18 +22,23 @@ monogfx { const ubyte MODE_STIPPLE = %00000001 const ubyte MODE_INVERT = %00000010 + uword buffer_visible, buffer_back + + sub lores() { ; enable 320*240 bitmap mode + buffer_visible = buffer_back = $0000 cx16.VERA_CTRL=0 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 + cx16.VERA_L1_TILEBASE = 0 ; lores width = 320 height = 240 lores_mode = true + buffer_visible = buffer_back = $0000 mode = MODE_NORMAL clear_screen(false) } @@ -46,14 +51,40 @@ monogfx { cx16.VERA_DC_VSCALE = 128 cx16.VERA_L1_CONFIG = %00000100 cx16.VERA_L1_MAPBASE = 0 - cx16.VERA_L1_TILEBASE = %00000001 + cx16.VERA_L1_TILEBASE = %00000001 ; hires width = 640 height = 480 lores_mode = false + buffer_visible = buffer_back = $0000 mode = MODE_NORMAL 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() { ; back to normal text 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) { ; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen. ; Midpoint algorithm, filled + ; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur. if radius==0 return 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) { ; Does bounds checking and clipping. ; Midpoint algorithm, filled + ; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur. if radius==0 return ubyte @zp yy = 0 @@ -696,7 +729,7 @@ invert: adc p8v_times40_lsb,y sta cx16.VERA_ADDR_L lda p8v_times40_msb,y - adc #0 + adc p8v_buffer_back+1 sta cx16.VERA_ADDR_M lda p8v_xx @@ -708,7 +741,6 @@ invert: ; width=640 (hires) %asm {{ stz cx16.VERA_CTRL - stz cx16.VERA_ADDR_H lda p8v_xx and #7 pha ; xbits @@ -723,10 +755,15 @@ invert: ;xx /= 8 xx += yy*(640/8) %asm {{ - lda p8v_xx+1 - sta cx16.VERA_ADDR_M lda p8v_xx 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 lda p8v_maskbits,x }} @@ -768,11 +805,15 @@ invert: %asm {{ stz cx16.VERA_CTRL - stz cx16.VERA_ADDR_H - lda p8v_xx+1 - sta cx16.VERA_ADDR_M lda p8v_xx 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 lda p8s_plot.p8v_maskbits,y and cx16.VERA_DATA0 @@ -855,8 +896,8 @@ skip: } sub fill_scanline_right() { - ; TODO maybe this could use vera auto increment, but that requires some clever masking calculations - cx16.r9s = xx + ; TODO maybe this could use vera auto increment, but that requires some clever masking calculations + cx16.r9s = xx while xx <= width-1 { if pgetset() break @@ -891,11 +932,15 @@ skip: %asm {{ stz cx16.VERA_CTRL - stz cx16.VERA_ADDR_H - lda p8v_xpos+1 - sta cx16.VERA_ADDR_M lda p8v_xpos 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 lda p8s_plot.p8v_maskbits,y and cx16.VERA_DATA0 @@ -942,12 +987,12 @@ _doplot beq + ror a lsr a lsr a - clc ldy p8v_yy + clc adc p8v_times40_lsb,y sta cx16.VERA_ADDR_L lda p8v_times40_msb,y - adc #0 + adc p8v_buffer_back+1 sta cx16.VERA_ADDR_M lda #%00010000 ; autoincr sta cx16.VERA_ADDR_H @@ -975,8 +1020,11 @@ _doplot beq + lda cx16.r0L sta cx16.VERA_ADDR_L lda cx16.r0H + clc + adc p8v_buffer_back+1 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 }} } @@ -1146,15 +1194,11 @@ cdraw_mod2 ora cx16.VERA_DATA1 cmp #0 beq + lda #255 -+ 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 ++ ldy #40 +- + .rept 16 sta cx16.VERA_DATA0 + .endrept dey bne - rts diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 1b15655c9..b01aae410 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -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 - optimized routines for monochrome (2-color) graphics - clearing screen, switching screen mode, also back to text mode +- doublebuffering option to avoid flicker - drawing and reading individual pixels - drawing lines, rectangles, filled rectangles, circles, discs - 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) Read the `monogfx source code `_ -to see what's in there. +and the `testmonogfx` example program, to see what's in there. palette (cx16 only) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 5da17e813..e014da5b9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,9 @@ 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". 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. diff --git a/examples/cx16/testmonogfx.p8 b/examples/cx16/testmonogfx.p8 index 47b5a5a22..575730cb9 100644 --- a/examples/cx16/testmonogfx.p8 +++ b/examples/cx16/testmonogfx.p8 @@ -1,6 +1,6 @@ %import monogfx -%import textio %import math +%import conv %option no_sysinit %zeropage basicsafe @@ -18,8 +18,7 @@ main { sys.wait(2*60) demo2() - monogfx.textmode() - txt.print("done!\n") + doublebuffer.demo() } 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() + } + } +} diff --git a/examples/test.p8 b/examples/test.p8 index 07b65101c..44894a79f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,58 +1,21 @@ -%import textio -%zeropage basicsafe - +%import math +%import monogfx main { sub start() { - word w1, w2, w3, w4 - uword uw1, uw2, uw3 + monogfx.hires() + monogfx.fillrect(100, 100, 200, 100, true) - w1 = -111 - w2 = 222 - w3 = -333 - w4 = -20 + sys.wait(60) - uw1 = 111 - uw2 = 222 - uw3 = 333 + monogfx.drawmode(monogfx.MODE_INVERT) + monogfx.circle(150, 120, 80, true) ; TODO INVERT is BROKEN + monogfx.line(10, 20, 250, 160, true) ; TODO INVERT is BROKEN - txt.print_w(w2*w3) - txt.spc() - w1 = w2 * w3 - txt.print_w(w1) - txt.nl() - txt.print_w(w3*w4) - txt.nl() + repeat 500 { + monogfx.plot( math.rnd(), math.rnd(), true) ; TODO INVERT is BROKEN + } - txt.print_uw(uw2*uw3) - 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() + repeat {} } }