blob: 979f699eeeb83c87034434cc200fe8bdb7fc7a7d [file] [log] [blame]
/*
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/****************************************************************************
*
* This file provides the decompress() and decompress16() functions
* which can be called in order to decompress an LZMA-compressed
* image. The code is modelled on the public-domain "XZ Embedded"
* implementation as used by the Linux kernel. Symbol names are
* chosen to match the XZ Embedded implementation where possible, for
* ease of reference.
*
* This code is optimised for size rather than speed, since the amount
* of data to be decompressed is trivially small by modern standards.
*
* The same basic assembly code is used to compile both decompress()
* and decompress16().
*
* Note that these functions require large amounts of stack space.
*
****************************************************************************
*/
.text
.arch i486
.section ".prefix.lib", "ax", @progbits
#ifdef CODE16
#define ADDR16
#define ADDR32 addr32
#define decompress decompress16
.code16
#else /* CODE16 */
#define ADDR16 addr16
#define ADDR32
.code32
#endif /* CODE16 */
#define CRCPOLY 0xedb88320
#define CRCSEED 0xffffffff
/****************************************************************************
* Debugging
****************************************************************************
*
* This code will usually run in 16-bit protected mode, in which case
* only the 0xe9 debug port (present on some virtual machines) can be
* used.
*
* To debug on real hardware, build with DEBUG=libprefix. This will
* cause this code to be called in flat real mode, and so DEBUG_INT10
* may be used.
*/
/* Enable debugging via 0xe9 debug port */
#define DEBUG_E9 0
/* Enable debugging via BIOS INT 10 (works only when in flat real mode) */
#define DEBUG_INT10 0
#if ( DEBUG_E9 || DEBUG_INT10 )
.macro print_character, reg
pushfl
pushw %ax
pushw %bx
pushw %bp
movb \reg, %al
movw $0x0007, %bx
movb $0x0e, %ah
#if DEBUG_E9
outb %al, $0xe9
#endif
#if DEBUG_INT10
cmpb $('\n'), %al
jne L\@
int $0x10
movb $('\r'), %al
L\@: int $0x10
#endif
popw %bp
popw %bx
popw %ax
popfl
.endm
.macro print_hex_nibble
pushfl
pushw %ax
cmpb $10, %al
sbb $0x69, %al
das
print_character %al
popw %ax
popfl
.endm
.macro print_hex_byte, reg
pushfl
pushw %ax
movb \reg, %al
pushw %ax
shrb $4, %al
print_hex_nibble
popw %ax
andb $0x0f, %al
print_hex_nibble
popw %ax
popfl
.endm
.macro print_hex_word, reg
pushw %ax
movw \reg, %ax
print_hex_byte %ah
print_hex_byte %al
popw %ax
.endm
.macro print_hex_dword, reg
pushl %eax
movl \reg, %eax
rorl $16, %eax
print_hex_word %ax
rorl $16, %eax
print_hex_word %ax
popl %eax
.endm
#else
.macro print_character, char
.endm
.macro print_hex_byte, reg
.endm
.macro print_hex_word, reg
.endm
.macro print_hex_dword, reg
.endm
#endif
/****************************************************************************
* LZMA parameters and data structures
****************************************************************************
*/
/* LZMA decompressor states (as used in XZ Embedded) */
#define STATE_LIT_LIT 0x00
#define STATE_MATCH_LIT_LIT 0x01
#define STATE_REP_LIT_LIT 0x02
#define STATE_SHORTREP_LIT_LIT 0x03
#define STATE_MATCH_LIT 0x04
#define STATE_REP_LIT 0x05
#define STATE_SHORTREP_LIT 0x06
#define STATE_LIT_MATCH 0x07
#define STATE_LIT_LONGREP 0x08
#define STATE_LIT_SHORTREP 0x09
#define STATE_NONLIT_MATCH 0x0a
#define STATE_NONLIT_REP 0x0b
/* LZMA maximum decompressor state in which most recent symbol was a literal */
#define STATE_LIT_MAX 0x06
/* LZMA number of literal context bits ("lc=" parameter) */
#define LZMA_LC 2
.struct 0
lzma_len_dec:
choice: .word 0
choice2: .word 0
low: .rept ( 1 << 3 )
.word 0
.endr
mid: .rept ( 1 << 3 )
.word 0
.endr
high: .rept ( 1 << 8 )
.word 0
.endr
.equ sizeof__lzma_len_dec, . - lzma_len_dec
.previous
.struct 0
lzma_dec:
out_start: .long 0
rc_code: .long 0
rc_range: .long 0
len: .word 0
reps:
rep0: .long 0
rep1: .long 0
rep2: .long 0
rep3: .long 0
probs:
is_match: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
is_rep: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
is_rep0: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
is_rep1: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
is_rep2: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
is_rep0_long: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dist_slot: .rept ( 4 * ( 1 << 6 ) )
.word 0
.endr
dist_special: .rept ( ( 1 << ( 14 / 2 ) ) - 14 )
.word 0
.endr
dist_align: .rept ( 1 << 4 )
.word 0
.endr
match_len_dec: .space sizeof__lzma_len_dec
rep_len_dec: .space sizeof__lzma_len_dec
literal: .rept ( ( 1 << LZMA_LC ) * 0x300 )
.word 0
.endr
.balign 4
.equ sizeof__lzma_dec, . - lzma_dec
.previous
/* Some binutils versions seem not to handle .struct/.previous */
.section ".prefix.lib", "ax", @progbits
/*****************************************************************************
* Normalise range encoder
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %eax : current range
*****************************************************************************
*/
rc_normalise:
/* Check if rc_range is less than 1<<24 */
testb $0xff, (rc_range+3)(%ebp)
jnz 1f
/* If it is, shift in a new byte from the compressed input data */
shll $8, rc_range(%ebp)
shll $8, rc_code(%ebp)
ADDR32 lodsb
movb %al, (rc_code+0)(%ebp)
1: /* Return current range */
movl rc_range(%ebp), %eax
ret
.size rc_normalise, . - rc_normalise
/*****************************************************************************
* Decode single range-encoded bit using a probability estimate
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %ebx : probability estimate pointer (offset from %ebp)
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* CF : decoded bit
* ZF : inverse of decoded bit
* Corrupts:
* none
*****************************************************************************
*/
rc_bit:
/* Preserve registers */
pushl %eax
pushl %edx
/* Perform normalisation */
call rc_normalise
/* Calculate bound in %eax and probability estimate in %dx */
shrl $11, %eax
movzwl (%ebp,%ebx), %edx
mul %edx /* will zero %edx */
movw (%ebp,%ebx), %dx
/* Compare code against bound */
cmpl %eax, rc_code(%ebp)
jae 2f
1: /* Code is less than bound */
movl %eax, rc_range(%ebp)
negw %dx
addw $(1<<11), %dx
shrw $5, %dx
addw %dx, (%ebp,%ebx)
xorw %ax, %ax /* Clear CF, set ZF */
jmp 99f
2: /* Code is greater than or equal to bound */
subl %eax, rc_range(%ebp)
subl %eax, rc_code(%ebp)
shrw $5, %dx
subw %dx, (%ebp,%ebx)
incw %dx /* Clear ZF (%dx is 11-bit; can never wrap) */
stc /* Set CF */
99: /* Restore registers and return */
popl %edx
popl %eax
ret
.size rc_bit, . - rc_bit
/*****************************************************************************
* Decode MSB-first bittree
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %ebx : probability estimate set pointer (offset from %ebp)
* %cx : number of bits to decode
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %eax : decoded bittree
* Corrupts:
* none
*****************************************************************************
*/
rc_bittree:
/* Preserve registers */
pushl %edi
pushw %cx
movl %ebx, %edi
/* Initialise registers */
movl $1, %eax
1: /* Decode bit */
leaw (%edi,%eax,2), %bx /* high word always zero anyway */
call rc_bit
rclw %ax
ADDR16 loop 1b
/* Restore registers, clear unwanted high bit of result, and return */
movl %edi, %ebx
popw %cx
popl %edi
btrw %cx, %ax
ret
.size rc_bittree, . - rc_bittree
/*****************************************************************************
* Decode LSB-first bittree
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %ebx : probability estimate set pointer (offset from %ebp)
* %cx : number of bits to decode
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %eax : decoded bittree
* Corrupts:
* none
*****************************************************************************
*/
rc_bittree_reverse:
/* Preserve registers */
pushw %cx
/* Decode bittree */
call rc_bittree
1: /* Reverse result */
rcrb %al
rclb %ah
ADDR16 loop 1b
shrw $8, %ax
/* Restore registers and return */
popw %cx
ret
.size rc_bittree_reverse, . - rc_bittree_reverse
/*****************************************************************************
* Decode MSB-first bittree with optional match byte
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %ebx : probability estimate set pointer (offset from %ebp)
* %cl : match byte
* %ch : 1 to use match byte, 0 to ignore match byte
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %eax : decoded bittree
* Corrupts:
* none
*****************************************************************************
*/
rc_bittree_match:
/* Preserve registers */
pushl %edi
pushw %cx
pushw %dx
movl %ebx, %edi
/* Initialise registers */
movl $1, %eax
1: /* Decode bit */
rolb $1, %cl
movw %cx, %dx
andb %dh, %dl /* match_bit in %dl */
movw %dx, %bx
addb %bl, %bh
xorb %bl, %bl
addw %ax, %bx /* offset + match_bit + symbol */
leaw (%edi,%ebx,2), %bx /* high word always zero anyway */
call rc_bit
rclw %ax
movb %al, %dh
notb %dh
xorb %dh, %dl
andb %dl, %ch /* offset &= ( match_bit ^ bit ) */
testb %ah, %ah
jz 1b
/* Restore registers, clear unwanted high bit of result, and return */
movl %edi, %ebx
popw %dx
popw %cx
popl %edi
xorb %ah, %ah
ret
.size rc_bittree_match, . - rc_bittree_match
/*****************************************************************************
* Decode direct bits (no probability estimates)
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %cx : number of bits to decode
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %eax : decoded bits
* Corrupts:
* none
*****************************************************************************
*/
rc_direct:
/* Preserve registers */
pushl %ebx
pushw %cx
pushl %edx
/* Initialise registers */
xorl %edx, %edx
1: /* Perform normalisation */
call rc_normalise
/* Decode bit */
shrl $1, %eax
movl %eax, rc_range(%ebp)
movl rc_code(%ebp), %ebx
subl %eax, %ebx
js 2f
movl %ebx, rc_code(%ebp)
2: rcll %ebx
rcll %edx
xorb $1, %dl
ADDR16 loop 1b
/* Restore registers and return */
movl %edx, %eax
popl %edx
popw %cx
popl %ebx
ret
.size rc_direct, . - rc_direct
/*****************************************************************************
* Decode an LZMA literal
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %es:%edi : uncompressed output data pointer (updated)
* %edx : LZMA state
* CF : end of payload marker found (always zero)
* Corrupts:
* %eax
* %ebx
* %ecx
*****************************************************************************
*
* Literals are coded as an eight-bit tree, using a match byte if the
* previous symbol was not a literal.
*
*/
lzma_literal:
/* Get most recent output byte, if available */
xorl %ebx, %ebx
cmpl %edi, out_start(%ebp)
je 1f
movb %es:-1(%edi), %bh
1: /* Locate probability estimate set */
shrb $( 8 - LZMA_LC ), %bh
shlb $1, %bh
leaw literal(%ebx,%ebx,2), %bx
/* Get match byte, if applicable */
xorw %cx, %cx
cmpb $STATE_LIT_MAX, %dl
jbe 1f
movl rep0(%ebp), %eax
notl %eax
movb %es:(%edi,%eax), %cl
movb $1, %ch
1: /* Decode bittree */
call rc_bittree_match
/* Store output byte */
ADDR32 stosb
print_hex_byte %al
print_character $(' ')
/* Update LZMA state */
subb $3, %dl
jns 1f
xorb %dl, %dl
1: cmpb $7, %dl
jb 1f
subb $3, %dl
1: /* Clear CF and return */
clc
ret
.size lzma_literal, . - lzma_literal
/*****************************************************************************
* Decode an LZMA length
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %ebx : length parameter pointer (offset from %ebp)
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* Corrupts:
* %ebx
*****************************************************************************
*
* Lengths are encoded as:
*
* "0" + 3 bits : lengths 2-9 ("low")
* "10" + 3 bits : lengths 10-17 ("mid")
* "11" + 8 bits : lengths 18-273 ("high")
*/
lzma_len:
/* Preserve registers */
pushl %eax
pushl %ecx
pushl %edi
movl %ebx, %edi
/* Start by assuming three bits and a base length of 2 */
movw $3, %cx
movw $2, len(%ebp)
/* Check low-length choice bit */
leal choice(%edi), %ebx
call rc_bit
leal low(%edi), %ebx
jz 1f
/* Check high-length choice bit */
leal choice2(%edi), %ebx
call rc_bit
leal mid(%edi), %ebx
movb $10, len(%ebp)
jz 1f
leal high(%edi), %ebx
movb $8, %cl
movb $18, len(%ebp)
1: /* Get encoded length */
call rc_bittree
addw %ax, len(%ebp)
/* Restore registers and return */
movl %edi, %ebx
popl %edi
popl %ecx
popl %eax
ret
.size lzma_len, . - lzma_len
/*****************************************************************************
* Copy (possibly repeated) matched data
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %es:%edi : uncompressed output data pointer
* %cl : repeated match distance index (for repeated matches)
* %eax : match distance (for non-repeated matches)
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %es:%edi : uncompressed output data pointer
* CF : match distance is out of range
* Corrupts:
* %eax
* %ebx
* %ecx
*****************************************************************************
*/
match: /* Update repeated match list */
print_character $('[')
movl $3, %ecx
jmp 1f
match_rep:
print_character $('[')
print_character $('R')
print_hex_byte %cl
print_character $('=')
movzbl %cl, %ecx
movl reps(%ebp,%ecx,4), %eax
jcxz 2f
1: movl (reps-4)(%ebp,%ecx,4), %ebx
movl %ebx, reps(%ebp,%ecx,4)
loop 1b
movl %eax, rep0(%ebp)
2: /* Preserve registers */
pushl %esi
/* Get stored match length */
movzwl len(%ebp), %ecx
print_hex_dword %eax
print_character $('+')
print_hex_word %cx
print_character $(']')
print_character $(' ')
/* Abort with CF set if match distance is out of range */
movl out_start(%ebp), %esi
negl %esi
leal -1(%edi,%esi), %esi
cmpl %eax, %esi
jc 99f
/* Perform copy */
notl %eax
leal (%edi,%eax), %esi
ADDR32 es rep movsb
99: /* Restore registers and return */
popl %esi
ret
.size match, . - match
/*****************************************************************************
* Decode an LZMA match
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* CF : end of payload marker found
* Corrupts:
* %eax
* %ebx
* %ecx
*****************************************************************************
*
* Matches are encoded as an LZMA length followed by a 6-bit "distance
* slot" code, 0-26 fixed-probability bits, and 0-5 context encoded
* bits.
*/
lzma_match:
/* Preserve registers */
pushl %edi
/* Update LZMA state */
cmpb $STATE_LIT_MAX, %dl
movb $STATE_LIT_MATCH, %dl
jbe 1f
movb $STATE_NONLIT_MATCH, %dl
1: /* Decode length */
movl $match_len_dec, %ebx
call lzma_len
/* Decode distance slot */
movw len(%ebp), %bx
subw $2, %bx
cmpw $4, %bx
jb 1f
movw $3, %bx
1: shlw $7, %bx
addw $dist_slot, %bx
movw $6, %cx
call rc_bittree
/* Distance slots 0-3 are literal distances */
cmpb $4, %al
jb 99f
/* Determine initial bits: 10/11 for even/odd distance codes */
movl %eax, %edi
andw $1, %di
orw $2, %di
/* Determine number of context-encoded bits */
movw %ax, %cx
shrb $1, %cl
decb %cl
/* Select context to be used in absence of fixed-probability bits */
movl %edi, %ebx
shlw %cl, %bx
subw %ax, %bx
leaw (dist_special-2)(%ebx,%ebx), %bx
/* Decode fixed-probability bits, if any */
cmpb $6, %cl
jb 1f
subb $4, %cl
shll %cl, %edi
call rc_direct
orl %eax, %edi
/* Select context to be used in presence of fixed-probability bits */
movb $4, %cl
movl $dist_align, %ebx
1: /* Decode context-encoded bits */
shll %cl, %edi
call rc_bittree_reverse
orl %edi, %eax
99: /* Restore registers and tail-call */
popl %edi
jmp match
.size lzma_match, . - lzma_match
/*****************************************************************************
* Decode an LZMA repeated match
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* CF : end of payload marker found
* Corrupts:
* %eax
* %ebx
* %ecx
*****************************************************************************
*
* Repeated matches are encoded as:
*
* "00" : shortrep0 (implicit length 1)
* "01" + len : longrep0
* "10" + len : longrep1
* "110" + len : longrep2
* "111" + len : longrep3
*/
lzma_rep_match:
/* Initially assume longrep0 */
movw $(STATE_LIT_LONGREP << 8), %cx
/* Get is_rep0 bit */
leal is_rep0(,%edx,2), %ebx
call rc_bit
jnz 1f
/* Get is_rep0_long bit */
leal is_rep0_long(,%edx,2), %ebx
call rc_bit
jnz 98f
movw $1, len(%ebp)
movb $STATE_LIT_SHORTREP, %ch
jmp 99f
1: /* Get is_rep1 bit */
incb %cl
leal is_rep1(,%edx,2), %ebx
call rc_bit
jz 98f
/* Get is_rep2 bit */
incb %cl
leal is_rep2(,%edx,2), %ebx
call rc_bit
adcb $0, %cl
98: /* Decode length */
movl $rep_len_dec, %ebx
call lzma_len
99: /* Update LZMA state */
cmpb $STATE_LIT_MAX, %dl
movb %ch, %dl
jbe 1f
movb $STATE_NONLIT_REP, %dl
1: /* Tail call */
jmp match_rep
.size lzma_match, . - lzma_match
/*****************************************************************************
* Decode one LZMA symbol
*
* Parameters:
* %ss:%ebp : LZMA parameter block
* %ds:%esi : compressed input data pointer
* %es:%edi : uncompressed output data pointer
* %edx : LZMA state
* Returns:
* %ds:%esi : compressed input data pointer (possibly updated)
* %es:%edi : uncompressed output data pointer (updated)
* %edx : LZMA state
* CF : end of payload marker found
* Corrupts:
* %eax
* %ebx
* %ecx
*****************************************************************************
*/
lzma_decode:
/* Get is_match bit */
leal is_match(,%edx,2), %ebx
call rc_bit
jz lzma_literal
/* Get is_rep bit */
leal is_rep(,%edx,2), %ebx
call rc_bit
jz lzma_match
jmp lzma_rep_match
.size lzma_decode, . - lzma_decode
/****************************************************************************
* Undo effect of branch-call-jump (BCJ) filter
*
* Parameters:
* %es:%esi : start of uncompressed output data (note %es)
* %es:%edi : end of uncompressed output data
* Returns:
* Corrupts:
* %eax
* %ebx
* %ecx
* %edx
* %esi
*****************************************************************************
*/
bcj_filter:
/* Store (negative) start of data in %edx */
movl %esi, %edx
negl %edx
/* Calculate limit in %ecx */
leal -5(%edi,%edx), %ecx
1: /* Calculate offset in %ebx */
leal (%esi,%edx), %ebx
/* Check for end of data */
cmpl %ecx, %ebx
ja 99f
/* Check for an opcode which would be followed by a rel32 address */
ADDR32 es lodsb
andb $0xfe, %al
cmpb $0xe8, %al
jne 1b
/* Get current jump target value in %eax */
ADDR32 es lodsl
/* Convert absolute addresses in the range [0,limit) back to
* relative addresses in the range [-offset,limit-offset).
*/
cmpl %ecx, %eax
jae 2f
subl %ebx,%es:-4(%esi)
2: /* Convert negative numbers in the range [-offset,0) back to
* positive numbers in the range [limit-offset,limit).
*/
notl %eax /* Range is now [0,offset) */
cmpl %ebx, %eax
jae 1b
addl %ecx,%es:-4(%esi)
jmp 1b
99: /* Return */
ret
.size bcj_filter, . - bcj_filter
/****************************************************************************
* Verify CRC32
*
* Parameters:
* %ds:%esi : Start of compressed input data
* %edx : Length of compressed input data (including CRC)
* Returns:
* CF clear if CRC32 is zero
* All other registers are preserved
* Corrupts:
* %eax
* %ebx
* %ecx
* %edx
* %esi
****************************************************************************
*/
verify_crc32:
/* Calculate CRC */
addl %esi, %edx
movl $CRCSEED, %ebx
1: ADDR32 lodsb
xorb %al, %bl
movw $8, %cx
2: rcrl %ebx
jnc 3f
xorl $CRCPOLY, %ebx
3: ADDR16 loop 2b
cmpl %esi, %edx
jne 1b
/* Set CF if result is nonzero */
testl %ebx, %ebx
jz 1f
stc
1: /* Return */
ret
.size verify_crc32, . - verify_crc32
/****************************************************************************
* decompress (real-mode or 16/32-bit protected-mode near call)
*
* Decompress data
*
* Parameters (passed via registers):
* %ds:%esi : Start of compressed input data
* %es:%edi : Start of output buffer
* Returns:
* %ds:%esi - End of compressed input data
* %es:%edi - End of decompressed output data
* CF set if CRC32 was incorrect
* All other registers are preserved
*
* NOTE: It would be possible to build a smaller version of the
* decompression code for -DKEEP_IT_REAL by using 16-bit registers
* where possible.
****************************************************************************
*/
.globl decompress
decompress:
/* Preserve registers */
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %ebp
/* Verify CRC32 */
ADDR32 lodsl
movl %eax, %edx
pushl %esi
call verify_crc32
popl %esi
jc 99f
/* Allocate parameter block */
subl $sizeof__lzma_dec, %esp
movl %esp, %ebp
/* Zero parameter block and set all probabilities to 0.5 */
pushl %edi
pushw %es
pushw %ss
popw %es
movl %ebp, %edi
xorl %eax, %eax
movl $( sizeof__lzma_dec / 4 ), %ecx
ADDR32 rep stosl
leal probs(%ebp), %edi
movw $( ( 1 << 11 ) / 2 ), %ax
movl $( ( sizeof__lzma_dec - probs ) / 2 ), %ecx
ADDR32 rep stosw
popw %es
popl %edi
/* Initialise remaining parameters */
movl %edi, out_start(%ebp)
print_character $('\n')
ADDR32 lodsb /* discard initial byte */
print_hex_byte %al
ADDR32 lodsl
bswapl %eax
print_hex_dword %eax
print_character $('\n')
movl %eax, rc_code(%ebp)
decl rc_range(%ebp)
movl $STATE_LIT_LIT, %edx
1: /* Decompress until we reach end of buffer */
call lzma_decode
jnc 1b
call rc_normalise
print_character $('\n')
/* Undo BCJ filter */
pushl %esi
movl out_start(%ebp), %esi
call bcj_filter
popl %esi
/* Skip CRC */
ADDR32 lodsl
/* Free parameter block (and clear CF) */
addl $sizeof__lzma_dec, %esp
99: /* Restore registers and return */
popl %ebp
popl %edx
popl %ecx
popl %ebx
popl %eax
ret
/* Specify minimum amount of stack space required */
.globl _min_decompress_stack
.equ _min_decompress_stack, ( sizeof__lzma_dec + 512 /* margin */ )