%target cx16 %import graphics %import textio %import diskio %option no_sysinit ; CommanderX16 Image file format. ; Numbers are encoded in little endian format (lsb first). ; ; offset value ; ----------------- ; HEADER (12 bytes): ; 0-1 'CI' in petscii , from "CommanderX16 Image". ; 2 Size of the header data following this byte (always 9, could become more if format changes) ; 3-4 Width in pixels (must be multiple of 8) ; 5-6 Height in pixels ; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors) ; this also determines the number of palette entries following later. ; 8 Settings bits. ; bit 0 and 1 = compression. 00 = uncompressed ; 01 = RLE [TODO not yet implemented] ; 10 = LZSA [TODO not yet implemented] ; 11 = Exomizer [TODO not yet implemented] ; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB) [TODO not yet implemented] ; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB) ; 4 bits per channel is what the Vera in the Cx16 supports. ; bit 3 = bitmap format. 0 = raw bitmap pixels ; 1 = tile-based image [TODO not yet implemented] ; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels ; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels ; bit 6,7: reserved, set to 0 ; 9-11 Size of the bitmap data following the palette data. ; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end). ; ; PALETTE (always present but size varies): ; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per ; entry is 2 or 3, depending on the chosen palette format in the setting bits. ; ; BITMAPDATA (size varies): ; After this, the actual image data follows. ; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence ; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8 ; If it is 'tiles', .... [TODO] ; If a compression scheme is used, the bitmap data here has to be decompressed first. ; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed? ; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that) ; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen) main { %option force_output ubyte[256] buffer ubyte[256] buffer2 ; add two more buffers to make enough space ubyte[256] buffer3 ; to store a 256 color palette ubyte[256] buffer4 ; .. and some more to be able to store 1280= ubyte[256] buffer5 ; two 640 bytes worth of bitmap scanline data sub start() { str[20] filename_ptrs ubyte num_files = diskio.list_files(8, ".ci", true, &filename_ptrs, len(filename_ptrs)) while num_files { num_files-- show_image(filename_ptrs[num_files]) } repeat { ; endless loop } } sub show_image(uword filename) { ubyte read_success = false uword bitmap_load_address = progend() ; uword max_bitmap_size = $9eff - bitmap_load_address txt.print(filename) txt.chrout('\n') if(diskio.f_open(8, filename)) { uword size = diskio.f_read(buffer, 12) ; read the header if size==12 { if buffer[0]=='c' and buffer[1]=='i' and buffer[2] == 9 { uword width = mkword(buffer[4], buffer[3]) uword height = mkword(buffer[6], buffer[5]) ubyte bpp = buffer[7] uword num_colors = 2 ** bpp ubyte flags = buffer[8] ubyte compression = flags & %00000011 ubyte palette_format = (flags & %00000100) >> 2 ubyte bitmap_format = (flags & %00001000) >> 3 ; ubyte hscale = (flags & %00010000) >> 4 ; ubyte vscale = (flags & %00100000) >> 5 uword bitmap_size = mkword(buffer[10], buffer[9]) uword palette_size = num_colors*2 if palette_format palette_size += num_colors ; 3 txt.print_uw(width) txt.chrout('*') txt.print_uw(height) txt.print(" * ") txt.print_uw(num_colors) txt.print(" colors\n") if width > graphics.WIDTH { txt.print("image is too wide for the display!\n") } else if compression!=0 { txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions } else if bitmap_format==1 { txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image } else { txt.print("loading...") size = diskio.f_read(buffer, palette_size) if size==palette_size { if compression { txt.print("todo: compressed image support\n") } else { ; uncompressed bitmap data. read it a scanline at a time and display as we go. ; restrict height to the maximun that can be displayed if height > graphics.HEIGHT height = graphics.HEIGHT graphics.enable_bitmap_mode() set_palette(num_colors, palette_format, buffer) cx16.r0 = 0 cx16.r1 = 0 cx16.FB_cursor_position() uword scanline_size = width * bpp / 8 ubyte y for y in 0 to lsb(height)-1 { void diskio.f_read(buffer, scanline_size) when bpp { 8 -> cx16.FB_set_pixels_from_buf(buffer, scanline_size) ; FB_set_pixels in rom v38 crashes with a size > 255 so we use our own replacement for now 4 -> display_scanline_16c(buffer, scanline_size) 2 -> display_scanline_4c(buffer, scanline_size) 1 -> display_scanline_2c(buffer, scanline_size) } } read_success = true } } } } } diskio.f_close() if not read_success txt.print("error!\n") else txt.print("ok\n") } } sub set_palette(uword num_colors, ubyte format, uword palletteptr) { uword vera_palette_ptr = $fa00 ubyte red ubyte greenblue if format { ; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel. repeat num_colors { red = @(palletteptr) >> 4 palletteptr++ greenblue = @(palletteptr) & %11110000 palletteptr++ greenblue |= @(palletteptr) >> 4 ; add Blue palletteptr++ cx16.vpoke(1, vera_palette_ptr, greenblue) vera_palette_ptr++ cx16.vpoke(1, vera_palette_ptr, red) vera_palette_ptr++ } } else { ; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped repeat num_colors { cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr)) palletteptr++ cx16.vpoke(1, vera_palette_ptr, @(palletteptr)) palletteptr++ vera_palette_ptr+=2 } } } sub display_scanline_16c(uword dataptr, uword numbytes) { ; TODO } sub display_scanline_4c(uword dataptr, uword numbytes) { ; TODO } sub display_scanline_2c(uword dataptr, uword numbytes) { ; TODO } }