[comp.sys.amiga.programmer] Data compression

GHGAEA8@cc1.kuleuven.ac.be (05/02/91)

=========================================================================
Newsgroups: comp.compression
Subject: LZRW1 derivative for 68000

    This program should actually be listed in comp.compression (and
it is), but since the code described herein is 68000 specific and was
written on an Amiga it may be also at its place in .amiga.programmer,
so here it is :

=========================================================================

    This file was written on an Amiga and then copied onto VM, therefor
lines may ve longer than some mailers ro readers can handle, if so, I
will UUENCODE the file and post it again.  All bug reports are welcome
at the addresses given in the listing ... A C listing containing the
optimized LZRW1 compression will follow soon !

    I added some optimization to the program when it was already on VM,
so I haven't been able to test that yet.  It's the part between the
ELSE and ENDIF statements in FindMatch.  If you should find a bug, be
so kind as to report it ... Simply post it to Netnews and mail it to
GHGAEA8 @ BLEKUL11

**************************************************************************
**                                                                      **
**      ######    #     ####  ##  ##  ####   ####  ##   ## #####        **
**      ##  ##   ###   ##  ## ##  ## ##  ## ##  ## ### ### ##  ##       **
**      ##      ## ##  ##     ###### ##     ##  ## ####### ##  ##       **
**      ####   #######  ####   ####  ##     ##  ## ## # ## #####        **
**      ##     ##   ##     ##   ##   ##     ##  ## ##   ## ##           **
**      ##  ## ##   ## ##  ##   ##   ##  ## ##  ## ##   ## ##           **
**      ###### ##   ##  ####    ##    ####   ####  ##   ## ##           **
**                                                                      **
**          A fast and easy-to-understand compression algoritm          **
**                                                                      **
**                                                                      **
**      This compression routine is based on an algoritme written       **
**      by Ross N. Williams.   Some optimization was done by Kurt       **
**      Haenen  and the code  was rewritten for  68000 assembler.       **
**                                                                      **
**      If you should enhance or modify this code in any way, you       **
**      should put an article in Netnews  describing the  changes       **
**      you made and you should also include the source.  This is       **
**      not done to control the usage of the program (by the way,       **
**      this stuff is completely public domain),  but to  keep up       **
**      with the  possible optimizations  and enhancements  which       **
**      can be useful for others aswell !  I hope you'll like the       **
**      code.  I think this routine has one of the highest  speed       **
**      vs. compression ratios,  zoo sometimes compresses better,       **
**      but is much slower. There could still be some unefficient       **
**      code floating around here ... for you to find it and post       **
**      the fixed versions. Hope to hear from you all very soon.        **
**                                                                      **
**                                                Kurt HAENEN           **
**                                                                      **
**      Special thanks should go to the following persons:              **
**                                                                      **
**      - Ross N. Williams for the original algoritm published in       **
**        "An  extremely fast  data compression  algoritm",  Data       **
**        Compression Conference, Utah, 7-11 April, 1991.               **
**      - Sigrid Pinnoy for her constant support  and her efforts       **
**        to keep me away from my Amiga and study instead.              **
**      - Lieven van Uytfanghe  for his  constant  critics on  my       **
**        work and his supply of Bicky Burgers.                         **
**      - All the guys I know at the URC KU Leuven,  who allow me       **
**        to read netnews and get some EMAILs.                          **
**                                                                      **
**      If you should  like to contact  me,  you can  always  try       **
**      sending an  EMAIL  to  GHGAEA8 @ BLEKUL11,  however  this       **
**      account is  only temporarily at my disposal,  so if  it's       **
**      really important :                                              **
**      my "-M-essing -A-round -I-n my -L-etters" address is ...        **
**                                                                      **
**                              Kurt HAENEN                             **
**                              Homsemstraat 53                         **
**                              3891 Borlo                              **
**                              Belgium                                 **
**                                                                      **
**----------------------------------------------------------------------**
**      ANNOUNCEMENT UNDER THE NAME UTOPIA :                            **
**                                                                      **
**           ///        I AM STILL SEARCHING FOR SOMEONE WHO'S          **
**          ///         WILLING TO DONATE AN AMIGA 3000 TO ME.          **
**      \\\///          IF YOU'RE INTERESTED ...  DON'T WAIT !          **
**       \XX/           SEND ME AN EMAIL, MAIL, OR ...                  **
**                                                                      **
**************************************************************************

**************************************************************************
**                                                                      **
**      This code was designed to be compiled with the  MACRO 68        **
**      assembler on the Amiga.  It should be easy to convert to        **
**      other assemblers. Try removing the following lines ...          **
**                                                                      **
**************************************************************************

                        EXEOBJ
                        NEWSYNTAX
                        MC68000
                        SYM

**************************************************************************
**                                                                      **
**                      Some Constants we use                           **
**                                                                      **
**************************************************************************

Block_CopiedAll         EQU     %10000000
Block_EasyCompression   EQU     %01000000

EntryBitsPerHash        EQU     0
EntriesPerHash          EQU     (1<<EntryBitsPerHash)

**************************************************************************
**                                                                      **
**      ROUTINE:        Compress                                        **
**                                                                      **
**      INPUTS:         A0.l =  Pointer to source buffer                **
**                      A1.l =  Pointer to destination buffer           **
**                      D0.l =  Size of source buffer                   **
**      OUTPUTS:        D1.l =  Size of destination buffer              **
**                                                                      **
**      NOTES:          The destination buffer should be at least       **
**                      two bytes larger than the source buffer.        **
**                      The compressed data will at the most be         **
**                      one byte larger than the source data.           **
**                                                                      **
**************************************************************************

                        SECTION "Compression Code",CODE

Compress                movem.l d0/d2-d7/a0-a6,-(sp)
                        bsr     ClearHashTable
                        move.b  #Block_EasyCompression,(a1)

                        moveq   #3,d1                   ; Position in
destination buffer
                        moveq   #0,d2                   ; Position in source
buffer
                        moveq   #1,d3                   ; Position of current
command-word
                        moveq   #0,d4                   ; Value of current
command-word
                        move.w  #15,d5                  ; Number of bits to do
in command-word

COMP_NextLoop           bsr     FindMatch
                        cmp.l   #-1,d7
                        beq.s   COMP_NoCompression
                        move.b  d7,(1,a1,d1.l)
                        ror.w   #8,d7
                        move.b  d7,(a1,d1.l)
                        addq.l  #2,d1
                        ror.w   #8,d7
                        and.l   #$0000000f,d7
                        add.l   d7,d2
                        addq.l  #3,d2
                        lsl.w   #1,d4
                        or.w    #$0001,d4
                        bra.s   COMP_BitShifted

COMP_NoCompression      move.b  (a0,d2.l),(a1,d1.l)
                        addq.l  #1,d2
                        addq.l  #1,d1
                        lsl.w   #1,d4

COMP_BitShifted         dbra    d5,COMP_NotFull
                        move.b  d4,(1,a1,d3.l)
                        ror.w   #8,d4
                        move.b  d4,(a1,d3.l)
                        move.l  d1,d3
                        addq.l  #2,d1
                        moveq   #0,d4
                        move.w  #15,d5

COMP_NotFull            cmp.l   d0,d1
                        bhi.s   COMP_Overflow
                        cmp.l   d0,d2
                        blo.s   COMP_NextLoop
                        lsl.w   d5,d4
                        lsl.w   #1,d4
                        move.b  d4,(1,a1,d3.l)
                        ror.w   #8,d4
                        move.b  d4,(a1,d3.l)

COMP_Exit               movem.l (sp)+,d0/d2-d7/a0-a6
                        rts

COMP_Overflow           moveq   #0,d1
                        move.b  #Block_CopiedAll,(a1)
COMP_CopyLoop           move.b  (a0,d1.l),(1,a1,d1.l)
                        addq.l  #1,d1
                        cmp.l   d0,d1
                        blo.s   COMP_CopyLoop
                        addq.l  #1,d1
                        bra.s   COMP_Exit

**************************************************************************
**                                                                      **
**      ROUTINE:        Decompress                                      **
**                                                                      **
**      INPUTS:         A0.l =  Pointer to source buffer                **
**                      A1.l =  Pointer to destination buffer           **
**                      D0.l =  Size of source buffer                   **
**      OUTPUTS:        D1.l =  Size of destination buffer              **
**                                                                      **
**      NOTES:          The routine will not check overflows on the     **
**                      destination buffer, so you should make sure     **
**                      your destination buffer can hold the            **
**                      decompressed data !                             **
**                                                                      **
**************************************************************************

Decompress              movem.l d0/d2-d7/a0-a6,-(sp)

                        cmp.b   #Block_CopiedAll,(a0)
                        bne.s   DEC_NotACopy
                        moveq   #1,d1
DEC_CopyLoop            move.b  (a0,d1.l),(-1,a1,d1.l)
                        addq.l  #1,d1
                        cmp.l   d0,d1
                        blo.s   DEC_CopyLoop
                        subq.l  #1,d1
                        bra.s   DEC_Exit

DEC_NotACopy            moveq   #0,d1
                        cmp.b   #Block_EasyCompression,(a0)
                        bne.s   DEC_Exit

                        moveq   #0,d1                   ; Position in
destination buffer
                        moveq   #1,d2                   ; Position in source
buffer

DEC_NextCommand         move.b  (a0,d2.l),d3            ; Current command-word
                        lsl.w   #8,d3
                        move.b  (1,a0,d2.l),d3
                        addq.l  #2,d2
                        move.w  #15,d4

DEC_ProcessLoop         btst    #15,d3
                        bne.s   DEC_DecompressIt
                        move.b  (a0,d2.l),(a1,d1.l)
                        addq.l  #1,d2
                        addq.l  #1,d1
                        bra.s   DEC_NextByte

DEC_DecompressIt        move.b  (1,a0,d2.l),d5          ; Size of item
                        and.w   #$000f,d5
                        addq.w  #2,d5
                        move.b  (a0,d2.l),d6            ; Position of item
                        lsl.w   #8,d6
                        move.b  (1,a0,d2.l),d6
                        lsr.w   #4,d6
                        and.l   #$00000fff,d6
                        neg.l   d6
                        add.l   d1,d6
                        addq.l  #2,d2
DEC_RepeatLoop          move.b  (a1,d6.l),(a1,d1.l)
                        addq.l  #1,d6
                        addq.l  #1,d1
                        dbra    d5,DEC_RepeatLoop

DEC_NextByte            cmp.l   d0,d2
                        bhs.s   DEC_Exit
                        lsl.w   #1,d3
                        dbra    d4,DEC_ProcessLoop
                        bra.s   DEC_NextCommand

DEC_Exit                movem.l (sp)+,d0/d2-d7/a0-a6
                        rts

**************************************************************************
**                                                                      **
**      ROUTINE:        FindMatch                                       **
**                                                                      **
**      INPUTS:         A0.l =  Pointer to source buffer                **
**                      D0.l =  Size of source buffer                   **
**                      D2.l =  Position in source-buffer               **
**      OUTPUTS:        D7.l =  Command in lo-word or -1 if not found   **
**                                                                      **
**************************************************************************

FindMatch               movem.l d0-d1/d3-d6/a0-a6,-(sp)
                        lea     (HashTable),a1
                        move.b  (a0,d2.l),d1
                        lsl.w   #4,d1
                        move.b  (1,a0,d2.l),d3
                        eor.b   d3,d1
                        lsl.w   #4,d1
                        move.b  (2,a0,d2.l),d3
                        eor.b   d3,d1
                        mulu    #40543,d1
                        lsr.l   #4,d1
                        and.l   #$00000fff,d1
                        lsl.l   #(2+EntryBitsPerHash),d1
                        lea     (a1,d1.l),a1

                        IFGT EntryBitsPerHash

                        move.l  a1,a2
                        moveq   #-3,d7          ; D7 Lo-word = Length of largest
match
                                                ; D7 Hi-word = Relative position
                        move.w  #(EntriesPerHash-1),d6

FM_FindLongest          move.l  (a2)+,d1
                        bmi.s   FM_FoundLargest
                        move.l  d2,d4
                        sub.l   d1,d4
                        cmp.l   #4095,d4
                        bhi.s   FM_NotLarger

                        move.l  d2,d4
                        moveq   #-3,d5          ; Length of match

FM_MatchLoop            move.b  (a0,d1.l),d3
                        cmp.b   (a0,d4.l),d3
                        bne.s   FM_NotMatching
                        addq.w  #1,d5
                        addq.l  #1,d1
                        addq.l  #1,d4
                        cmp.l   d0,d4
                        bhs.s   FM_FoundLargest
                        cmp.w   #15,d5
                        bne.s   FM_MatchLoop

FM_NotMatching          cmp.w   d7,d5
                        ble.s   FM_NotLarger
                        move.w  d5,d7
                        move.l  d2,d5
                        sub.l   (-4,a2),d5
                        swap    d7
                        move.w  d5,d7
                        swap    d7
                        cmp.w   #15,d7
                        beq.s   FM_FoundLargest

FM_NotLarger            dbra    d6,FM_FindLongest
FM_FoundLargest         tst.w   d7
                        bmi.s   FM_NoMatch
                        ror.w   #4,d7
                        lsr.l   #8,d7
                        lsr.l   #4,d7

FM_AddToHash            move.w  #(EntriesPerHash*4-8),d1
FM_ShiftHash            move.l  (a1,d1.w),(4,a1,d1.w)
                        subq.w  #4,d1
                        bhs.s   FM_ShiftHash

                        ELSE

                        move.l  (a1),d1
                        bmi.s   FM_NoMatch
                        move.l	d2,d5
                        sub.l   d1,d5
                        cmp.l   #4095,d5
                        bhi.s   FM_NoMatch

                        move.l  d2,d4
                        moveq   #-3,d7

FM_MatchLoop            move.b  (a0,d1.l),d3
                        cmp.b   (a0,d4.l),d3
                        bne.s   FM_FoundLargest
                        addq.w  #1,d7
                        addq.l  #1,d1
                        addq.l  #1,d4
                        cmp.l   d0,d4
                        bhs.s   FM_FoundLargest
                        cmp.w   #15,d7
                        bne.s   FM_MatchLoop

FM_FoundLargest        	tst.w   d7
                        bmi.s   FM_NoMatch
                        swap    d7
                        move.w  d5,d7
                        ror.w   #4,d7
                        lsr.l   #8,d7
                        lsr.l   #4,d7

FM_AddToHash
                        ENDIF

                        move.l  d2,(a1)

FM_Exit                 movem.l (sp)+,d0-d1/d3-d6/a0-a6
                        rts

FM_NoMatch              moveq   #-1,d7
                        bra.s   FM_AddToHash

**************************************************************************
**                                                                      **
**      ROUTINE:        ClearHashTable                                  **
**                                                                      **
**      INPUTS:         None                                            **
**      OUTPUTS:        None                                            **
**                                                                      **
**************************************************************************

ClearHashTable          movem.l d0-d7/a0-a6,-(sp)
                        lea     (HashTable),a0
                        move.l  #(EntriesPerHash*4096-1),d0
CHT_Loop                move.l  #-1,(a0)+
                        subq.l  #1,d0
                        bhs.s   CHT_Loop
                        movem.l (sp)+,d0-d7/a0-a6
                        rts

**************************************************************************
**                                                                      **
**                         The Hash Table                               **
**                                                                      **
**      The  hash table  contains  4096 * EntriesPerHash  pointers      **
**      to sub-arrays containing specific characters.  This method      **
**      is used to speed up the match-searching,  without the loss      **
**      of too much compression.                                        **
**                                                                      **
**************************************************************************

                        SECTION "Hash Table",BSS

HashTable               ds.l    (4096*EntriesPerHash)

                        END