From bc9683cc5471048830a721b255c67ad181a5d7f8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 26 Nov 2024 01:47:47 +0100 Subject: [PATCH] add compression.decode_rle_vram() to decompress RLE data directly to X16's VRAM. Document the compression library. --- compiler/res/prog8lib/buffers.p8 | 8 ++-- compiler/res/prog8lib/compression.p8 | 72 +++++++++++++++++++++++++--- docs/source/libraries.rst | 31 ++++++++++++ docs/source/todo.rst | 2 + scripts/cx16images.py | 5 +- 5 files changed, 106 insertions(+), 12 deletions(-) diff --git a/compiler/res/prog8lib/buffers.p8 b/compiler/res/prog8lib/buffers.p8 index 5021da23b..cdae93bb2 100644 --- a/compiler/res/prog8lib/buffers.p8 +++ b/compiler/res/prog8lib/buffers.p8 @@ -4,7 +4,7 @@ smallringbuffer { ; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 255 bytes maximum. - ; You can store and retrieve words too. + ; You can store and retrieve bytes and words too. ; It's optimized for speed and depends on the byte-wrap-around feature when doing incs and decs. ubyte fill @@ -70,8 +70,8 @@ smallringbuffer { ringbuffer { - ; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 8 KB maximum. - ; You can store and retrieve words too. + ; -- A ringbuffer (FIFO queue) that uses a block of 8 KB of memory. + ; You can store and retrieve bytes and words too. uword fill uword head @@ -148,5 +148,5 @@ ringbuffer { } -; TODO ringbuffer (FIFO queue) should use banked ram on the X16, but still work on virtual +; TODO ringbuffer (FIFO queue) should use banked ram on the X16, but still work on virtual target ; TODO stack (LIFO queue) using more than 1 page of ram (maybe even banked ram on the x16) diff --git a/compiler/res/prog8lib/compression.p8 b/compiler/res/prog8lib/compression.p8 index 351ed8e27..d1c9675b6 100644 --- a/compiler/res/prog8lib/compression.p8 +++ b/compiler/res/prog8lib/compression.p8 @@ -7,7 +7,7 @@ compression { sub encode_rle_outfunc(uword data, uword size, uword output_function, bool is_last_block) { ; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding. ; output_function = address of a routine that gets a byte arg in A, - ; this is the next RLE byte to write to the compressed output buffer or file. + ; which is the next RLE byte to write to the compressed output buffer or file. ; is_last_block = usually true, but you can set it to false if you want to concatenate multiple ; compressed blocks (for instance if the source data is >64Kb) ; Worst case result storage size needed = (size + (size+126) / 127) + 1 @@ -136,9 +136,9 @@ compression { } asmsub decode_rle_srcfunc(uword source_function @AY, uword target @R0, uword maxsize @R1) clobbers(X) -> uword @AY { - ; -- decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. - ; instead of a source buffer, you provide a callback function that must return the next byte to compress in A. - ; Stops decompressing when the maxsize has been reached. + ; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. + ; Also stops decompressing when the maxsize has been reached. Returns the size of the decompressed data. + ; Instead of a source buffer, you provide a callback function that must return the next byte to compress in A. %asm {{ sta _cb_mod1+1 sty _cb_mod1+2 @@ -234,8 +234,9 @@ _end } asmsub decode_rle(uword compressed @AY, uword target @R0, uword maxsize @R1) clobbers(X) -> uword @AY { - ; -- decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. - ; Stops decompressing when the maxsize has been reached. + ; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. + ; Also stops decompressing if the maxsize has been reached. + ; Returns the size of the decompressed data. %asm {{ sta P8ZP_SCRATCH_W1 ; compressed data ptr sty P8ZP_SCRATCH_W1+1 @@ -345,6 +346,65 @@ _end }} } + asmsub decode_rle_vram(uword compressed @R0, ubyte vbank @X, uword vaddr @AY) { + ; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data directly into Vera VRAM. + ; Control byte value 128 ends the decoding. This routine is for the Commander X16 only. + %asm {{ + stz cx16.VERA_CTRL + sta cx16.VERA_ADDR_L + sty cx16.VERA_ADDR_M + txa + ora #%00010000 ; autoincr by 1 + sta cx16.VERA_ADDR_H +_loop + lda (cx16.r0) + bpl _copy_literals + cmp #128 + bne + + rts ; DONE! + + ; replicate the next byte -n+1 times ++ + inc cx16.r0L + bne + + inc cx16.r0H ++ eor #255 + clc + adc #2 + tay + lda (cx16.r0) +- sta cx16.VERA_DATA0 + dey + bne - + inc cx16.r0L + bne _loop + inc cx16.r0H + bra _loop + +_copy_literals + ; copy the next n+1 bytes + inc cx16.r0L + bne + + inc cx16.r0H ++ pha + tax + inx + ldy #0 +- lda (cx16.r0),y + sta cx16.VERA_DATA0 + iny + dex + bne - + ; increase pointer by n+1 bytes + pla + sec + adc cx16.r0L + sta cx16.r0L + bcc _loop + inc cx16.r0H + bra _loop + }} + } /*** ; prog8 source code for the asm routine above: diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index be5e0fb02..9412544a0 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -928,6 +928,37 @@ the `bmx source code uword`` + Compress the given data block using ByteRun1 aka PackBits RLE encoding. + Returns the size of the compressed RLE data. Worst case result storage size needed = (size + (size+126) / 127) + 1. + 'is_last_block' = usually true, but you can set it to false if you want to concatenate multiple + compressed blocks (for instance if the source data is >64Kb) + +``encode_rle_outfunc (uword data, uword size, uword output_function, bool is_last_block)`` + Like ``encode_rle`` but not with an output buffer, but with an 'output_function' argument. + This is the address of a routine that gets a byte arg in A, + which is the next RLE byte to write to the compressed output buffer or file. + +``decode_rle (uword compressed, uword target, uword maxsize) -> uword`` + Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. + Also stops decompressing if the maxsize has been reached. Returns the size of the decompressed data. + +``decode_rle_srcfunc (uword source_function, uword target, uword maxsize) -> uword`` + Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding. + Also stops decompressing when the maxsize has been reached. Returns the size of the decompressed data. + Instead of a source buffer, you provide a callback function that must return the next byte to compress in A. + +``decode_rle_vram (uword compressed, ubyte vbank, uword vaddr)`` (cx16 only) + Decodes "ByteRun1" (aka PackBits) RLE compressed data directly into Vera VRAM. + Control byte value 128 ends the decoding. + + + emudbg (cx16 only) ------------------- X16Emu Emulator debug routines, for Cx16 only. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 751033848..ac167d440 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,8 @@ TODO ==== +work a bit more on the buffers library + document the @R0 - @R15 register support for normal subroutine parameters (footgun!) make a compiler switch to disable footgun warnings diff --git a/scripts/cx16images.py b/scripts/cx16images.py index 96f4477aa..a6c1cfded 100644 --- a/scripts/cx16images.py +++ b/scripts/cx16images.py @@ -82,7 +82,8 @@ class BitmapImage: def show(self) -> None: """Shows the image on the screen""" if self.img.mode == "P": - self.img.convert("RGB").convert("P").show() + self.img.show() + # self.img.convert("RGB").convert("P").show() else: self.img.show() @@ -198,7 +199,7 @@ class BitmapImage: elif bits_per_pixel == 4: num_colors = 15 if fixed_color_zero else 16 if num_colors==16 and preserve_first_16_colors: - return self.quantize_to(default_colors[:16]) + return self.quantize_to(default_colors[:16], 0) elif bits_per_pixel == 2: assert preserve_first_16_colors==False, "bpp is too small for 16 default colors" num_colors = 3 if fixed_color_zero else 4