;=========================================================================== ; 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< 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