[comp.compression] LZRW1 derivative for 68000

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

    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