/*
* inffast.S is a hand tuned assembler version of:
*
* inffast.c -- fast decoding
* Copyright (C) 1995-2003 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*
* Copyright (C) 2003 Chris Anderson <christop@charm.net>
* Please use the copyright conditions above.
*
* This version (Jan-23-2003) of inflate_fast was coded and tested under
* machine, I found that gzip style archives decompressed about 20% faster than
* the gcc-3.2 -O3 -fomit-frame-pointer compiled version. Your results will
* depend on how large of a buffer is used for z_stream.next_in & next_out
* (8K-32K worked best for my 256K cpu cache) and how much overhead there is in
* 70% of the cpu time and crc32 used 20%.
*
* I am confident that this version will work in the general case, but I have
* not tested a wide variety of datasets or a wide variety of platforms.
*
* Jan-24-2003 -- Added -DUSE_MMX define for slightly faster inflating.
* It should be a runtime flag instead of compile time flag...
*
* Jan-26-2003 -- Added runtime check for MMX support with cpuid instruction.
* With -DUSE_MMX, only MMX code is compiled. With -DNO_MMX, only non-MMX code
* is compiled. Without either option, runtime detection is enabled. Runtime
* detection should work on all modern cpus and the recomended algorithm (flip
* ID bit on eflags and then use the cpuid instruction) is used in many
* multimedia applications. Tested under win2k with gcc-2.95 and gas-2.12
* distributed with cygwin3. Compiling with gcc-2.95 -c inffast.S -o
* inffast.obj generates a COFF object which can then be linked with MSVC++
* compiled code. Tested under FreeBSD 4.7 with gcc-2.95.
*
* Jan-28-2003 -- Tested Athlon XP... MMX mode is slower than no MMX (and
* slower than compiler generated code). Adjusted cpuid check to use the MMX
* code only for Pentiums < P4 until I have more data on the P4. Speed
* improvment is only about 15% on the Athlon when compared with code generated
* with MSVC++. Not sure yet, but I think the P4 will also be slower using the
* MMX mode because many of it's x86 ALU instructions execute in .5 cycles and
* have less latency than MMX ops. Added code to buffer the last 11 bytes of
* the input stream since the MMX code grabs bits in chunks of 32, which
* differs from the inffast.c algorithm. I don't think there would have been
* read overruns where a page boundary was crossed (a segfault), but there
* could have been overruns when next_in ends on unaligned memory (unintialized
* memory read).
*
* Mar-13-2003 -- P4 MMX is slightly slower than P4 NO_MMX. I created a C
* version of the non-MMX code so that it doesn't depend on zstrm and zstate
* structure offsets which are hard coded in this file. This was last tested
* with zlib-1.2.0 which is currently in beta testing, newer versions of this
* and inffas86.c can be found at http://www.eetbeetee.com/zlib/ and
*/
/*
* if you have underscore linking problems (_inflate_fast undefined), try
* using -DGAS_COFF
*/
#if defined( WIN32 ) || defined( __CYGWIN__ )
#else
#define GAS_ELF
#endif
#endif /* ! GAS_COFF && ! GAS_ELF */
#if defined( GAS_COFF )
/* coff externals have underscores */
#endif /* GAS_COFF */
.file "inffast.S"
.text
.align 4,0
.align 4,0
.string "invalid distance code"
.align 4,0
.string "invalid distance too far back"
#if ! defined( NO_MMX )
.align 4,0
.L_mask: /* mask[N] = ( 1 << N ) - 1 */
.long 0
.long 1
.long 3
.long 7
.long 15
.long 31
.long 63
.long 127
.long 255
.long 511
.long 1023
.long 2047
.long 4095
.long 8191
.long 16383
.long 32767
.long 65535
.long 131071
.long 262143
.long 524287
.long 1048575
.long 2097151
.long 4194303
.long 8388607
.long 16777215
.long 33554431
.long 67108863
.long 134217727
.long 268435455
.long 536870911
.long 1073741823
.long 2147483647
.long 4294967295
#endif /* NO_MMX */
.text
/*
* struct z_stream offsets, in zlib.h
*/
/*
* struct inflate_state offsets, in inflate.h
*/
/*
* inflate_fast's activation record
*/
/*
* offsets for local vars on stack
*/
/*
* typedef enum inflate_mode consts, in inflate.h
*/
#define RUN_TIME_MMX
.data
.align 4,0
.long CHECK_MMX
#if defined( GAS_ELF )
/* elf info */
#endif
#endif /* RUN_TIME_MMX */
#if defined( GAS_COFF )
/* coff info: scl 2 = extern, type 32 = function */
#endif
.text
pushf /* save eflags (strm_sp, state_sp assumes this is 32 bits) */
/* in = strm->next_in;
* out = strm->next_out;
* last = in + strm->avail_in - 11;
* beg = out - (start - strm->avail_out);
* end = out + (strm->avail_out - 257);
*/
/* wsize = state->wsize;
* write = state->write;
* window = state->window;
* hold = state->hold;
* bits = state->bits;
* lcode = state->lencode;
* dcode = state->distcode;
* lmask = ( 1 << state->lenbits ) - 1;
* dmask = ( 1 << state->distbits ) - 1;
*/
/* align in_r on long boundary */
#if defined( NO_MMX )
#endif
#if defined( USE_MMX )
#endif
/*** Runtime MMX check ***/
#if defined( RUN_TIME_MMX )
* to see if cpu supports cpuid...
* ID bit method not supported by NexGen but
* bios may load a cpuid instruction and
* cpuid may be disabled on Cyrix 5-6x86 */
cpuid /* get cpu features */
#endif
/*** Non-MMX code ***/
#if defined ( NO_MMX ) || defined( RUN_TIME_MMX )
/* while (in < last && out < end)
*/
/* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
*
* do {
* if (bits < 15) {
* hold |= *((unsigned short *)in)++ << bits;
* bits += 16
* }
* this = lcode[hold & lmask]
*/
lodsw /* al = *(ushort *)in++ */
.L_dolen:
/* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out
*
* dolen:
* bits -= this.bits;
* hold >>= this.bits
*/
/* check if op is a literal
* if (op == 0) {
* PUP(out) = this.val;
* }
*/
/* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = len
*
* else if (op & 16) {
* len = this.val
* op &= 15
* if (op) {
* if (op > bits) {
* hold |= *((unsigned short *)in)++ << bits;
* bits += 16
* }
* len += hold & mask[op];
* bits -= op;
* hold >>= op;
* }
*/
lodsw /* al = *(ushort *)in++ */
/* regs: %esi = in, %ebp = hold, %bl = bits, %edi = out, %edx = dist
*
* if (bits < 15) {
* hold |= *((unsigned short *)in)++ << bits;
* bits += 16
* }
* this = dcode[hold & dmask];
* dodist:
* bits -= this.bits;
* hold >>= this.bits;
* op = this.op;
*/
lodsw /* al = *(ushort *)in++ */
.L_dodist:
/* if (op & 16) {
* dist = this.val
* op &= 15
* if (op > bits) {
* hold |= *((unsigned short *)in)++ << bits;
* bits += 16
* }
* dist += hold & mask[op];
* bits -= op;
* hold >>= op;
*/
lodsw /* al = *(ushort *)in++ */
/* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
* %ecx = nbytes
*
* nbytes = out - beg;
* if (dist <= nbytes) {
* from = out - dist;
* do {
* PUP(out) = PUP(from);
* } while (--len > 0) {
* }
*/
/* else if ((op & 64) == 0) {
* this = lcode[this.val + (hold & mask[op])];
* }
*/
/* else if ((op & 64) == 0) {
* this = dcode[this.val + (hold & mask[op])];
* }
*/
/* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
* %ecx = nbytes
*
* else {
* if (dist > wsize) {
* invalid distance
* }
* from = window;
* nbytes = dist - nbytes;
* if (write == 0) {
* from += wsize - nbytes;
*/
/* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
* %ecx = nbytes, %eax = len
*
* if (nbytes < len) {
* len -= nbytes;
* do {
* PUP(out) = PUP(from);
* } while (--nbytes);
* from = out - dist;
* }
* }
*/
/* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
* %ecx = nbytes, %eax = write, %eax = len
*
* else if (write < nbytes) {
* from += wsize + write - nbytes;
* nbytes -= write;
* if (nbytes < len) {
* len -= nbytes;
* do {
* PUP(out) = PUP(from);
* } while (--nbytes);
* from = window;
* nbytes = write;
* if (nbytes < len) {
* len -= nbytes;
* do {
* PUP(out) = PUP(from);
* } while(--nbytes);
* from = out - dist;
* }
* }
* }
*/
/* regs: %esi = from, %ebp = hold, %bl = bits, %edi = out, %edx = dist
* %ecx = nbytes, %eax = write, %eax = len
*
* else {
* from += write - nbytes;
* if (nbytes < len) {
* len -= nbytes;
* do {
* PUP(out) = PUP(from);
* } while (--nbytes);
* from = out - dist;
* }
* }
*/
/* regs: %esi = from, %esi = in, %ebp = hold, %bl = bits, %edi = out
* %eax = len
*
* while (len > 0) {
* PUP(out) = PUP(from);
* len--;
* }
* }
* } while (in < last && out < end);
*/
#endif /* NO_MMX || RUN_TIME_MMX */
/*** MMX code ***/
#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
/* while (in < last && out < end)
*/
#endif /* USE_MMX || RUN_TIME_MMX */
/*** USE_MMX, NO_MMX, and RUNTIME_MMX from here on ***/
/* else {
* strm->msg = "invalid distance code";
* state->mode = BAD;
* }
*/
/* else if (op & 32) {
* state->mode = TYPE;
* break;
* }
*/
/* else {
* state->mode = BAD;
* }
*/
/* strm->msg = "invalid distance too far back";
* state->mode = BAD;
*/
/* set strm->msg = %ecx, strm->state->mode = %edx */
/*
* Regs:
*
* bits = %ebp when mmx, and in %ebx when non-mmx
* hold = %hold_mm when mmx, and in %ebp when non-mmx
* in = %esi
* out = %edi
*/
#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
#if defined( RUN_TIME_MMX )
#endif /* RUN_TIME_MMX */
#endif
/* len = bits >> 3;
* in -= len;
* bits -= len << 3;
* hold &= (1U << bits) - 1;
* state->hold = hold;
* state->bits = bits;
* strm->next_in = in;
* strm->next_out = out;
*/
#if defined( USE_MMX ) || defined( RUN_TIME_MMX )
#if defined( RUN_TIME_MMX )
#endif /* RUN_TIME_MMX */
#endif /* USE_MMX || RUN_TIME_MMX */
/* strm->avail_in = in < last ? 11 + (last - in) : 11 - (in - last) */
/* strm->avail_out = out < end ? 257 + (end - out) : 257 - (out - end)*/
.L_done:
#if defined( GAS_ELF )
/* elf info */
#endif