123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053 |
- ;===========================================================================
- ; Copyright (c) 1990-1999 Info-ZIP. All rights reserved.
- ;
- ; See the accompanying file LICENSE, version 1999-Oct-05 or later
- ; (the contents of which are also included in zip.h) for terms of use.
- ; If, for some reason, both of these files are missing, the Info-ZIP license
- ; also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html
- ;===========================================================================
- ; This is a 680x0 assembly language translation of the Info-ZIP source file
- ; deflate.c, by Paul Kienitz. The function longest_match is based in part
- ; on match.a by Carsten Steger, which in turn is partly based on match.s
- ; for 386 by Jean-loup Gailly and Kai Uwe Rommel. Mostly, however, this
- ; material is based on deflate.c, by Gailly, Rommel, and Igor Mandrichenko.
- ; This code is not commented very much; see deflate.c for comments that explain
- ; what the functions are doing.
- ;
- ; The symbols that can be used to select different versions 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.
- ; Runtime test is nonportable; it is different for each OS.
- ;
- ; AMIGA use Amiga-specific test for 68020, if CPUTEST defined. Also
- ; tells it that registers d0/a0/d1/a1 are not preserved by
- ; function calls. At present, if AMIGA is not defined, it
- ; causes functions to preserve all registers. ALL OF THIS CODE
- ; CURRENTLY ASSUMES THAT REGISTERS D2-D7/A2-A6 WILL BE PRESERVED
- ; BY ANY FUNCTIONS THAT IT CALLS.
- ;
- ; DYN_ALLOC should be defined here if it is defined for C source; tells us
- ; that big arrays are allocated instead of static.
- ;
- ; WSIZE must be defined as the same number used for WSIZE in the C
- ; source, and must be a power of two <= 32768. As elsewhere,
- ; the default value is 32768.
- ;
- ; INT16 define this if ints are 16 bits; otherwise 32 bit ints assumed.
- ;
- ; SMALL_MEM define this if it is defined in the C source; otherwise it uses
- ; the MEDIUM_MEM model. BIG_MEM and MMAP are *not* supported.
- ; The FULL_SEARCH option in deflate.c is also not supported.
- ;
- ; DEBUG activates some tracing output, as in the C source.
- ;
- ; QUADLONG this selects a different version of the innermost longest_match
- ; loop code for 68020 operations, comparing bytes four at a time
- ; instead of two at a time. It seems to be a tiny bit faster on
- ; average, but it's slower often enough that one can't generalize.
- ;
- ; This code currently assumes that function results are returned in D0 for
- ; all platforms. It assumes that args to functions are pushed onto the stack,
- ; last arg first. It also currently assumes that all C symbols have an
- ; underscore prepended when referenced from assembly.
- IFND CPU020
- IFND CPUTEST
- CPU000 equ 1
- ENDC
- ENDC
- ; Use these macros for accessing variables of type int:
- IFD INT16
- MOVINT MACRO
- move.w \1,\2
- ENDM
- CLRINT MACRO
- clr.w \1
- ENDM
- INTSIZE equ 2
- ELSE ; !INT16
- MOVINT MACRO
- move.l \1,\2
- ENDM
- CLRINT MACRO
- clr.l \1
- ENDM
- INTSIZE equ 4
- ENDC
- IFD DYN_ALLOC
- BASEPTR MACRO
- move.l \1,\2
- ENDM
- ELSE
- BASEPTR MACRO
- lea \1,\2
- ENDM
- ENDC
- ; constants we use, many of them adjustable:
- MAX_MATCH equ 258
- MIN_MATCH equ 3
- TOO_FAR equ 4096
- IFND WSIZE
- WSIZE equ 32768
- ENDC
- WMASK equ WSIZE-1
- MAX_DIST equ WSIZE-MAX_MATCH-MIN_MATCH-1
- MIN_LOOKAHEAD equ MAX_MATCH+MIN_MATCH+1
- ; IFD BIG_MEM ; NOT supported -- type Pos needs to be 32 bits
- ;HASH_BITS equ 15
- ; ELSE
- IFD SMALL_MEM
- HASH_BITS equ 13
- ELSE
- HASH_BITS equ 14 ; default -- MEDIUM_MEM
- ENDC
- ; ENDC ; BIG_MEM
- HASH_SIZE equ 1<<HASH_BITS
- HASH_MASK equ HASH_SIZE-1
- H_SHIFT equ (HASH_BITS+MIN_MATCH-1)/MIN_MATCH
- B_SLOW equ 1
- B_FAST equ 2
- ZE_MEM equ 4
- EOF equ -1
- ; struct config is defined by these offsets:
- Good_length equ 0
- Max_lazy equ 2
- Nice_length equ 4
- Max_chain equ 6
- Sizeof_config equ 8
- ; external functions we call:
- xref _ct_tally ; int ct_tally(int, int)
- xref _flush_block ; unsigned long F(char *, unsigned long, int)
- xref _ziperr ; void ziperr(int, char *)
- xref _error ; void error(char *)
- xref _calloc ; stdlib function: void *calloc(size_t, size_t)
- xref _free ; stdlib function: void free(void *)
- IFD DEBUG
- xref _fputc ; stdio function: int fputc(int, FILE *)
- xref _stderr ; pointer to FILE, which we pass to fputc
- ENDC
- ; our entry points:
- xdef _lm_init ; void lm_init(int level, unsigned short *flags)
- xdef _lm_free ; void lm_free(void)
- xdef _deflate ; void deflate(void) ...the big one
- xdef _fill_window ; this line is just for debugging
- ; ============================================================================
- ; Here is where we have our global variables.
- section deflatevars,data
- ; external global variables we reference:
- xref _verbose ; signed int
- xref _level ; signed int
- xref _read_buf ; int (*read_buf)(char *, unsigned int)
- ; global variables we make available:
- xdef _window
- xdef _prev
- xdef _head
- xdef _window_size
- xdef _block_start
- xdef _strstart
- IFD DYN_ALLOC
- _prev: ds.l 1 ; pointer to calloc()'d unsigned short array
- _head: ds.l 1 ; pointer to calloc()'d unsigned short array
- _window: ds.l 1 ; pointer to calloc()'d unsigned char array
- ELSE ; !DYN_ALLOC
- _prev: ds.w WSIZE ; array of unsigned short
- _head: ds.w HASH_SIZE ; array of unsigned short
- _window: ds.b 2*WSIZE ; array of unsigned char
- ENDC ; ?DYN_ALLOC
- _window_size: ds.l 1 ; unsigned long
- _block_start: ds.l 1 ; unsigned long
- _strstart: ds.w INTSIZE/2 ; unsigned int
- ; Now here are our private variables:
- IFD CPUTEST
- is020: ds.w 1 ; bool: CPU type is '020 or higher
- ENDC
- ins_h: ds.w 1 ; unsigned short
- sliding: ds.w 1 ; bool: the file is read a piece at a time
- eofile: ds.w 1 ; bool: we have read in the end of the file
- max_lazy_match: ds.w 1 ; unsigned short
- lookahead: ds.w 1 ; unsigned short
- ; These are NOT DECLARED AS STATIC in deflate.c, but currently could be:
- max_chain_len: ds.w 1 ; unsigned short (unsigned int in deflate.c)
- prev_length: ds.w 1 ; unsigned short (unsigned int in deflate.c)
- good_match: ds.w 1 ; unsigned short (unsigned int in deflate.c)
- nice_match: ds.w 1 ; unsigned short (signed int in deflate.c)
- match_start: ds.w 1 ; unsigned short (unsigned int in deflate.c)
- ; This array of struct config is a constant and could be in the code section:
- config_table: dc.w 0,0,0,0 ; level 0: store uncompressed
- dc.w 4,4,8,4 ; level 1: fastest, loosest compression
- dc.w 4,5,16,8 ; level 2
- dc.w 4,6,32,32 ; level 3: highest to use deflate_fast
- dc.w 4,4,16,16 ; level 4: lowest to use lazy matches
- dc.w 8,16,32,32 ; level 5
- dc.w 8,16,128,128 ; level 6: the default level
- dc.w 8,32,128,256 ; level 7
- dc.w 32,128,258,1024 ; level 8
- dc.w 32,258,258,4096 ; level 9: maximum compression, slow
- ;;CAL_SH MACRO ; macro for calling zcalloc()
- ;; IFD INT16
- ;; move.w #2,-(sp)
- ;; move.w #\1,-(sp)
- ;; jsr _zcalloc
- ;; addq #4,sp
- ;; ELSE
- ;; pea 2
- ;; pea \1
- ;; jsr _zcalloc
- ;; addq #8,sp
- ;; ENDC
- ;; ENDM
- CAL_SH MACRO ; Okay, we're back to using regular calloc()...
- pea 2
- pea \1
- jsr _calloc
- addq #8,sp
- ENDM
- ; ============================================================================
- ; And here we begin our functions. match_init is for internal use only:
- section deflate,code
- match_init:
- 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
- FAIL Write an '020-detector for your system here!
- ; On the Macintosh, I believe GetEnvironment() provides the information.
- ENDC ; AMIGA
- ENDC ; CPUTEST
- rts ; match_init consists only of rts if CPUTEST unset
- ; ============================================================================
- ; Here is longest_match(), the function that the rest of this was built up
- ; from, the hottest hot spot in the program and therefore the most heavily
- ; optimized. It has two different versions, one for '020 and higher CPUs, and
- ; one for 68000/68010. It can test at runtime which version to use if you
- ; create a test function in match_init for your platform. Currently such a
- ; test is implemented for the Amiga. It can also be assembled to use '000 or
- ; '020 code only.
- Cur_Match equr d0 ; unsigned int, kept valid as long
- Best_Len equr d1 ; unsigned int, kept valid as long
- Scan_Start equr d3 ; pair of bytes
- Scan_End equr d4 ; pair of bytes
- Limit equr d5 ; unsigned int
- Chain_Length equr d6 ; unsigned int
- Scan_Test equr d7 ; counter, pair of bytes sometimes
- Scan equr a0 ; pointer to unsigned char
- Match equr a1 ; pointer to unsigned char
- Prev_Address equr a2 ; pointer to unsigned short
- Scan_Ini equr a3 ; pointer to unsigned char
- Match_Ini equr a5 ; pointer to unsigned char
- ; Note: "pair of bytes" means the two low order bytes of the register in
- ; 68020 code, but means the lowest and third lowest bytes on the 68000.
- IFD AMIGA
- SAVEREGS reg d3-d7/a2/a3/a5 ; don't protect d0/d1/a0/a1
- ELSE ; !AMIGA
- SAVEREGS reg d1/d3-d7/a0-a3/a5 ; protect all except d0 return val
- ENDC ; ?AMIGA
- ; d2, a4, a6 not used... on Amiga, a4 is used by small-data memory model
- longest_match:
- movem.l SAVEREGS,-(sp)
- ; setup steps common to byte and word versions:
- IFD INT16
- and.l #$0000FFFF,Cur_Match ; upper half must be zero!
- ; we use an and.l down here for the sake of ATSIGN/REGARGS.
- moveq #0,Limit ; so adding to Scan_Ini works
- ENDC
- move.w max_chain_len,Chain_Length
- move.w prev_length,Best_Len
- MOVINT _strstart,Limit
- BASEPTR _prev,Prev_Address
- BASEPTR _window,Match_Ini
- move.l Match_Ini,Scan_Ini
- addq #MIN_MATCH,Match_Ini ; optimizes inner loop
- add.l Limit,Scan_Ini
- sub.w #MAX_DIST,Limit
- bhi.s limit_ok
- moveq #0,Limit
- limit_ok:
- cmp.w good_match,Best_Len
- blo.s length_ok
- lsr.w #2,Chain_Length
- length_ok:
- subq.w #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 & 4th bytes, use 1st & 3rd
- moveq #0,Scan_End ; likewise
- moveq #0,Scan_Test ; likewise
- move.b (Scan_Ini),Scan_Start
- swap Scan_Start ; swap is faster than 8 bit shift
- move.b 1(Scan_Ini),Scan_Start
- move.b -1(Scan_Ini,Best_Len.w),Scan_End
- swap Scan_End
- move.b 0(Scan_Ini,Best_Len.w),Scan_End
- bra.s bdo_scan
- blong_loop:
- move.b -1(Scan_Ini,Best_Len.w),Scan_End
- swap Scan_End
- move.b 0(Scan_Ini,Best_Len.w),Scan_End
- bshort_loop:
- add.w Cur_Match,Cur_Match ; assert value before doubling < 32K
- IFNE 32768-WSIZE
- and.w #(WMASK*2),Cur_Match
- ENDC
- move.w (Prev_Address,Cur_Match.l),Cur_Match
- cmp.w 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.w),Scan_Test
- swap Scan_Test
- move.b -MIN_MATCH(Match,Best_Len.w),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-3),Scan_Test
- lea MIN_MATCH(Scan_Ini),Scan ; offset optimizes inner loop
- bscan_loop:
- cmp.b (Match)+,(Scan)+
- dbne Scan_Test,bscan_loop
- subq #1,Scan
- sub.l Scan_Ini,Scan ; assert difference is 16 bits
- cmp.w Best_Len,Scan
- bls.s bshort_loop
- MOVINT Scan,Best_Len
- move.w Cur_Match,match_start
- cmp.w nice_match,Best_Len
- blo.s blong_loop
- IFD CPUTEST
- bra return
- ENDC
- ENDC ; !CPU020
- IFND CPU000
- MACHINE MC68020
- ; 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.w),Scan_End
- bra.s wdo_scan
- wlong_loop:
- move.w -1(Scan_Ini,Best_Len.w),Scan_End
- wshort_loop:
- and.w #WMASK,Cur_Match
- move.w (Prev_Address,Cur_Match.w*2),Cur_Match ; '020 addressing mode
- cmp.w 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.w),Scan_End
- bne.s wshort_loop
- cmp.w -MIN_MATCH(Match),Scan_Start
- bne.s wshort_loop
- IFD QUADLONG
- ; By some measurements, this version of the code is a little tiny bit faster.
- ; But on some files it's slower. It probably pays off only when there are
- ; long match strings, and costs in the most common case of three-byte matches.
- moveq #((MAX_MATCH-MIN_MATCH)/16),Scan_Test ; value = 15
- lea MIN_MATCH(Scan_Ini),Scan ; offset optimizes inner loop
- wscan_loop:
- cmp.l (Match)+,(Scan)+ ; test four bytes at a time
- bne.s odd
- cmp.l (Match)+,(Scan)+
- bne.s odd
- cmp.l (Match)+,(Scan)+
- bne.s odd
- cmp.l (Match)+,(Scan)+
- dbne Scan_Test,wscan_loop ; '020 can cache a bigger loop
- odd:
- subq #4,Scan
- subq #4,Match
- cmp.b (Match)+,(Scan)+ ; find good bytes in bad longword
- bne.s even
- cmp.b (Match)+,(Scan)+
- bne.s even
- cmp.b (Match)+,(Scan)+
- beq.s steven
- even: subq #1,Scan
- ELSE ; !QUADLONG
- moveq #((MAX_MATCH-MIN_MATCH)/2),Scan_Test ; value = 127
- lea MIN_MATCH(Scan_Ini),Scan ; offset optimizes inner loop
- wscan_loop:
- cmp.w (Match)+,(Scan)+
- dbne Scan_Test,wscan_loop
- subq #2,Scan
- move.b -2(Match),Scan_Test
- cmp.b (Scan),Scan_Test
- bne.s steven
- addq #1,Scan
- ENDC ; ?QUADLONG
- steven:
- sub.l Scan_Ini,Scan ; assert: difference is 16 bits
- cmp.w Best_Len,Scan
- bls.s wshort_loop
- MOVINT Scan,Best_Len
- move.w Cur_Match,match_start
- cmp.w nice_match,Best_Len
- blo.s wlong_loop
- MACHINE MC68000
- ENDC ; !CPU000
- return:
- MOVINT Best_Len,d0 ; return value (upper half should be clear)
- movem.l (sp)+,SAVEREGS
- rts
- ; =============================================================================
- ; This is the deflate() function itself, our main entry point. It calls
- ; longest_match, above, and some outside functions. It is a hot spot, but not
- ; as hot as longest_match. It uses no special '020 code.
- ; ================== Several macros used in deflate() and later functions:
- ; Arg 1 is D-reg that new ins_h value is to be left in,
- ; arg 2 is the byte value to be hashed into it, which must not be the same reg
- UP_HASH MACRO
- move.w ins_h,\1
- asl.w #H_SHIFT,\1
- eor.b \2,\1
- and.w #HASH_MASK,\1 ; ((ins_h << H_SHIFT) ^ c) & HASH_MASK
- move.w \1,ins_h ; ins_h = that
- ENDM
- ; Arg 1 is scratch A, arg 2 is scratch D
- IN_STR MACRO
- move.l Strst,\2
- addq.w #MIN_MATCH-1,\2
- move.b (Window,\2.l),\2 ; window[strstart + MIN_MATCH - 1]
- UP_HASH Head,\2
- add.l Head,Head ; assert upper word is zero before add
- BASEPTR _head,\1
- add.l Head,\1
- move.w (\1),Head ; hash_head = head[ins_h]
- move.w Strst,(\1) ; head[ins_h] = strstart
- move.l Strst,\2
- IFNE WSIZE-32768
- and.w #WMASK,\2
- ENDC
- add.w \2,\2 ; masks implicitly when WSIZE == 32768
- move.w Head,(Prev,\2.l) ; prev[str_start & WMASK] = hash_head
- ENDM
- ; Arg 1 is bool (int) EOF flag, flush_block result is in d0, trashes d1/a0/a1
- FLUSH_B MACRO
- IFC '\1','#0'
- CLRINT -(sp)
- ELSE
- MOVINT \1,-(sp)
- ENDC
- move.l _block_start,d0
- blt.s nenu\@
- move.l Window,a0
- add.l d0,a0
- bra.s nun\@
- nenu\@: sub.l a0,a0 ; if block_start < 0, push NULL
- nun\@: sub.l Strst,d0
- neg.l d0
- move.l d0,-(sp)
- move.l a0,-(sp)
- jsr _flush_block
- lea 8+INTSIZE(sp),sp
- ENDM
- ; This expands to nothing unless DEBUG is defined.
- ; Arg 1 is a byte to be trace-outputted -- if it is d0 it must be a valid int
- TRACE_C MACRO
- IFD DEBUG
- cmp.w #1,_verbose+INTSIZE-2 ; test lower word only
- ble.s qui\@
- IFNC '\1','d0'
- moveq #0,d0
- move.b \1,d0
- ENDC
- move.l _stderr,-(sp)
- MOVINT d0,-(sp)
- jsr _fputc
- addq #4+INTSIZE,sp
- qui\@:
- ENDC ; DEBUG
- ENDM
- ; ================== Here are the register vars we use, and deflate() itself:
- Window equr a2 ; cached address of window[]
- Prev equr a3 ; cached address of prev[]
- Strst equr d7 ; strstart cached as a longword
- Look equr d6 ; lookahead cached as short
- Head equr d5 ; local variable hash_head, short
- PrevL equr d4 ; prev_length cached as short
- MatchL equr d3 ; local variable match_length, unsigned short
- Avail equr d2 ; local variable available_match, bool
- PrevM equr a5 ; local variable prev_match, int in an A-reg
- IFD AMIGA
- DEFREGS reg d2-d7/a2/a3/a5
- ELSE
- DEFREGS reg d0-d7/a0/a2/a3/a5 ; play it safe, preserve all regs
- ENDC
- _deflate: ; first, setup steps common to deflate and deflate_fast:
- movem.l DEFREGS,-(sp)
- IFD INT16
- moveq #0,Strst ; make sure strstart is valid as a long
- ENDC
- moveq #0,Head ; ditto for hash_head
- MOVINT _strstart,Strst
- move.w lookahead,Look
- move.w prev_length,PrevL
- BASEPTR _window,Window
- BASEPTR _prev,Prev
- MOVINT _level,d0
- cmp.w #3,d0
- ble deflate_fast
- moveq #MIN_MATCH-1,MatchL
- moveq #0,Avail
- look_loop:
- tst.w Look
- beq last_tally
- IN_STR a0,d0
- move.w MatchL,PrevL
- move.w match_start,PrevM
- move.w #MIN_MATCH-1,MatchL
- tst.w Head
- beq.s no_new_match
- cmp.w max_lazy_match,PrevL
- bhs.s no_new_match
- move.w Strst,d0
- sub.w Head,d0
- cmp.w #MAX_DIST,d0
- bhi.s no_new_match
- move.w PrevL,prev_length ; longest_match reads these variables
- MOVINT Strst,_strstart
- MOVINT Head,d0 ; parm for longest_match
- bsr longest_match ; sets match_start
- cmp.w Look,d0 ; does length exceed valid data?
- bls.s stml
- move.w Look,d0
- stml: move.w d0,MatchL ; valid length of match
- cmp.w #MIN_MATCH,MatchL ; is the match only three bytes?
- bne.s no_new_match
- move.w match_start,d0
- sub.w Strst,d0
- cmp.w #-TOO_FAR,d0
- bge.s no_new_match
- moveq #MIN_MATCH-1,MatchL ; mark the current match as no good
- no_new_match:
- cmp.w #MIN_MATCH,PrevL
- blo literal
- cmp.w MatchL,PrevL
- blo literal
- ; CHECK_MATCH Strst-1,PrevM,PrevL
- MOVINT Strst,_strstart ; ct_tally reads this variable
- move.l PrevL,d0
- subq.w #MIN_MATCH,d0
- MOVINT d0,-(sp)
- move.l Strst,d0
- sub.w PrevM,d0
- subq.w #1,d0
- MOVINT d0,-(sp)
- jsr _ct_tally ; sets d0 true if we have to flush
- addq #2*INTSIZE,sp
- subq.w #3,PrevL ; convert for dbra (prev_length - 2)
- sub.w PrevL,Look
- subq.w #2,Look
- insertmatch:
- addq.w #1,Strst
- IN_STR a0,d1 ; don't clobber d0
- dbra PrevL,insertmatch
- moveq #0,Avail
- moveq #0,PrevL ; not needed?
- moveq #MIN_MATCH-1,MatchL
- addq.w #1,Strst
- tst.w d0
- beq refill
- FLUSH_B #0
- move.l Strst,_block_start
- bra.s refill
- literal:
- tst.w Avail
- bne.s yeslit
- moveq #1,Avail
- bra.s skipliteral
- yeslit: TRACE_C <-1(Window,Strst.l)>
- MOVINT Strst,_strstart ; ct_tally reads this variable
- moveq #0,d0
- move.b -1(Window,Strst.l),d0
- MOVINT d0,-(sp)
- CLRINT -(sp)
- jsr _ct_tally
- addq #2*INTSIZE,sp
- tst.w d0
- beq.s skipliteral
- FLUSH_B #0
- move.l Strst,_block_start
- skipliteral:
- addq.w #1,Strst
- subq.w #1,Look
- refill:
- cmp.w #MIN_LOOKAHEAD,Look
- bhs look_loop
- bsr fill_window
- bra look_loop
- last_tally:
- tst.w Avail
- beq last_flush
- MOVINT Strst,_strstart ; ct_tally reads this variable
- moveq #0,d0
- move.b -1(Window,Strst.l),d0
- MOVINT d0,-(sp)
- CLRINT -(sp)
- jsr _ct_tally
- addq #2*INTSIZE,sp
- last_flush:
- FLUSH_B #1
- bra deflate_exit
- ; ================== This is another version used for low compression levels:
- deflate_fast:
- moveq #0,MatchL
- moveq #MIN_MATCH-1,PrevL
- flook_loop:
- tst.w Look
- beq flast_flush
- IN_STR a0,d0
- tst.w Head
- beq.s fno_new_match
- move.w Strst,d0
- sub.w Head,d0
- cmp.w #MAX_DIST,d0
- bhi.s fno_new_match
- move.w PrevL,prev_length ; longest_match reads these variables
- MOVINT Strst,_strstart
- MOVINT Head,d0 ; parm for longest_match
- bsr longest_match ; sets match_start
- cmp.w Look,d0 ; does length exceed valid data?
- bls.s fstml
- move.w Look,d0
- fstml: move.w d0,MatchL ; valid length of match
- fno_new_match:
- cmp.w #MIN_MATCH,MatchL
- blo fliteral
- ; CHECK_MATCH Strst,match_start,MatchL
- MOVINT Strst,_strstart ; ct_tally reads this variable
- move.l MatchL,d0
- subq.w #MIN_MATCH,d0
- MOVINT d0,-(sp)
- move.l Strst,d0
- sub.w match_start,d0
- MOVINT d0,-(sp)
- jsr _ct_tally ; sets d0 true if we have to flush
- addq #2*INTSIZE,sp
- sub.w MatchL,Look
- cmp.w max_lazy_match,MatchL
- bhi ftoolong
- subq.w #2,MatchL
- finsertmatch:
- addq.w #1,Strst
- IN_STR a0,d1 ; preserve d0
- dbra MatchL,finsertmatch
- moveq #0,MatchL ; not needed?
- addq.w #1,Strst
- bra.s flushfill
- ftoolong:
- add.w MatchL,Strst
- moveq #0,MatchL
- moveq #0,d1 ; preserve d0
- move.b (Window,Strst.l),d1
- move.w d1,ins_h
- ; My assembler objects to passing <1(Window,Strst.l)> directly to UP_HASH...
- move.b 1(Window,Strst.l),Avail ; Avail is not used in deflate_fast
- UP_HASH d1,Avail ; preserve d0
- IFNE MIN_MATCH-3
- FAIL needs to UP_HASH another MIN_MATCH-3 times, but with what arg?
- ENDC
- bra.s flushfill
- fliteral:
- TRACE_C <(Window,Strst.l)>
- MOVINT Strst,_strstart ; ct_tally reads this variable
- moveq #0,d0
- move.b (Window,Strst.l),d0
- MOVINT d0,-(sp)
- CLRINT -(sp)
- jsr _ct_tally ; d0 set if we need to flush
- addq #2*INTSIZE,sp
- addq.w #1,Strst
- subq.w #1,Look
- flushfill:
- tst.w d0
- beq.s frefill
- FLUSH_B #0
- move.l Strst,_block_start
- frefill:
- cmp.w #MIN_LOOKAHEAD,Look
- bhs flook_loop
- bsr fill_window
- bra flook_loop
- flast_flush:
- FLUSH_B #1 ; sets our return value
- deflate_exit:
- MOVINT Strst,_strstart ; save back cached values
- move.w PrevL,prev_length
- move.w Look,lookahead
- movem.l (sp)+,DEFREGS
- rts
- ; =========================================================================
- ; void fill_window(void) calls the input function to refill the sliding
- ; window that we use to find substring matches in.
- More equr Head ; local variable in fill_window
- WindTop equr Prev ; local variable used for sliding
- SlidIx equr PrevL ; local variable used for sliding
- IFD AMIGA
- FWREGS reg d2-d5/a2-a6 ; does NOT include Look and Strst
- ELSE
- FWREGS reg d1-d5/a0-a6 ; likewise
- ENDC
- ; all registers available to be clobbered by the sliding operation:
- ; we exclude More, WindTop, SlidIx, Look, Strst, Window, a4 and a7.
- SPAREGS reg d0-d3/a0-a1/a5-a6
- SPCOUNT equ 8 ; number of registers in SPAREGS
- _fill_window: ; C-callable entry point
- movem.l Strst/Look,-(sp)
- IFD INT16
- moveq #0,Strst ; Strst must be valid as a long
- ENDC
- MOVINT _strstart,Strst
- move.w lookahead,Look
- BASEPTR _window,Window
- bsr.s fill_window
- MOVINT Strst,_strstart
- move.w Look,lookahead
- movem.l (sp)+,Strst/Look
- rts
- ; strstart, lookahead, and window must be cached in Strst, Look, and Window:
- fill_window: ; asm-callable entry point
- movem.l FWREGS,-(sp)
- tst.w eofile ; we put this up here for speed
- bne fwdone
- and.l #$FFFF,Look ; make sure Look is valid as long
- fw_refill:
- move.l _window_size,More ; <= 64K
- sub.l Look,More
- sub.l Strst,More ; Strst is already valid as long
- cmp.w #EOF,More
- bne.s notboundary
- subq.w #1,More
- bra checkend
- notboundary:
- tst.w sliding
- beq checkend
- cmp.w #WSIZE+MAX_DIST,Strst
- blo checkend
- IFGT 32768-WSIZE
- lea WSIZE(Window),WindTop ; WindTop is aligned when Window is
- ELSE
- move.l Window,WindTop
- add.l #WSIZE,WindTop
- ENDC
- move.l Window,d0
- and.w #3,d0
- beq.s isaligned
- subq.w #1,d0
- align: move.b (WindTop)+,(Window)+ ; copy up to a longword boundary
- dbra d0,align
- isaligned:
- ; This is faster than a simple move.l (WindTop)+,(Window)+ / dbra loop:
- move.w #(WSIZE-1)/(4*SPCOUNT),SlidIx
- slide: movem.l (WindTop)+,SPAREGS ; copy, 32 bytes at a time!
- movem.l SPAREGS,(Window) ; a slight overshoot doesn't matter.
- lea 4*SPCOUNT(Window),Window ; can't use (aN)+ as movem.l dest
- dbra SlidIx,slide
- BASEPTR _window,Window ; restore cached value
- sub.w #WSIZE,match_start
- sub.w #WSIZE,Strst
- sub.l #WSIZE,_block_start
- add.w #WSIZE,More
- BASEPTR _head,a0
- move.w #HASH_SIZE-1,d0
- fixhead:
- move.w (a0),d1
- sub.w #WSIZE,d1
- bpl.s headok
- moveq #0,d1
- headok: move.w d1,(a0)+
- dbra d0,fixhead
- BASEPTR _prev,a0
- move.w #WSIZE-1,d0
- fixprev:
- move.w (a0),d1
- sub.w #WSIZE,d1
- bpl.s prevok
- moveq #0,d1
- prevok: move.w d1,(a0)+
- dbra d0,fixprev
- TRACE_C #'.'
- checkend: ; assert eofile is false
- MOVINT More,-(sp) ; assert More's upper word is zero
- move.l Strst,d0
- add.w Look,d0
- add.l Window,d0
- move.l d0,-(sp)
- move.l _read_buf,a0
- jsr (a0) ; refill the upper part of the window
- addq #4+INTSIZE,sp
- tst.w d0
- beq.s iseof
- cmp.w #EOF,d0
- beq.s iseof
- add.w d0,Look
- cmp.w #MIN_LOOKAHEAD,Look
- blo fw_refill ; eofile is still false
- bra.s fwdone
- iseof: move.w #1,eofile
- fwdone: movem.l (sp)+,FWREGS
- rts
- ; =========================================================================
- ; void lm_free(void) frees dynamic arrays in the DYN_ALLOC version.
- xdef _lm_free ; the entry point
- _lm_free:
- IFD DYN_ALLOC
- move.l _window,d0
- beq.s lf_no_window
- move.l d0,-(sp)
- jsr _free
- addq #4,sp
- clr.l _window
- lf_no_window:
- move.l _prev,d0
- beq.s lf_no_prev
- move.l d0,-(sp)
- jsr _free
- move.l _head,(sp) ; reuse the same stack arg slot
- jsr _free
- addq #4,sp
- clr.l _prev
- clr.l _head
- lf_no_prev:
- ENDC
- rts
- ; ============================================================================
- ; void lm_init(int pack_level, unsigned short *flags) allocates dynamic arrays
- ; if any, and initializes all variables so that deflate() is ready to go.
- xdef _lm_init ; the entry point
- Level equr d2
- ;Window equr a2 ; as in deflate()
- IFD AMIGA
- INIREGS reg d2/a2
- ELSE
- INIREGS reg d0-d2/a0-a1
- ENDC
- _lm_init:
- MOVINT 4(sp),d0
- move.l 4+INTSIZE(sp),a0
- movem.l INIREGS,-(sp)
- move.w d0,Level
- cmp.w #1,Level
- blt.s levelerr
- bgt.s try9
- bset.b #B_FAST,1(a0)
- try9: cmp.w #9,Level
- bgt.s levelerr
- blt.s levelok
- bset.b #B_SLOW,1(a0)
- bra.s levelok
- levelerr:
- pea level_message
- jsr _error ; never returns
- levelok:
- clr.w sliding
- tst.l _window_size
- bne.s gotawindowsize
- move.w #1,sliding
- move.l #2*WSIZE,_window_size
- gotawindowsize:
- BASEPTR _window,Window
- IFD DYN_ALLOC
- move.l Window,d0 ; fake tst.l
- bne.s gotsomewind
- CAL_SH WSIZE
- move.l d0,Window
- move.l d0,_window
- bne.s gotsomewind
- pea window_message
- MOVINT #ZE_MEM,-(sp)
- jsr _ziperr ; never returns
- gotsomewind:
- tst.l _prev
- bne.s gotsomehead
- CAL_SH WSIZE
- move.l d0,_prev
- beq.s nohead
- CAL_SH HASH_SIZE
- move.l d0,_head
- bne.s gotfreshhead ; newly calloc'd memory is zeroed
- nohead: pea hash_message
- MOVINT #ZE_MEM,-(sp)
- jsr _ziperr ; never returns
- gotsomehead:
- ENDC ; DYN_ALLOC
- move.w #(HASH_SIZE/2)-1,d0 ; two shortwords per loop
- BASEPTR _head,a0
- wipeh: clr.l (a0)+
- dbra d0,wipeh
- gotfreshhead:
- move.l Level,d0
- IFEQ Sizeof_config-8
- asl.l #3,d0
- ELSE
- mulu #Sizeof_config,d0
- ENDC
- lea config_table,a0
- add.l d0,a0
- move.w Max_lazy(a0),max_lazy_match
- move.w Good_length(a0),good_match
- move.w Nice_length(a0),nice_match
- move.w Max_chain(a0),max_chain_len
- CLRINT _strstart
- clr.l _block_start
- bsr match_init
- clr.w eofile
- MOVINT #WSIZE,-(sp) ; We read only 32K because lookahead is short
- move.l Window,-(sp) ; even when int size is long, as if deflate.c
- move.l _read_buf,a0 ; were compiled with MAXSEG_64K defined.
- jsr (a0)
- addq #4+INTSIZE,sp
- move.w d0,lookahead
- beq.s noread
- cmp.w #EOF,d0
- bne.s irefill
- noread: move.w #1,eofile
- clr.w lookahead
- bra.s init_done
- irefill:
- move.w lookahead,d0
- cmp.w #MIN_LOOKAHEAD,d0
- bhs.s hashify
- bsr _fill_window ; use the C-callable version
- hashify:
- clr.w ins_h
- moveq #MIN_MATCH-2,d0
- hash1: move.b (Window)+,d1
- UP_HASH Level,d1
- dbra d0,hash1
- init_done:
- movem.l (sp)+,INIREGS
- rts
- ; strings for error messages:
- hash_message dc.b 'hash table allocation',0
- window_message dc.b 'window allocation',0
- level_message dc.b 'bad pack level',0
- end
|