; This is a 68000 assembly language version of the Zip function
; longest_match().  It is written for any 680x0 based computer, but at this
; time the feature for runtime testing of CPU type is only supported for the
; Amiga.  Hopefully a similar test for the Macintosh is possible, and for any
; other system that supports both 68000 and 68020+ CPUs.  This is written by
; Paul Kienitz, partially derived from a simpler version by Carsten Steger,
; derived in turn from a 386 assembly function by Jean-loup Gailly and Uwe
; Rommel... but also based directly on the C original.  No rights reserved.
;
; The main difference of this from other longest_match() implementations is
; that it includes both byte based and word based versions of the function,
; and various symbols can be defined to select whether to use one routine or
; the other, or to do a platform-specific test at runtime.  The symbols that
; can be used to select behavior are as follows:
;
;   CPU020     if defined, use 68020 instructions always
;   CPUTEST    if defined, check at runtime for CPU type.  Another symbol
;               specifying the platform-specific test must be used with this.
;               If neither of these is defined, use 68000 instructions only.
;   AMIGA      use Amiga-specific test for 68020, if CPUTEST defined.  Also
;               tells it to let a0/a1/d1 be clobbered by functions.
;   ATSIGN     define entry symbols in @foo form as well as _foo, with
;               @longest_match taking its parm in d0 instead of on the stack.
;   WSIZE      if defined, determines the sliding window size for deflate;
;               the default is 32K. If you have reduced WSIZE for the C code,
;               make sure that this module is assembled with an equivalent
;               "-dWSIZE=<whatever>" (or "-e...") switch.
;
; NOTE: no provision is made for 16 bit ints.  All external int variables are
; treated as 32 bit values.  This also assumes that longest_match's result is
; returned in D0.

        IFND    CPU020
        IFND    CPUTEST
CPU000  equ     1
        ENDC
        ENDC

; global variables:
        xref    _max_chain_length   ; unsigned int
        xref    _prev_length        ; unsigned int
        xref    _match_start        ; unsigned int
        xref    _strstart           ; unsigned int
        xref    _good_match         ; signed int
        xref    _nice_match         ; signed int
        xref    _window             ; array of unsigned char
        xref    _prev               ; array of unsigned short

; our entry points:
        xdef    _match_init         ; void match_init(void);
        xdef    _longest_match      ; int longest_match(unsigned cur_match);
        IFD     ATSIGN
        xdef    @match_init         ; for SAS assembler
        xdef    @longest_match      ; ditto
        ENDC

; flag variable for remembering CPU type:
        IFD     CPUTEST
        section cpuflag,data
is020:  ds.w    1
        ENDC


        section match,code
_match_init:
        IFD    ATSIGN
@match_init:
        ENDC

        IFD     CPUTEST         ; now check for platform type
        IFD     AMIGA           ; Amiga specific test for '020 CPU:

        xref    _SysBase

        NOLIST
        INCLUDE       'exec/execbase.i'
        LIST

        clr.w   is020           ; default value is 68000
        move.l  _SysBase,a0
        btst    #AFB_68020,AttnFlags+1(a0)
        beq.s   cheap
        move.w  #1,is020

cheap:
        ELSE ; !AMIGA

 !!   Write an '020-detector for your system here!

        ENDC ; AMIGA
        ENDC ; CPUTEST
        rts                ; match_init consists only of rts if CPUTEST unset


        IFD     AMIGA
SAVEREGS        reg     d3-d7/a2/a3/a5      ; don't protect d0/d1/a0/a1
        ELSE
SAVEREGS        reg     d1/d3-d7/a0-a3/a5   ; protect all but d0 return val
        ENDC

Cur_Match       equr    d0                  ; Must be in d0!
Best_Len        equr    d1
Scan_Start      equr    d3
Scan_End        equr    d4
Limit           equr    d5
Chain_Length    equr    d6
Scan_Test       equr    d7
Scan            equr    a0
Match           equr    a1
Prev_Address    equr    a2
Scan_Ini        equr    a3
Match_Ini       equr    a5


MAX_MATCH       equ     258
MIN_MATCH       equ     3
        IFND    WSIZE
WSIZE           equ     32768
        ENDC
MAX_DIST        equ     WSIZE-MAX_MATCH-MIN_MATCH-1

_longest_match:
        move.l  4(sp),Cur_Match         ; stack arg to register
        IFD     ATSIGN
@longest_match:
        ENDC
        movem.l SAVEREGS,-(sp)

; setup steps common to byte and word versions:
        move.l  _max_chain_length,Chain_Length
        move.l  _prev_length,Best_Len
        lea     _prev,Prev_Address
        lea     _window,Match_Ini
        move.l  _strstart,Limit
        move.l  Match_Ini,Scan_Ini
        addq    #MIN_MATCH,Match_Ini
        add.l   Limit,Scan_Ini
        subi.w  #MAX_DIST,Limit
        bhi.s   limit_ok
        moveq   #0,Limit
limit_ok:
        cmp.l   _good_match,Best_Len
        bcs.s   length_ok
        lsr.l   #2,Chain_Length
length_ok:
        subq.l  #1,Chain_Length

        IFD     CPUTEST
        tst.w   is020               ; can we use '020 stuff today?
        bne     WORD_match
        ENDC

        IFND    CPU020

; for 68000 or 68010, use byte operations:
        moveq   #0,Scan_Start       ; clear 2nd and 4th bytes, use 1st & 3rd
        moveq   #0,Scan_End
        moveq   #0,Scan_Test
        move.b  (Scan_Ini),Scan_Start
        swap    Scan_Start
        move.b  1(Scan_Ini),Scan_Start
        move.b  -1(Scan_Ini,Best_Len),Scan_End
        swap    Scan_End
        move.b  0(Scan_Ini,Best_Len),Scan_End
        bra.s   bdo_scan

blong_loop:
        move.b  -1(Scan_Ini,Best_Len),Scan_End
        swap    Scan_End
        move.b  0(Scan_Ini,Best_Len),Scan_End

bshort_loop:
        add.w   Cur_Match,Cur_Match
        move.w  0(Prev_Address,Cur_Match.l),Cur_Match
        cmp.l   Limit,Cur_Match
        dbls    Chain_Length,bdo_scan
        bra     return

bdo_scan:
        move.l  Match_Ini,Match
        add.l   Cur_Match,Match
        move.b  -MIN_MATCH-1(Match,Best_Len),Scan_Test
        swap    Scan_Test
        move.b  -MIN_MATCH(Match,Best_Len),Scan_Test
        cmp.l   Scan_Test,Scan_End
        bne.s   bshort_loop
        move.b  -MIN_MATCH(Match),Scan_Test
        swap    Scan_Test
        move.b  -MIN_MATCH+1(Match),Scan_Test
        cmp.l   Scan_Test,Scan_Start
        bne.s   bshort_loop
        move.w  #(MAX_MATCH-MIN_MATCH),Scan_Test
        lea     MIN_MATCH(Scan_Ini),Scan

bscan_loop:
        cmpm.b  (Match)+,(Scan)+
        dbne    Scan_Test,bscan_loop
        subq    #1,Scan

        sub.l   Scan_Ini,Scan
        cmp.l   Best_Len,Scan
        bls.s   bshort_loop
        move.l  Scan,Best_Len
        move.l  Cur_Match,_match_start
        cmp.l   _nice_match,Best_Len
        bcs.s   blong_loop
        IFD     CPUTEST
        bra     return
        ENDC

        ENDC ; !CPU020

        IFND    CPU000

; for 68020 or higher, use word operations even on odd addresses:
WORD_match:
        move.w  (Scan_Ini),Scan_Start
        move.w  -1(Scan_Ini,Best_Len),Scan_End
        bra.s   wdo_scan

wlong_loop:
        move.w  -1(Scan_Ini,Best_Len),Scan_End

wshort_loop:
        add.w   Cur_Match,Cur_Match
        move.w  (Prev_Address,Cur_Match.l),Cur_Match
        cmp.l   Limit,Cur_Match
        dbls    Chain_Length,wdo_scan
        bra.s   return

wdo_scan:
        move.l  Match_Ini,Match
        add.l   Cur_Match,Match
        cmp.w   -MIN_MATCH-1(Match,Best_Len),Scan_End
        bne.s   wshort_loop
        cmp.w   -MIN_MATCH(Match),Scan_Start
        bne.s   wshort_loop
        moveq   #((MAX_MATCH-MIN_MATCH)/2),Scan_Test    ; value = 127
        lea     MIN_MATCH(Scan_Ini),Scan

wscan_loop:
        cmpm.w  (Match)+,(Scan)+
        dbne    Scan_Test,wscan_loop
        subq    #2,Scan
        move.b  -MIN_MATCH+1(Match),Scan_Test
        cmp.b   (Scan),Scan_Test
        bne     steven
        addq    #1,Scan
steven:
        sub.l   Scan_Ini,Scan
        cmp.l   Best_Len,Scan
        bls.s   wshort_loop
        move.l  Scan,Best_Len
        move.l  Cur_Match,_match_start
        cmp.l   _nice_match,Best_Len
        bcs.s   wlong_loop

        ENDC ; !CPU000

return:
        move.l  Best_Len,d0             ; function return value
        movem.l (sp)+,SAVEREGS
        rts

        end
