User Tools

Site Tools


base:rle_pack_unpack

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

base:rle_pack_unpack [2015-04-17 04:33] (current)
Line 1: Line 1:
 +====== RLE Toolkit for CC65 v 1.0 ======
 +By MagerValp.
 +
 +The homepage and sources to this Toolkit is available [[http://​www.paradroid.net/​rle/​|here]]. Check that page for potential updates to this code. A copy of the source package (of v1.0) is also available for download right here from {{:​base:​rle-toolkit-1.0.zip|codebase64}}. ​
 +
 +RLE Toolkit consists of:
 +
 +  * a small RLE compression library for CC65
 +  * commandline utilities to pack and unpack files
 +  * sample code that shows you how to use it
 +
 +====== Compression API ======
 +
 +
 +There are two functions, rle_pack and rle_unpack, that can be called from both C and assembler.
 +
 +C interface:
 +
 +<​code>​
 +  unsigned int __fastcall__ rle_pack(unsigned char *dest, unsigned char *src, unsigned int length);
 +
 +
 +  unsigned int __fastcall__ rle_unpack(unsigned char *dest, unsigned char *src);
 +
 +</​code>​
 +
 +Assembler interface:
 +
 +<​code>​
 +
 + .import rle_pack
 + .importzp src, dest
 + .import srclen, destlen
 +
 + ; set source pointer
 + lda #<​sourcedata
 + sta src
 + lda #>​sourcedata
 + sta src + 1
 +
 + ; set destination pointer
 + lda #<​destbuffer
 + sta dest
 + lda #>​destbuffer
 + sta dest + 1
 +
 + ; set length of source data when packing
 + ; parameter is ignored when unpacking
 + lda #<​datalen
 + sta srclen
 + lda #>​datalen
 + sta srclen + 1
 +
 + jsr rle_pack
 +
 +   or
 +
 + jsr rle_unpack
 +
 +
 + ; length of output is returned in destlen
 +
 +</​code>​
 +
 +
 +
 +====== Packed stream format ======
 +
 +
 +When two or more consecutive bytes are identical, they are replaced by
 +<​BYTE>​ <​BYTE>​ <​COUNT>​. A COUNT of 0 indicates End Of Stream. A COUNT
 +of 1 indicates that two bytes should be written, a COUNT of 2
 +indicates three bytes, and so on.
 +
 +
 +
 +====== Commandline tools ======
 +
 +<​code>​
 +  rlepack infile outfile
 +
 +  rleunpack infile outfile
 +</​code>​
 +
 +
 +Self explanatory. Right?
 +
 +
 +
 +====== The Routines ======
 +
 +===== rle.h =====
 +<​code>​
 +/* Pack data. Returns the number of bytes written to destination. */
 +unsigned int __fastcall__ rle_pack(unsigned char *dest, const unsigned char *src, unsigned int length);
 +
 +/* Unpack data. Returns the number of unpacked bytes. */
 +unsigned int __fastcall__ rle_unpack(unsigned char *dest, const unsigned char *src);
 +</​code>​
 +
 +===== rle.s =====
 +<​code>​
 +; Routines for packing and unpacking run length encoded byte streams
 +;
 +; When two or more consecutive bytes are identical, they are replaced by
 +; <​BYTE>​ <​BYTE>​ <​COUNT>​. A COUNT of 0 indicates End Of Stream. A COUNT
 +; of 1 indicates that two bytes should be written, a COUNT of 2 indicates
 +; three bytes, and so on.
 +
 +
 +  .export rle_read, rle_store
 +  .exportzp src, dest
 + .export lastbyte
 + .export destlen
 +
 +
 + .importzp ptr1, ptr2
 +
 +
 + .zeropage
 +
 +src = ptr1 ; borrow cc65's temp pointers
 +dest = ptr2
 +
 +
 + .bss
 +
 +lastbyte:​ .res 1 ; last byte read
 +destlen:​ .res 2 ; number of bytes written
 +
 +
 + .code
 +
 +
 +; read a byte and increment source pointer
 +rle_read:
 + lda (src),y
 + inc src
 + bne :+
 + inc src + 1
 +: rts
 +
 +
 +; write a byte and increment destination pointer
 +rle_store:
 + sta (dest),y
 + inc dest
 + bne :+
 + inc dest + 1
 +: inc destlen
 + bne :+
 + inc destlen + 1
 +: rts
 +</​code>​
 +
 +===== rlepack.s =====
 +<​code>​
 +
 +
 + .export _rle_pack, rle_pack
 +
 +
 + .import rle_store, rle_read
 + .importzp src, dest
 + .import lastbyte
 + .import destlen
 +
 + .import popax
 +
 +
 + .bss
 +
 +srclen:​ .res 2 ; length of source data
 +
 +
 + .code
 +
 +
 +; cc65 interface to rle_pack
 +; unsigned int __fastcall__ rle_pack(unsigned char *dest, unsigned char *src, unsigned int length);
 +_rle_pack:
 + sta srclen ; save length arg
 + stx srclen + 1
 + jsr popax ; get src arg
 + sta src
 + stx src + 1
 + jsr popax ; get dest arg
 + sta dest
 + stx dest + 1
 + jsr rle_pack ; execute
 + lda destlen ; return length
 + ldx destlen + 1
 + rts
 +
 +
 +; run length encode a stream
 +rle_pack:
 + ldy #0
 + sty destlen ; reset the byte counter
 + sty destlen + 1
 + jsr rle_read ; read the first byte
 + sta lastbyte ; save for reference
 + jsr rle_store ;​ store it
 + jsr @decsrclen ;​ decrease source count
 + beq @end ; if you're trying to pack a single byte, this the end
 +@pack:
 + jsr rle_read ; grab a byte
 + cmp lastbyte ; same as last byte?
 + beq @rle ; then count bytes and store run length
 + sta lastbyte ; save for reference
 + jsr rle_store ;​ store byte
 + jsr @decsrclen ;​ decrease source count
 + bne @pack ; next
 +@end:
 + lda lastbyte ; store last byte...
 + jsr rle_store
 + lda #0 ; ...with a 0 count as the terminator
 + jsr rle_store
 + rts ; done
 +@rle:
 + ldx #1 ; start with a count of 1
 + jsr @decsrclen
 + beq @rleend
 +@rlenext:
 + jsr rle_read ; grab a byte
 + cmp lastbyte ; make sure it's the same
 + bne @newbyte ; no, then terminate
 + inx ; inc counter
 + beq @stop ; overflow?
 + jsr @decsrclen ;​ check for end of data
 + bne @rlenext
 +@rleend:​ ;​ end of data
 + lda lastbyte ; store double byte
 + jsr rle_store
 + txa ; and counter
 + jsr rle_store
 + jmp @end
 +@stop:​  ​   ; overflow
 + lda lastbyte ; store the double byte
 + jsr rle_store
 + lda #$ff ; $ff as the byte count
 + jsr rle_store
 + inx ; start over with a counter of 1
 + jsr @decsrclen
 + beq @rleend
 + bne @rlenext
 +@newbyte:​ ;​ new byte detected
 + pha ; save
 + lda lastbyte ; store double byte
 + jsr rle_store
 + txa ; and counter
 + jsr rle_store
 + pla ; restore new byte
 + sta lastbyte ; save for reference
 + jsr rle_store ;​ store it
 + jsr @decsrclen ;​ data left?
 + bne @pack ; yep, pack
 + beq @end ; nope, end
 +; decrease number of bytes left, return 0 when done
 +@decsrclen:
 + lda srclen
 + bne :+
 + dec srclen + 1
 +: sec
 + sbc #1
 + sta srclen
 + ora srclen + 1
 + rts
 +</​code>​
 +===== rleunpack.s =====
 +<​code>​
 +
 +
 + .export _rle_unpack,​ rle_unpack
 +
 +
 + .import rle_store, rle_read
 + .importzp src, dest
 + .import lastbyte
 + .import destlen
 +
 + .import popax
 +
 +
 + .code
 +
 +
 +; cc65 interface to rle_unpack
 +; unsigned int __fastcall__ rle_unpack(unsigned char *dest, unsigned char *src);
 +_rle_unpack:​
 + sta src ; save src arg
 + stx src + 1
 + jsr popax ; get dest arg
 + sta dest
 + stx dest + 1
 + jsr rle_unpack ;​ execute
 + lda destlen ; return length
 + ldx destlen + 1
 + rts
 +
 +
 +; unpack a run length encoded stream
 +rle_unpack:
 + ldy #0
 + sty destlen ; reset byte counter
 + sty destlen + 1
 + jsr rle_read ; read the first byte
 + sta lastbyte ; save as last byte
 + jsr rle_store ;​ store
 +@unpack:
 + jsr rle_read ; read next byte
 + cmp lastbyte ; same as last one?
 + beq @rle ; yes, unpack
 + sta lastbyte ; save as last byte
 + jsr rle_store ;​ store
 + jmp @unpack ; next
 +@rle:
 + jsr rle_read ; read byte count
 + tax
 + beq @end ; 0 = end of stream
 + lda lastbyte
 +@read:
 + jsr rle_store ;​ store X bytes
 + dex
 + bne @read
 + beq @unpack ; next
 +@end:
 + rts
 +</​code>​
 +===== test.c =====
 +And finally, a test program (for the cc65 compiler).
 +
 +<​code>​
 +/*
 +
 +Pack data to a buffer, unpack and compare with original. If source and
 +destination are identical, everything works. Simple.
 +
 +*/
 +
 +#include <​stdio.h>​
 +#include <​string.h>​
 +#include "​rle.h"​
 +
 +
 +// number of bytes to pack
 +#define DATASIZE 1000
 +// source
 +#define SRC 0x0400
 +// destination
 +#define DEST 0x4400
 +// buffer, worst case should be DATASIZE * 1.5 + 2
 +#define BUFFER 0xc000
 +#define BUFSIZE 0x1000
 +
 +
 +void main(void) {
 +  unsigned int packlen, unpacklen, diffcount, i;
 +  unsigned char *src, *dest, *buffer;
 +
 +  src = (unsigned char *) SRC;
 +  dest = (unsigned char *) DEST;
 +  buffer = (unsigned char *) BUFFER;
 +
 +  // fill with 0x55 to see what gets written
 +  memset(buffer,​ 0x55, BUFSIZE);
 +  memset(dest,​ 0x55, DATASIZE);
 +
 +  // pack src to buffer
 +  packlen = rle_pack(buffer,​ src, DATASIZE);
 +  // unpack buffer to dest
 +  unpacklen = rle_unpack(dest,​ buffer);
 +
 +  // check differences
 +  diffcount = 0;
 +  for (i = 0; i < DATASIZE; ++i) {
 +    if (*src++ != *dest++) {
 +      ++diffcount;​
 +    }
 +  }
 +
 +  // print statistics
 +  printf("​Packed %d bytes to %d bytes\n",​ DATASIZE, packlen);
 +  printf("​Unpacked to %d bytes\n",​ unpacklen);
 +  printf("​%d bytes differ\n",​ diffcount);
 +}
 +</​code>​
 +===== Makefile =====
 +Example makefile just to show how this stuff can be built. In any case, don't forget that you can download the whole package at MagerValp'​s site - [[http://​www.paradroid.net/​rle/​]].
 +<​code>​
 +ARGET=c64
 +CC=cl65
 +AS=ca65
 +LD=cl65
 +AR=ar65
 +C1541=c1541
 +CFLAGS=-Oirs -t $(TARGET)
 +AFLAGS=
 +
 +
 +%.o: %.c
 + $(CC) -c $(CFLAGS) $<
 +
 +%.o: %.s
 + $(AS) $(AFLAGS) $<
 +
 +
 +all: rle.lib test.prg
 +
 +
 +RLEOBJS = \
 + rle.o \
 + rlepack.o \
 + rleunpack.o
 +
 +TESTOBJS= \
 + test.o \
 + rle.lib
 +
 +
 +rle.lib: $(RLEOBJS)
 + $(AR) a $@ $^
 +
 +
 +test.prg: $(TESTOBJS)
 + $(LD) -m test.map -Ln test.lab -t $(TARGET) -o test.prg $(AFLAGS) $^
 +
 +
 +.PHONY: clean distclean
 +
 +clean:
 + rm -f *.o rle.lib
 + rm -f test.prg test.map test.lab
 +
 +
 +distclean: clean
 + rm -f *~
 +
 +</​code>​
 +
 +
  
base/rle_pack_unpack.txt ยท Last modified: 2015-04-17 04:33 (external edit)