; **experimental** data compression/decompression routines, API subject to change!! 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. ; 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 ; This routine is not optimized for speed but for readability and ease of use. uword idx = 0 uword literals_start_idx = 0 ubyte literals_length = 0 asmsub call_output_function(ubyte arg @A) { %asm {{ jmp (p8v_output_function) }} } sub next_same_span() { ; returns length in cx16.r1L, and the byte value in cx16.r1H cx16.r1H = data[idx] cx16.r1L = 0 while data[idx]==cx16.r1H and cx16.r1L<128 and idx1 { ; a replicate run if literals_length>0 output_literals() call_output_function((cx16.r1L^255)+2) ; 257-cx16.r1L call_output_function(cx16.r1H) } else { ; add more to the literals run if literals_length==128 output_literals() if literals_length==0 literals_start_idx = idx-1 literals_length++ } } if literals_length>0 output_literals() if is_last_block call_output_function(128) } sub encode_rle(uword data, uword size, uword target, bool is_last_block) -> 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) ; This routine is not optimized for speed but for readability and ease of use. uword idx = 0 uword literals_start_idx = 0 ubyte literals_length = 0 uword orig_target = target sub next_same_span() { ; returns length in cx16.r1L, and the byte value in cx16.r1H cx16.r1H = data[idx] cx16.r1L = 0 while data[idx]==cx16.r1H and cx16.r1L<128 and idx1 { ; a replicate run if literals_length>0 output_literals() @(target) = (cx16.r1L^255)+2 ; 257-cx16.r1L target++ @(target) = cx16.r1H target++ } else { ; add more to the literals run if literals_length==128 output_literals() if literals_length==0 literals_start_idx = idx-1 literals_length++ } } if literals_length>0 output_literals() if is_last_block { @(target) = 128 target ++ } return target-orig_target } 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. %asm {{ sta _cb_mod1+1 sty _cb_mod1+2 sta _cb_mod2+1 sty _cb_mod2+2 sta _cb_mod3+1 sty _cb_mod3+2 lda cx16.r0L ldy cx16.r0H sta P8ZP_SCRATCH_W2 ; target ptr sta _orig_target sty P8ZP_SCRATCH_W2+1 sty _orig_target+1 lda cx16.r0L clc adc cx16.r1L sta cx16.r1L lda cx16.r0H adc cx16.r1H sta cx16.r1H ; decompression limit _loop ; while target (W2) < limit (r1) lda P8ZP_SCRATCH_W2 ldy P8ZP_SCRATCH_W2+1 cmp cx16.r1L tya sbc cx16.r1H bcs _end _cb_mod1 jsr $ffff ; modified bpl _literals cmp #128 beq _end ; replicate next byte -n+1 times eor #255 clc adc #2 sta P8ZP_SCRATCH_REG _cb_mod2 jsr $ffff ; modified ldx P8ZP_SCRATCH_REG ldy #0 - sta (P8ZP_SCRATCH_W2),y iny dex bne - ; add A to target lda P8ZP_SCRATCH_REG clc adc P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2 lda #0 adc P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1 jmp _loop _literals ; copy the next n+1 bytes pha sta P8ZP_SCRATCH_B1 ldy #0 sty P8ZP_SCRATCH_REG _cb_mod3 jsr $ffff ; modified ldy P8ZP_SCRATCH_REG sta (P8ZP_SCRATCH_W2),y inc P8ZP_SCRATCH_REG dec P8ZP_SCRATCH_B1 bpl _cb_mod3 ; add N+1 to target pla sec adc P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2 lda #0 adc P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1 jmp _loop _orig_target .word 0 _end ; return w2-orig_target, the size of the decompressed data lda P8ZP_SCRATCH_W2 ldy P8ZP_SCRATCH_W2+1 sec sbc _orig_target tax tya sbc _orig_target+1 tay txa rts }} } 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. %asm {{ sta P8ZP_SCRATCH_W1 ; compressed data ptr sty P8ZP_SCRATCH_W1+1 lda cx16.r0L ldy cx16.r0H sta P8ZP_SCRATCH_W2 ; target ptr sta _orig_target sty P8ZP_SCRATCH_W2+1 sty _orig_target+1 lda cx16.r0L clc adc cx16.r1L sta cx16.r1L lda cx16.r0H adc cx16.r1H sta cx16.r1H ; decompression limit _loop ; while target (W2) < limit (r1) lda P8ZP_SCRATCH_W2 ldy P8ZP_SCRATCH_W2+1 cmp cx16.r1L tya sbc cx16.r1H bcs _end ldy #0 lda (P8ZP_SCRATCH_W1),y bpl _literals cmp #128 beq _end ; replicate next byte -n+1 times eor #255 clc adc #2 pha tax iny lda (P8ZP_SCRATCH_W1),y ldy #0 - sta (P8ZP_SCRATCH_W2),y iny dex bne - ; add A to target pla clc adc P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2 lda #0 adc P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1 ; increase source by 2 clc lda P8ZP_SCRATCH_W1 adc #2 sta P8ZP_SCRATCH_W1 lda #0 adc P8ZP_SCRATCH_W1+1 sta P8ZP_SCRATCH_W1+1 jmp _loop _literals ; copy the next n+1 bytes pha tax inc P8ZP_SCRATCH_W1 bne + inc P8ZP_SCRATCH_W1+1 + ldy #0 - lda (P8ZP_SCRATCH_W1),y sta (P8ZP_SCRATCH_W2),y iny dex bpl - ; add N+1 to source pla tax sec adc P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1 lda #0 adc P8ZP_SCRATCH_W1+1 sta P8ZP_SCRATCH_W1+1 ; add N+1 to target as well txa sec adc P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2 lda #0 adc P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1 jmp _loop _orig_target .word 0 _end ; return w2-orig_target, the size of the decompressed data lda P8ZP_SCRATCH_W2 ldy P8ZP_SCRATCH_W2+1 sec sbc _orig_target tax tya sbc _orig_target+1 tay txa rts }} } /*** ; prog8 source code for the asm routine above: sub decode_rle_prog8(uword @zp compressed, uword @zp target, uword maxsize) -> uword { cx16.r0 = target ; original target cx16.r1 = target+maxsize ; decompression limit while target uword { cx16.r0 = target ; original target cx16.r1 = target+maxsize ; decompression limit while target