| /* |
| * 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. |
| * |
| **************************************************************************** |
| */ |
| |
| .section ".note.GNU-stack", "", @progbits |
| .code32 |
| .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 */ ) |