1 ; gvmat32.asm -- Asm portion of the optimized longest_match for 32 bits x86
2 ; Copyright (C) 1995-1996 Jean-loup Gailly and Gilles Vollant.
3 ; File written by Gilles Vollant, by modifiying the longest_match
4 ; from Jean-loup Gailly in deflate.c
7 ; http://www.winimage.com/zLibDll
8 ; http://www.muppetlabs.com/~breadbox/software/assembly.html
10 ; For Visual C++ 4.x and higher and ML 6.x and higher
11 ; ml.exe is in directory \MASM611C of Win95 DDK
12 ; ml.exe is also distributed in http://www.masm32.com/masmdl.htm
13 ; and in VC++2003 toolkit at http://msdn.microsoft.com/visualc/vctoolkit2003/
15 ; this file contain two implementation of longest_match
17 ; longest_match_7fff : written 1996 by Gilles Vollant optimized for
18 ; first Pentium. Assume s->w_mask == 0x7fff
19 ; longest_match_686 : written by Brian raiter (1998), optimized for Pentium Pro
21 ; for using an seembly version of longest_match, you need define ASMV in project
22 ; There is two way in using gvmat32.asm
25 ; if you want include both longest_match_7fff and longest_match_686
26 ; compile the asm file running
27 ; ml /coff /Zi /Flgvmat32.lst /c gvmat32.asm
28 ; and include gvmat32c.c in your project
29 ; if you have an old cpu (386,486 or first Pentium) and s->w_mask==0x7fff,
30 ; longest_match_7fff will be used
31 ; if you have a more modern CPU (Pentium Pro, II and higher)
32 ; longest_match_686 will be used
33 ; on old cpu with s->w_mask!=0x7fff, longest_match_686 will be used,
34 ; but this is not a sitation you'll find often
37 ; if you are not interresed in old cpu performance and want the smaller
40 ; compile the asm file running
41 ; ml /coff /Zi /c /Flgvmat32.lst /DNOOLDPENTIUMCODE gvmat32.asm
42 ; and do not include gvmat32c.c in your project (ou define also
45 ; note : as I known, longest_match_686 is very faster than longest_match_7fff
46 ; on pentium Pro/II/III, faster (but less) in P4, but it seem
47 ; longest_match_7fff can be faster (very very litte) on AMD Athlon64/K8
49 ; see below : zlib1222add must be adjuster if you use a zlib version < 1.2.2.2
51 ;uInt longest_match_7fff(s, cur_match)
53 ; IPos cur_match; /* current match */
56 cur_match
equ dword ptr[esp+NbStack
-0]
57 str_s
equ dword ptr[esp+NbStack
-4]
58 ; 5 dword on top (ret,ebp,esi,edi,ebx)
59 adrret
equ dword ptr[esp+NbStack
-8]
60 pushebp
equ dword ptr[esp+NbStack
-12]
61 pushedi
equ dword ptr[esp+NbStack
-16]
62 pushesi
equ dword ptr[esp+NbStack
-20]
63 pushebx
equ dword ptr[esp+NbStack
-24]
65 chain_length
equ dword ptr [esp+NbStack
-28]
66 limit
equ dword ptr [esp+NbStack
-32]
67 best_len
equ dword ptr [esp+NbStack
-36]
68 window
equ dword ptr [esp+NbStack
-40]
69 prev
equ dword ptr [esp+NbStack
-44]
70 scan_start
equ word ptr [esp+NbStack
-48]
71 wmask
equ dword ptr [esp+NbStack
-52]
72 match_start_ptr
equ dword ptr [esp+NbStack
-56]
73 nice_match
equ dword ptr [esp+NbStack
-60]
74 scan
equ dword ptr [esp+NbStack
-64]
76 windowlen
equ dword ptr [esp+NbStack
-68]
77 match_start
equ dword ptr [esp+NbStack
-72]
78 strend
equ dword ptr [esp+NbStack
-76]
79 NbStackAdd
equ (NbStack
-24)
88 ; all the +zlib1222add offsets are due to the addition of fields
89 ; in zlib in the deflate_state structure since the asm code was first written
90 ; (if you compile with zlib 1.0.4 or older, use "zlib1222add equ (-4)").
91 ; (if you compile with zlib between 1.0.5 and 1.2.2.1, use "zlib1222add equ 0").
92 ; if you compile with zlib 1.2.2.2 or later , use "zlib1222add equ 8").
96 ; Note : these value are good with a 8 bytes boundary pack structure
97 dep_chain_length
equ 74h+zlib1222add
98 dep_window
equ 30h+zlib1222add
99 dep_strstart
equ 64h+zlib1222add
100 dep_prev_length
equ 70h+zlib1222add
101 dep_nice_match
equ 88h+zlib1222add
102 dep_w_size
equ 24h+zlib1222add
103 dep_prev
equ 38h+zlib1222add
104 dep_w_mask
equ 2ch+zlib1222add
105 dep_good_match
equ 84h+zlib1222add
106 dep_match_start
equ 68h+zlib1222add
107 dep_lookahead
equ 6ch+zlib1222add
113 IFDEF NOOLDPENTIUMCODE
117 public longest_match_7fff
119 public longest_match_686
122 IFDEF NOOLDPENTIUMCODE
123 public _longest_match
126 public _longest_match_7fff
128 public _longest_match_686
134 MIN_LOOKAHEAD
equ (MAX_MATCH
+MIN_MATCH
+1)
138 IFNDEF NOOLDPENTIUMCODE
140 longest_match_7fff
proc near
142 _longest_match_7fff
proc near
156 ; initialize or check the variables used in match.asm.
159 ; chain_length = s->max_chain_length
160 ; if (prev_length>=good_match) chain_length >>= 2
161 mov edx,[ebp+dep_chain_length
]
162 mov ebx,[ebp+dep_prev_length
]
163 cmp [ebp+dep_good_match
],ebx
167 ; we increment chain_length because in the asm, the --chain_lenght is in the beginning of the loop
169 mov edi,[ebp+dep_nice_match
]
171 mov eax,[ebp+dep_lookahead
]
173 ; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
174 jae nolookaheadnicematch
176 nolookaheadnicematch:
177 ; best_len = s->prev_length
181 mov esi,[ebp+dep_window
]
182 mov ecx,[ebp+dep_strstart
]
186 ; scan = window + strstart
190 mov dx,word ptr [esi]
191 ; bx = *(window+best_len-1)
192 mov bx,word ptr [esi+ebx-1]
196 ; strend = scan + MAX_MATCH-1
198 ; bx = scan_end = *(window+best_len-1)
200 ; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
201 ; s->strstart - (IPos)MAX_DIST(s) : NIL;
203 mov esi,[ebp+dep_w_size
]
204 sub esi,MIN_LOOKAHEAD
205 ; here esi = MAX_DIST(s)
213 mov edx,[ebp+dep_prev
]
217 mov edx,dword ptr [ebp+dep_match_start
]
227 ; windowlen = window + best_len -1
234 ; eax = ax = cur_match
238 ; edi = windowlen (window + best_len -1)
242 ;// here; chain_length <=16
247 cmp word ptr[edi+eax],bx
250 ; cur_match = prev[cur_match & wmask]
252 mov ax,word ptr[esi+eax*2]
253 ; if cur_match > limit, go to exitloop
256 ; if --chain_length != 0, go to exitloop
262 ; if (scan_start==*(cur_match+window)) goto normalbeg2
263 cmp bp,word ptr[edx+eax]
270 ; cur_match = prev[cur_match & wmask]
272 mov ax,word ptr[esi+eax*2]
273 ; if cur_match > limit, go to exitloop
277 ; if --chain_length != 0, go to exitloop
280 ; begin the main loop
282 sub chain_length
,16+1
283 ; if chain_length <=16, don't use the unrolled loop
287 cmp word ptr[edi+eax],bx
292 mov ax,word ptr[esi+eax*2]
295 cmp word ptr[edi+eax],bx
330 maccn
short normalbeg2dc11
333 maccn
short normalbeg2dc12
336 maccn
short normalbeg2dc13
339 maccn
short normalbeg2dc14
342 maccn
short normalbeg2dc15
346 mov ax,word ptr[esi+eax*2]
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
356 normbeg
MACRO rcontlab
,valsub
357 ; if we are here, we know that *(match+best_len-1) == scan_end
358 cmp bp,word ptr[edx+eax]
359 ; if (match != scan_start) goto rcontlab
361 ; calculate the good chain_length, and we'll compare scan and match string
362 add chain_length
,16-valsub
368 normbeg rcontloop11
,11
371 normbeg
short rcontloop12
,12
374 normbeg
short rcontloop13
,13
377 normbeg
short rcontloop14
,14
380 normbeg
short rcontloop15
,15
383 normbeg rcontloop10
,10
416 ; we go in normalbeg2 because *(ushf*)(match+best_len-1) == scan_end
421 cmp bp,word ptr[edi+eax]
422 jne contloop3
; if *(ushf*)match != scan_start, continue
425 ; if we are here, we know that *(match+best_len-1) == scan_end
426 ; and (match == scan_start)
429 mov esi,scan
; esi = scan
430 add edi,eax ; edi = window + cur_match = match
432 mov edx,[esi+3] ; compare manually dword at match+3
433 xor edx,[edi+3] ; and scan +3
435 jz begincompare
; if equal, go to long compare
437 ; we will determine the unmatch byte and calculate len (in esi)
458 ; here we now scan and match begin same
461 mov ecx,(MAX_MATCH
-(2+4))/4 ; scan for at most MAX_MATCH bytes
462 repe cmpsd ; loop until mismatch
464 je trfin
; go to trfin if not unmatch
465 ; we determine the unmatch byte
483 sub esi,scan
; esi = len
485 ; here we have finised compare, and esi contain len of equal string
486 cmp esi,best_len
; if len > best_len, go newbestlen
488 ; now we restore edx, ecx and esi, for the big loop
495 mov best_len
,esi ; len become best_len
497 mov match_start
,eax ; save new position as match_start
498 cmp esi,nice_match
; if best_len >= nice_match, exit
501 mov edx,window
; restore edx=window
506 mov windowlen
,esi ; windowlen = window + best_len-1
507 mov bx,[ecx-1] ; bx = *(scan+best_len-1) = scan_end
509 ; now we restore ecx and esi, for the big loop :
515 ; exit : s->match_start=match_start
519 mov dword ptr [ebp+dep_match_start
],ebx
520 mov eax,dword ptr [ebp+dep_lookahead
]
525 ; return min(best_len,s->lookahead)
527 ; restore stack and register ebx,esi,edi,ebp
536 ; please don't remove this string !
537 ; Your are free use gvmat32 in any fre or commercial apps if you don't remove the string in the binary!
538 db 0dh,0ah,"GVMat32 optimised assembly code written 1996-98 by Gilles Vollant",0dh,0ah
543 longest_match_7fff
endp
545 _longest_match_7fff
endp
550 cpudetect32
proc near
552 _cpudetect32
proc near
557 pushfd ; push original EFLAGS
558 pop eax ; get original EFLAGS
559 mov ecx, eax ; save original EFLAGS
560 xor eax, 40000h ; flip AC bit in EFLAGS
561 push eax ; save new EFLAGS value on stack
562 popfd ; replace current EFLAGS value
563 pushfd ; get new EFLAGS
564 pop eax ; store new EFLAGS in EAX
565 xor eax, ecx ; can’t toggle AC bit, processor=80386
566 jz end_cpu_is_386
; jump if 80386 processor
568 popfd ; restore AC bit in EFLAGS first
574 mov eax, ecx ; get original EFLAGS
575 xor eax, 200000h ; flip ID bit in EFLAGS
576 push eax ; save new EFLAGS value on stack
577 popfd ; replace current EFLAGS value
578 pushfd ; get new EFLAGS
579 pop eax ; store new EFLAGS in EAX
580 popfd ; restore original EFLAGS
581 xor eax, ecx ; can’t toggle ID bit,
582 je is_old_486
; processor=old
608 MIN_LOOKAHEAD
equ (MAX_MATCH
+ MIN_MATCH
+ 1)
609 MAX_MATCH_8_
equ ((MAX_MATCH
+ 7) AND 0FFF0h
)
612 ;;; stack frame offsets
614 chainlenwmask
equ esp + 0 ; high word: current chain len
616 window
equ esp + 4 ; local copy of s->window
617 windowbestlen
equ esp + 8 ; s->window + bestlen
618 scanstart
equ esp + 16 ; first two bytes of string
619 scanend
equ esp + 12 ; last two bytes of string
620 scanalign
equ esp + 20 ; dword-misalignment of string
621 nicematch
equ esp + 24 ; a good enough match size
622 bestlen
equ esp + 28 ; size of best match so far
623 scan
equ esp + 32 ; ptr to string wanting match
626 ; saved ebx byte esp + 36
627 ; saved edi byte esp + 40
628 ; saved esi byte esp + 44
629 ; saved ebp byte esp + 48
630 ; return address byte esp + 52
631 deflatestate
equ esp + 56 ; the function arguments
632 curmatch
equ esp + 60
634 ;;; Offsets for fields in the deflate_state structure. These numbers
635 ;;; are calculated from the definition of deflate_state, with the
636 ;;; assumption that the compiler will dword-align the fields. (Thus,
637 ;;; changing the definition of deflate_state could easily cause this
638 ;;; program to crash horribly, without so much as a warning at
639 ;;; compile time. Sigh.)
641 dsWSize
equ 36+zlib1222add
642 dsWMask
equ 44+zlib1222add
643 dsWindow
equ 48+zlib1222add
644 dsPrev
equ 56+zlib1222add
645 dsMatchLen
equ 88+zlib1222add
646 dsPrevMatch
equ 92+zlib1222add
647 dsStrStart
equ 100+zlib1222add
648 dsMatchStart
equ 104+zlib1222add
649 dsLookahead
equ 108+zlib1222add
650 dsPrevLen
equ 112+zlib1222add
651 dsMaxChainLen
equ 116+zlib1222add
652 dsGoodMatch
equ 132+zlib1222add
653 dsNiceMatch
equ 136+zlib1222add
656 ;;; match.asm -- Pentium-Pro-optimized version of longest_match()
657 ;;; Written for zlib 1.1.2
658 ;;; Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
659 ;;; You can look at http://www.muppetlabs.com/~breadbox/software/assembly.html
661 ;;; This is free software; you can redistribute it and/or modify it
662 ;;; under the terms of the GNU General Public License.
664 ;GLOBAL _longest_match, _match_init
669 ;;; uInt longest_match(deflate_state *deflatestate, IPos curmatch)
672 IFDEF NOOLDPENTIUMCODE
674 longest_match
proc near
676 _longest_match
proc near
680 longest_match_686
proc near
682 _longest_match_686
proc near
686 ;;; Save registers that the compiler may be using, and adjust esp to
687 ;;; make room for our stack frame.
693 sub esp, LocalVarsSize
695 ;;; Retrieve the function arguments. ecx will hold cur_match
696 ;;; throughout the entire function. edx will hold the pointer to the
697 ;;; deflate_state structure during the function's setup (before
698 ;;; entering the main loop.
700 mov edx, [deflatestate
]
703 ;;; uInt wmask = s->w_mask;
704 ;;; unsigned chain_length = s->max_chain_length;
705 ;;; if (s->prev_length >= s->good_match) {
706 ;;; chain_length >>= 2;
709 mov eax, [edx + dsPrevLen
]
710 mov ebx, [edx + dsGoodMatch
]
712 mov eax, [edx + dsWMask
]
713 mov ebx, [edx + dsMaxChainLen
]
718 ;;; chainlen is decremented once beforehand so that the function can
719 ;;; use the sign flag instead of the zero flag for the exit test.
720 ;;; It is then shifted into the high word, to make room for the wmask
721 ;;; value, which it will always accompany.
726 mov [chainlenwmask
], ebx
728 ;;; if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
730 mov eax, [edx + dsNiceMatch
]
731 mov ebx, [edx + dsLookahead
]
735 LookaheadLess: mov [nicematch
], ebx
737 ;;; register Bytef *scan = s->window + s->strstart;
739 mov esi, [edx + dsWindow
]
741 mov ebp, [edx + dsStrStart
]
745 ;;; Determine how many bytes the scan ptr is off from being
753 ;;; IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
754 ;;; s->strstart - (IPos)MAX_DIST(s) : NIL;
756 mov eax, [edx + dsWSize
]
757 sub eax, MIN_LOOKAHEAD
763 ;;; int best_len = s->prev_length;
765 mov eax, [edx + dsPrevLen
]
768 ;;; Store the sum of s->window + best_len in esi locally, and in esi.
771 mov [windowbestlen
], esi
773 ;;; register ush scan_start = *(ushf*)scan;
774 ;;; register ush scan_end = *(ushf*)(scan+best_len-1);
775 ;;; Posf *prev = s->prev;
777 movzx ebx, word ptr [edi]
779 movzx ebx, word ptr [edi + eax - 1]
781 mov edi, [edx + dsPrev
]
783 ;;; Jump into the main loop.
785 mov edx, [chainlenwmask
]
791 ;;; match = s->window + cur_match;
792 ;;; if (*(ushf*)(match+best_len-1) != scan_end ||
793 ;;; *(ushf*)match != scan_start) continue;
795 ;;; } while ((cur_match = prev[cur_match & wmask]) > limit
796 ;;; && --chain_length != 0);
798 ;;; Here is the inner loop of the function. The function will spend the
799 ;;; majority of its time in this loop, and majority of that time will
800 ;;; be spent in the first ten instructions.
802 ;;; Within this loop:
805 ;;; edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
806 ;;; esi = windowbestlen - i.e., (window + bestlen)
812 movzx ecx, word ptr [edi + ecx*2]
817 LoopEntry: movzx eax, word ptr [esi + ecx - 1]
821 movzx eax, word ptr [eax + ecx]
825 ;;; Store the current value of chainlen.
827 mov [chainlenwmask
], edx
829 ;;; Point edi to the string under scrutiny, and esi to the string we
830 ;;; are hoping to match it up with. In actuality, esi and edi are
831 ;;; both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and edx is
832 ;;; initialized to -(MAX_MATCH_8 - scanalign).
838 mov edx, 0fffffef8h
; -(MAX_MATCH_8)
839 lea edi, [edi + eax + 0108h] ;MAX_MATCH_8]
840 lea esi, [esi + eax + 0108h] ;MAX_MATCH_8]
842 ;;; Test the strings for equality, 8 bytes at a time. At the end,
843 ;;; adjust edx so that it is offset to the exact byte that mismatched.
845 ;;; We already know at this point that the first three bytes of the
846 ;;; strings match each other, and they can be safely passed over before
847 ;;; starting the compare loop. So what this code does is skip over 0-3
848 ;;; bytes, as much as necessary in order to dword-align the edi
849 ;;; pointer. (esi will still be misaligned three times out of four.)
851 ;;; It should be confessed that this loop usually does not represent
852 ;;; much of the total running time. Replacing it with a more
853 ;;; straightforward "rep cmpsb" would not drastically degrade
860 mov eax, [esi + edx + 4]
861 xor eax, [edi + edx + 4]
866 LeaveLoopCmps4: add edx, 4
867 LeaveLoopCmps: test eax, 0000FFFFh
874 ;;; Calculate the length of the match. If it is longer than MAX_MATCH,
875 ;;; then automatically accept it as the best possible match and leave.
883 ;;; If the length of the match is not longer than the best match we
884 ;;; have so far, then forget it and return to the lookup loop.
886 mov edx, [deflatestate
]
890 mov esi, [windowbestlen
]
891 mov edi, [edx + dsPrev
]
893 mov edx, [chainlenwmask
]
896 ;;; s->match_start = cur_match;
898 ;;; if (len >= nice_match) break;
899 ;;; scan_end = *(ushf*)(scan+best_len-1);
901 LongerMatch: mov ebx, [nicematch
]
903 mov [edx + dsMatchStart
], ecx
908 mov [windowbestlen
], esi
909 movzx ebx, word ptr [edi + eax - 1]
910 mov edi, [edx + dsPrev
]
912 mov edx, [chainlenwmask
]
915 ;;; Accept the current string, with the maximum possible length.
917 LenMaximum: mov edx, [deflatestate
]
918 mov dword ptr [bestlen
], MAX_MATCH
919 mov [edx + dsMatchStart
], ecx
921 ;;; if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
922 ;;; return s->lookahead;
925 mov edx, [deflatestate
]
927 mov eax, [edx + dsLookahead
]
933 ;;; Restore the stack and return from whence we came.
935 add esp, LocalVarsSize
942 ; please don't remove this string !
943 ; Your can freely use gvmat32 in any free or commercial app if you don't remove the string in the binary!
944 db 0dh,0ah,"asm686 with masm, optimised assembly code from Brian Raiter, written 1998",0dh,0ah
947 IFDEF NOOLDPENTIUMCODE
959 _match_init
proc near
965 longest_match_686
endp
967 _longest_match_686
endp