diff --git a/compiler/res/prog8lib/cx16/diskio.p8 b/compiler/res/prog8lib/cx16/diskio.p8 index cb115a932..44cad3d57 100644 --- a/compiler/res/prog8lib/cx16/diskio.p8 +++ b/compiler/res/prog8lib/cx16/diskio.p8 @@ -703,7 +703,7 @@ io_error: ; CommanderX16 extensions over the basic C64/C128 diskio routines: ; For use directly after a load or load_raw call (don't mess with the ram bank yet): - ; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits) + ; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits) sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword { return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress } diff --git a/compiler/res/prog8lib/cx16/gfx_lores.p8 b/compiler/res/prog8lib/cx16/gfx_lores.p8 index 4d82b2684..f4c867e2e 100644 --- a/compiler/res/prog8lib/cx16/gfx_lores.p8 +++ b/compiler/res/prog8lib/cx16/gfx_lores.p8 @@ -2,14 +2,18 @@ ; bitmap image needs to start at VRAM addres $00000. ; This is compatible with the CX16's screen mode 128. (void cx16.set_screen_mode(128)) + +%import syslib %import verafx - gfx_lores { - %option ignore_unused - sub set_screen_mode() { + const uword WIDTH = 320 + const ubyte HEIGHT = 240 + + sub graphics_mode() { + ; enable 320x240 256c bitmap graphics mode cx16.VERA_CTRL=0 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_HSCALE = 64 @@ -20,6 +24,13 @@ gfx_lores { clear_screen(0) } + sub text_mode() { + ; back to normal 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 + } + sub clear_screen(ubyte color) { if verafx.available() { ; use verafx cache writes to quicly clear the screen @@ -56,10 +67,10 @@ gfx_lores { cx16.VERA_CTRL=0 cx16.VERA_ADDR=0 cx16.VERA_ADDR_H = 1<<4 ; 1 pixel auto increment - repeat 240 { + repeat HEIGHT { %asm {{ lda p8v_color - ldy #320/8 + ldy #p8c_WIDTH/8 - .rept 8 sta cx16.VERA_DATA0 .endrept @@ -71,24 +82,145 @@ gfx_lores { cx16.VERA_ADDR_H = 0 } - asmsub plot(uword x @AX, ubyte y @Y, ubyte color @R0) { - ; x in r0, y in r1, color. + sub rect(uword xx, ubyte yy, uword rwidth, ubyte rheight, ubyte color) { + if rwidth==0 or rheight==0 + return + horizontal_line(xx, yy, rwidth, color) + if rheight==1 + return + horizontal_line(xx, yy+rheight-1, rwidth, color) + vertical_line(xx, yy+1, rheight-2, color) + if rwidth==1 + return + vertical_line(xx+rwidth-1, yy+1, rheight-2, color) + } + + sub safe_rect(uword xx, ubyte yy, uword rwidth, ubyte rheight, ubyte color) { + ; does bounds checking and clipping + safe_horizontal_line(xx, yy, rwidth, color) + if rheight==1 + return + uword bottomyy = yy as uword + rheight -1 + if bottomyy=WIDTH or yy>=HEIGHT + return + if msb(xx)&$80!=0 { + rwidth += xx + xx = 0 + } + if xx>=WIDTH + return + if xx+rwidth>WIDTH + rwidth = WIDTH-xx + if rwidth>WIDTH + return + + if yy as uword + rheight > HEIGHT + rheight = HEIGHT-yy + if rheight>HEIGHT + return + + repeat rheight { + horizontal_line(xx, yy, rwidth, color) + yy++ + } + } + + sub horizontal_line(uword xx, ubyte yy, uword length, ubyte color) { + if length==0 + return + plot(xx, yy, color) ; set starting position by reusing plot routine + ; set vera auto-increment to 1 pixel + cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (1<<4) + %asm {{ - clc - adc times320_lo,y - sta cx16.VERA_ADDR_L - txa - adc times320_mid,y - sta cx16.VERA_ADDR_M - lda #0 - adc times320_hi,y - sta cx16.VERA_ADDR_H - lda cx16.r0L - sta cx16.VERA_DATA0 - rts + lda p8v_color + ldx p8v_length+1 + beq + + ldy #0 +- sta cx16.VERA_DATA0 + iny + bne - + dex + bne - ++ ldy p8v_length ; remaining + beq + +- sta cx16.VERA_DATA0 + dey + bne - ++ }} } + sub safe_horizontal_line(uword xx, ubyte yy, uword length, ubyte color) { + ; does bounds checking and clipping + if yy>=HEIGHT + return + if msb(xx)&$80!=0 { + length += xx + xx = 0 + } + if xx>=WIDTH + return + if xx+length>WIDTH + length = WIDTH-xx + if length>WIDTH + return + + horizontal_line(xx, yy, length, color) + } + + sub vertical_line(uword xx, ubyte yy, ubyte lheight, ubyte color) { + if lheight==0 + return + plot(xx, yy, color) ; set starting position by reusing plot routine + ; set vera auto-increment to 320 pixel increment (=next line) + cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4) + %asm {{ + ldy p8v_lheight + lda p8v_color +- sta cx16.VERA_DATA0 + dey + bne - + }} + } + + sub safe_vertical_line(uword xx, ubyte yy, ubyte lheight, ubyte color) { + ; does bounds checking and clipping + if yy>=HEIGHT + return + if msb(xx)&$80!=0 or xx>=WIDTH + return + if yy as uword + lheight > HEIGHT + lheight = HEIGHT-yy + if lheight>HEIGHT + return + + vertical_line(xx, yy, lheight, color) + } + sub line(uword x1, ubyte y1, uword x2, ubyte y2, ubyte color) { ; Bresenham algorithm. ; This code special-cases various quadrant loops to allow simple ++ and -- operations. @@ -214,48 +346,491 @@ gfx_lores { } } - sub horizontal_line(uword xx, ubyte yy, uword length, ubyte color) { - if length==0 + sub circle(uword @zp xcenter, ubyte @zp ycenter, ubyte radius, ubyte color) { + ; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen. + ; Midpoint algorithm. + if radius==0 return - plot(xx, yy, color) ; set starting position by reusing plot routine - ; set vera auto-increment to 1 pixel - cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (1<<4) + 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 + plotq() + cx16.r14 = xcenter - xx + plotq() + cx16.r14 = xcenter + xx + cx16.r15 = ycenter - yy + plotq() + cx16.r14 = xcenter - xx + plotq() + cx16.r14 = xcenter + yy + cx16.r15 = ycenter + xx + plotq() + cx16.r14 = xcenter - yy + plotq() + cx16.r14 = xcenter + yy + cx16.r15 = ycenter - xx + plotq() + cx16.r14 = xcenter - yy + plotq() + + yy++ + if decisionOver2>=0 { + xx-- + decisionOver2 -= xx*$0002 + } + decisionOver2 += yy*$0002 + decisionOver2++ + } + + sub plotq() { + ; cx16.r14 = x, cx16.r15 = y, color=color. + plot(cx16.r14, cx16.r15L, color) + } + } + + sub safe_circle(uword @zp xcenter, uword @zp ycenter, ubyte radius, ubyte color) { + ; This version does bounds checks and clipping, but is a lot slower. + ; Midpoint algorithm. + if radius==0 + return + + 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 + plotq() + cx16.r14 = xcenter - xx + plotq() + cx16.r14 = xcenter + xx + cx16.r15 = ycenter - yy + plotq() + cx16.r14 = xcenter - xx + plotq() + cx16.r14 = xcenter + yy + cx16.r15 = ycenter + xx + plotq() + cx16.r14 = xcenter - yy + plotq() + cx16.r14 = xcenter + yy + cx16.r15 = ycenter - xx + plotq() + cx16.r14 = xcenter - yy + plotq() + + yy++ + if decisionOver2>=0 { + xx-- + decisionOver2 -= xx*$0002 + } + decisionOver2 += yy*$0002 + decisionOver2++ + } + + sub plotq() { + ; cx16.r14 = x, cx16.r15 = y, color=color. + if cx16.r15 < HEIGHT + safe_plot(cx16.r14, cx16.r15L, color) + } + } + + sub disc(uword @zp xcenter, ubyte @zp ycenter, ubyte @zp radius, ubyte color) { + ; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen. + ; Midpoint algorithm, filled + if radius==0 + return + ubyte @zp yy = 0 + word @zp decisionOver2 = (1 as word)-radius + ubyte last_y3 = ycenter+radius + ubyte last_y4 = ycenter-radius + ubyte new_y3, new_y4 + + while radius>=yy { + horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color) + horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color) + + new_y3 = ycenter+radius + if new_y3 != last_y3 { + horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color) + last_y3 = new_y3 + } + new_y4 = ycenter-radius + if new_y4 != last_y4 { + horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color) + last_y4 = new_y4 + } + + yy++ + if decisionOver2>=0 { + radius-- + decisionOver2 -= radius*$0002 + } + decisionOver2 += yy*$0002 + decisionOver2++ + } + + ; draw the final two spans + yy-- + horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color) + horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color) + } + + sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) { + ; This version does bounds checks and clipping, but is a lot slower. + ; Midpoint algorithm, filled + if radius==0 + return + ubyte @zp yy = 0 + word @zp decisionOver2 = (1 as word)-radius + uword last_y3 = ycenter+radius + uword last_y4 = ycenter-radius + uword new_y3, new_y4 + + while radius>=yy { + uword liney = ycenter+yy + if msb(liney)==0 + safe_horizontal_line(xcenter-radius, lsb(ycenter+yy), radius*$0002+1, color) + liney = ycenter-yy + if msb(liney)==0 + safe_horizontal_line(xcenter-radius, lsb(ycenter-yy), radius*$0002+1, color) + new_y3 = ycenter+radius + if new_y3 != last_y3 { + if msb(last_y3)==0 + safe_horizontal_line(xcenter-yy, lsb(last_y3), yy*$0002+1, color) + last_y3 = new_y3 + } + new_y4 = ycenter-radius + if new_y4 != last_y4 { + if msb(last_y4)==0 + safe_horizontal_line(xcenter-yy, lsb(last_y4), yy*$0002+1, color) + last_y4 = new_y4 + } + yy++ + if decisionOver2>=0 { + radius-- + decisionOver2 -= radius*$0002 + } + decisionOver2 += yy*$0002 + decisionOver2++ + } + ; draw the final two spans + yy-- + if msb(last_y3)==0 + safe_horizontal_line(xcenter-yy, lsb(last_y3), yy*$0002+1, color) + if msb(last_y4)==0 + safe_horizontal_line(xcenter-yy, lsb(last_y4), yy*$0002+1, color) + } + + asmsub plot(uword x @AX, ubyte y @Y, ubyte color @R0) { + ; x in r0, y in r1, color. %asm {{ - lda p8v_color - ldx p8v_length+1 + clc + adc times320_lo,y + sta cx16.VERA_ADDR_L + txa + adc times320_mid,y + sta cx16.VERA_ADDR_M + lda #0 + adc times320_hi,y + sta cx16.VERA_ADDR_H + lda cx16.r0L + sta cx16.VERA_DATA0 + rts + }} + } + + sub safe_plot(uword xx, ubyte yy, ubyte color) { + ; A plot that does bounds checks to see if the pixel is inside the screen. + if msb(xx)&$80!=0 + return + if xx >= WIDTH or yy >= HEIGHT + return + plot(xx, yy, color) + } + + asmsub pget(uword x @AX, ubyte y @Y) -> ubyte @A { + ; returns the color of the pixel + %asm {{ + jsr p8s_position + lda cx16.VERA_DATA0 + rts + }} + } + + sub fill(uword x, ubyte y, ubyte new_color) { + ; reuse a few virtual registers in ZP for variables + &ubyte fillm = &cx16.r7L + &ubyte seedm = &cx16.r8L + &ubyte cmask = &cx16.r8H + &ubyte vub = &cx16.r13L + &ubyte nvub = &cx16.r13H + ubyte[4] amask = [$c0,$30,$0c,$03] ; array of cmask bytes + + ; 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 = 100 + word @zp xx = x as word + word @zp yy = y as word + word[MAXDEPTH] @split @shared stack_xl + word[MAXDEPTH] @split @shared stack_xr + word[MAXDEPTH] @split @shared stack_y + 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 + lda p8v_sxl + sta p8v_stack_xl_lsb,y + lda p8v_sxl+1 + sta p8v_stack_xl_msb,y + lda p8v_sxr + sta p8v_stack_xr_lsb,y + lda p8v_sxr+1 + sta p8v_stack_xr_msb,y + lda p8v_sy + sta p8v_stack_y_lsb,y + lda p8v_sy+1 + sta p8v_stack_y_msb,y + ldy cx16.r12L + lda p8v_sdy + sta p8v_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 p8v_stack_xl_lsb,y + sta p8v_x1 + lda p8v_stack_xl_msb,y + sta p8v_x1+1 + lda p8v_stack_xr_lsb,y + sta p8v_x2 + lda p8v_stack_xr_msb,y + sta p8v_x2+1 + lda p8v_stack_y_lsb,y + sta p8v_yy + lda p8v_stack_y_msb,y + sta p8v_yy+1 + ldy cx16.r12L + lda p8v_stack_dy,y + sta p8v_dy + }} + yy+=dy + } + cx16.r11L = pget(xx as uword, lsb(yy)) ; old_color + if cx16.r11L == cx16.r10L + return + if xx<0 or xx>WIDTH-1 or yy<0 or yy>HEIGHT-1 + return + push_stack(xx, xx, yy, 1) + push_stack(xx, xx, yy + 1, -1) + word left = 0 + while cx16.r12L!=0 { + pop_stack() + xx = x1 + if fill_scanline_left_8bpp() goto skip + left = xx + 1 + if left < x1 + push_stack(left, x1 - 1, yy, -dy) + xx = x1 + 1 + + do { + fill_scanline_right_8bpp() + push_stack(left, xx - 1, yy, dy) + if xx > x2 + 1 + push_stack(x2 + 1, xx - 1, yy, -dy) +skip: + xx++ + while xx <= x2 { + if pget(xx as uword, lsb(yy)) == cx16.r11L + break + xx++ + } + left = xx + } until xx>x2 + } + + sub set_vera_address(bool decr) { + ; set both data0 and data1 addresses + position(xx as uword, lsb(yy)) + cx16.r0 = cx16.VERA_ADDR + cx16.r1L = cx16.VERA_ADDR_H & 1 | if decr %00011000 else %00010000 + cx16.VERA_ADDR_H = cx16.r1L + cx16.VERA_CTRL = 1 + cx16.VERA_ADDR = cx16.r0 + cx16.VERA_ADDR_H = cx16.r1L + cx16.VERA_CTRL = 0 + } + + sub fill_scanline_left_8bpp() -> bool { + set_vera_address(true) + cx16.r9s = xx + while xx >= 0 { + if cx16.VERA_DATA0 != cx16.r11L + break + cx16.VERA_DATA1 = cx16.r10L + xx-- + } + return xx==cx16.r9s + } + + sub fill_scanline_right_8bpp() { + set_vera_address(false) + while xx <= WIDTH-1 { + if cx16.VERA_DATA0 != cx16.r11L + break + cx16.VERA_DATA1 = cx16.r10L + xx++ + } + } + } + + sub text_charset(ubyte charset) { + ; -- select the text charset to use with the text() routine + ; 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 etc. etc. + cx16.screen_set_charset(charset, 0) + } + + const ubyte charset_bank = $1 + const uword charset_addr = $f000 ; in bank 1, so $1f000 + + sub text(uword @zp xx, uword yy, ubyte color, uword textptr) { + ; -- Write some text at the given pixel position. The text string must be in an encoding approprite for the charset. + ; You must also have called text_charset() first to select and prepare the character set to use. + uword chardataptr + ubyte[8] @shared char_bitmap_bytes_left + ubyte[8] @shared char_bitmap_bytes_right + + while @(textptr)!=0 { + chardataptr = charset_addr + (@(textptr) as uword)*8 + cx16.vaddr(charset_bank, chardataptr, 1, 1) + repeat 8 { + position(xx,lsb(yy)) + yy++ + %asm {{ + ldx p8v_color + lda cx16.VERA_DATA1 + sta P8ZP_SCRATCH_B1 + ldy #8 +- 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 + bne - + }} + } + xx+=8 + yy-=8 + textptr++ + } + } + + asmsub position(uword x @AX, ubyte y @Y) { + %asm {{ + clc + adc times320_lo,y + sta cx16.VERA_ADDR_L + txa + adc times320_mid,y + sta cx16.VERA_ADDR_M + lda #%00010000 ; auto increment on + adc times320_hi,y + sta cx16.VERA_ADDR_H + rts + }} + } + + inline asmsub next_pixel(ubyte color @A) { + ; -- sets the next pixel byte to the graphics chip. + ; for 8 bpp screens this will plot 1 pixel. + ; for 2 bpp screens it will plot 4 pixels at once (color = bit pattern). + %asm {{ + sta cx16.VERA_DATA0 + }} + } + + asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, X, Y) { + ; -- sets the next bunch of pixels from a prepared array of bytes. + ; for 8 bpp screens this will plot 1 pixel per byte. + ; 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 -- sta cx16.VERA_DATA0 +- lda (P8ZP_SCRATCH_W1),y + sta cx16.VERA_DATA0 iny bne - + inc P8ZP_SCRATCH_W1+1 ; next page of 256 pixels dex bne - -+ ldy p8v_length ; remaining + ++ ldx cx16.r0 ; remaining pixels beq + -- sta cx16.VERA_DATA0 - dey + ldy #0 +- lda (P8ZP_SCRATCH_W1),y + sta cx16.VERA_DATA0 + iny + dex bne - -+ ++ rts }} } - sub vertical_line(uword xx, ubyte yy, ubyte lheight, ubyte color) { - if lheight==0 - return - plot(xx, yy, color) ; set starting position by reusing plot routine - ; set vera auto-increment to 320 pixel increment (=next line) - cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4) + asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) clobbers(X) { + ; this is only useful in 256 color mode where one pixel equals one byte value. %asm {{ - ldy p8v_lheight - lda p8v_color -- sta cx16.VERA_DATA0 - dey + ldx #8 +- asl cx16.r0 + bcc + + sta cx16.VERA_DATA0 + bra ++ ++ sty cx16.VERA_DATA0 ++ dex bne - + rts }} } - %asm {{ ; multiplication by 320 lookup table times320 := 320*range(240) @@ -265,4 +840,3 @@ times320_mid .byte >times320 times320_hi .byte `times320 }} } - diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 1f426691d..6a520349e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,7 +1,8 @@ TODO ==== -Allow %merge to overwrite existing subs if the signature is identical? +Transform gfx2 into gfx_hires +Add a eor mode to gfx_lores / gfx_hires Improve register load order in subroutine call args assignments: in certain situations, the "wrong" order of evaluation of function call arguments is done which results diff --git a/examples/cx16/starszoom.p8 b/examples/cx16/starszoom.p8 index 08ca09c46..4f4f76fc1 100644 --- a/examples/cx16/starszoom.p8 +++ b/examples/cx16/starszoom.p8 @@ -25,7 +25,7 @@ main { uword[NUM_STARS] @split prev_x ubyte[NUM_STARS] prev_y - gfx_lores.set_screen_mode() + gfx_lores.graphics_mode() ; init the star positions ubyte star diff --git a/examples/test.p8 b/examples/test.p8 index 97d0c3e98..20c950c80 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,26 +1,38 @@ %import textio +%import gfx2 +%import gfx_lores %option no_sysinit %zeropage basicsafe main { sub start() { - txt.print("sdfdsf") - } -} + gfx_lores.graphics_mode() -txt { - ; merges this block into the txt block coming from the textio library - %option merge - - sub print(str text) { - repeat 4 chrout('@') - repeat { - cx16.r0L = @(text) - if_z break - chrout(cx16.r0L) - text++ + ; gfx2.screen_mode(1) + gfx_lores.text_charset(1) + for cx16.r9L in 0 to 10 { + gfx_lores.text(50+cx16.r9L, 20+cx16.r9L, cx16.r9L, sc:"the quick brown fox 12345") + } + gfx_lores.text_charset(2) + for cx16.r9L in 0 to 10 { + gfx_lores.text(50+cx16.r9L, 40+cx16.r9L, cx16.r9L, sc:"the quick brown fox 12345") + } + gfx_lores.text_charset(3) + for cx16.r9L in 0 to 10 { + gfx_lores.text(50+cx16.r9L, 60+cx16.r9L, cx16.r9L, sc:"the quick brown fox 12345") + } + gfx_lores.text_charset(4) + for cx16.r9L in 0 to 10 { + gfx_lores.text(50+cx16.r9L, 80+cx16.r9L, cx16.r9L, sc:"the quick brown fox 12345") + } + gfx_lores.text_charset(5) + for cx16.r9L in 0 to 10 { + gfx_lores.text(50+cx16.r9L, 100+cx16.r9L, cx16.r9L, sc:"the quick brown fox 12345") + } + + repeat { } - repeat 4 chrout('@') } } +