/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2008  Free Software Foundation, Inc.
 *
 *  GRUB 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#define FIXED_PROPS

#define LZMA_BASE_SIZE 1846
#define LZMA_LIT_SIZE 768

#define LZMA_PROPERTIES_SIZE 5

#define kNumTopBits 24
#define kTopValue (1 << kNumTopBits)

#define kNumBitModelTotalBits 11
#define kBitModelTotal (1 << kNumBitModelTotalBits)
#define kNumMoveBits 5


#define kNumPosBitsMax 4
#define kNumPosStatesMax (1 << kNumPosBitsMax)

#define kLenNumLowBits 3
#define kLenNumLowSymbols (1 << kLenNumLowBits)
#define kLenNumMidBits 3
#define kLenNumMidSymbols (1 << kLenNumMidBits)
#define kLenNumHighBits 8
#define kLenNumHighSymbols (1 << kLenNumHighBits)

#define LenChoice 0
#define LenChoice2 (LenChoice + 1)
#define LenLow (LenChoice2 + 1)
#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
#define kNumLenProbs (LenHigh + kLenNumHighSymbols)


#define kNumStates 12
#define kNumLitStates 7

#define kStartPosModelIndex 4
#define kEndPosModelIndex 14
#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))

#define kNumPosSlotBits 6
#define kNumLenToPosStates 4

#define kNumAlignBits 4
#define kAlignTableSize (1 << kNumAlignBits)

#define kMatchMinLen 2

#define IsMatch 0
#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
#define IsRepG0 (IsRep + kNumStates)
#define IsRepG1 (IsRepG0 + kNumStates)
#define IsRepG2 (IsRepG1 + kNumStates)
#define IsRep0Long (IsRepG2 + kNumStates)
#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
#define LenCoder (Align + kAlignTableSize)
#define RepLenCoder (LenCoder + kNumLenProbs)
#define Literal (RepLenCoder + kNumLenProbs)


#if 0

DbgOut:
	pushf
	pushl	%ebp
	pushl	%edi
	pushl	%esi
	pushl	%edx
	pushl	%ecx
	pushl	%ebx
	pushl	%eax

	call	_DebugPrint

	popl	%eax
	popl	%ebx
	popl	%ecx
	popl	%edx
	popl	%esi
	popl	%edi
	popl	%ebp
	popf

	ret


/*
 * int LzmaDecodeProperties(CLzmaProperties *propsRes,
 *                          const unsigned char *propsData,
 *                          int size);
 */

_LzmaDecodePropertiesA:
	movb	(%edx), %dl

	xorl	%ecx, %ecx
1:
	cmpb	$45, %dl
	jb	2f
	incl	%ecx
	subb	$45, %dl
	jmp	1b
2:
	movl	%ecx, 8(%eax)		/* pb */
	xorl	%ecx, %ecx
1:
	cmpb	$9, %dl
	jb	2f
	incl	%ecx
	subb	$9, %dl
2:
	movl	%ecx, 4(%eax)		/* lp */
	movb	%dl, %cl
	movl	%ecx, (%eax)		/* lc */

#endif

#ifndef ASM_FILE
	xorl	%eax, %eax
#endif
	ret

#define out_size	8(%ebp)

#define now_pos		-4(%ebp)
#define prev_byte	-8(%ebp)
#define range		-12(%ebp)
#define code		-16(%ebp)
#define state		-20(%ebp)
#define rep0		-24(%ebp)
#define rep1		-28(%ebp)
#define rep2		-32(%ebp)
#define rep3		-36(%ebp)

#ifdef FIXED_PROPS

#define FIXED_LC	3
#define FIXED_LP	0
#define FIXED_PB	2

#define POS_STATE_MASK	((1 << (FIXED_PB)) - 1)
#define LIT_POS_MASK	((1 << (FIXED_LP)) - 1)

#define LOCAL_SIZE	36

#else

#define lc		(%ebx)
#define lp		4(%ebx)
#define pb		8(%ebx)
#define probs		12(%ebx)

#define pos_state_mask	-40(%ebp)
#define lit_pos_mask	-44(%ebp)

#define LOCAL_SIZE	44

#endif

RangeDecoderBitDecode:
#ifdef FIXED_PROPS
	leal	(%ebx, %eax, 4), %eax
#else
	shll	$2, %eax
	addl	probs, %eax
#endif

	movl	%eax, %ecx
	movl	(%ecx), %eax

	movl	range, %edx
	shrl	$kNumBitModelTotalBits, %edx
	mull	%edx

	cmpl	code, %eax
	jbe	1f

	movl	%eax, range
	movl	$kBitModelTotal, %edx
	subl	(%ecx), %edx
	shrl	$kNumMoveBits, %edx
	addl	%edx, (%ecx)
	clc
3:
	pushf
	cmpl	$kTopValue, range
	jnc	2f
	shll	$8, code
	lodsb
	movb	%al, code
	shll	$8, range
2:
	popf
	ret
1:
	subl	%eax, range
	subl	%eax, code
	movl	(%ecx), %edx
	shrl	$kNumMoveBits, %edx
	subl	%edx, (%ecx)
	stc
	jmp	3b

RangeDecoderBitTreeDecode:
RangeDecoderReverseBitTreeDecode:
	movzbl	%cl, %ecx
	xorl	%edx, %edx
	pushl	%edx
	incl	%edx
	pushl	%edx

1:
	pushl	%eax
	pushl	%ecx
	pushl	%edx

	addl	%edx, %eax
	call	RangeDecoderBitDecode

	popl	%edx
	popl	%ecx

	jnc	2f
	movl	4(%esp), %eax
	orl	%eax, 8(%esp)
	stc

2:
	adcl	%edx, %edx
	popl	%eax

	shll	$1, (%esp)
	loop	1b

	popl	%ecx
	subl	%ecx, %edx		/* RangeDecoderBitTreeDecode */
	popl	%ecx			/* RangeDecoderReverseBitTreeDecode */
	ret

LzmaLenDecode:
	pushl	%eax
	addl	$LenChoice, %eax
	call	RangeDecoderBitDecode
	popl	%eax
	jc	1f
	pushl	$0
	movb	$kLenNumLowBits, %cl
	addl	$LenLow, %eax
2:
	movl	12(%esp), %edx
	shll	%cl, %edx
	addl	%edx, %eax
3:

	call	RangeDecoderBitTreeDecode
	popl	%eax
	addl	%eax, %edx
	ret

1:
	pushl	%eax
	addl	$LenChoice2, %eax
	call	RangeDecoderBitDecode
	popl	%eax
	jc	1f
	pushl	$kLenNumLowSymbols
	movb	$kLenNumMidBits, %cl
	addl	$LenMid, %eax
	jmp	2b

1:
	pushl	$(kLenNumLowSymbols + kLenNumMidSymbols)
	addl	$LenHigh, %eax
	movb	$kLenNumHighBits, %cl
	jmp	3b

WriteByte:
	movb	%al, prev_byte
	stosb
	incl	now_pos
	ret

/*
 * int LzmaDecode(CLzmaDecoderState *vs,
 *                const unsigned char *inStream,
 *                unsigned char *outStream,
 *                SizeT outSize);
 */

_LzmaDecodeA:

	pushl	%ebp
	movl	%esp, %ebp
	subl	$LOCAL_SIZE, %esp

#ifndef ASM_FILE
	pushl	%esi
	pushl	%edi
	pushl	%ebx

	movl	%eax, %ebx
	movl	%edx, %esi
	pushl	%ecx
#else
	pushl	%edi
#endif

	cld

#ifdef FIXED_PROPS
	movl	%ebx, %edi
	movl	$(Literal + (LZMA_LIT_SIZE << (FIXED_LC + FIXED_LP))), %ecx
#else
	movl	$LZMA_LIT_SIZE, %eax
	movb	lc, %cl
	addb	lp, %cl
	shll	%cl, %eax
	addl	$Literal, %eax
	movl	%eax, %ecx
	movl	probs, %edi
#endif

	movl	$(kBitModelTotal >> 1), %eax

	rep
	stosl

	popl	%edi

	xorl	%eax, %eax
	movl	%eax, now_pos
	movl	%eax, prev_byte
	movl	%eax, state

	incl	%eax
	movl	%eax, rep0
	movl	%eax, rep1
	movl	%eax, rep2
	movl	%eax, rep3

#ifndef FIXED_PROPS
	movl	%eax, %edx
	movb	pb, %cl
	shll	%cl, %edx
	decl	%edx
	movl	%edx, pos_state_mask

	movl	%eax, %edx
	movb	lp, %cl
	shll	%cl, %edx
	decl	%edx
	movl	%edx, lit_pos_mask;
#endif

	/* RangeDecoderInit */
	negl	%eax
	movl	%eax, range

	incl	%eax
	movb	$5, %cl

1:
	shll	$8, %eax
	lodsb
	loop	1b

	movl	%eax, code

lzma_decode_loop:
	movl	now_pos, %eax
	cmpl	out_size, %eax

	jb	1f

#ifndef ASM_FILE
	xorl	%eax, %eax

	popl	%ebx
	popl	%edi
	popl	%esi
#endif

	movl	%ebp, %esp
	popl	%ebp
	ret

1:
#ifdef FIXED_PROPS
	andl	$POS_STATE_MASK, %eax
#else
	andl	pos_state_mask, %eax
#endif
	pushl	%eax				/* posState */
	movl	state, %edx
	shll	$kNumPosBitsMax, %edx
	addl	%edx, %eax
	pushl	%eax				/* (state << kNumPosBitsMax) + posState */

	call	RangeDecoderBitDecode
	jc	1f

	movl	now_pos, %eax

#ifdef FIXED_PROPS
	andl	$LIT_POS_MASK, %eax
	shll	$FIXED_LC, %eax
	movl	prev_byte, %edx
	shrl	$(8 - FIXED_LC), %edx
#else
	andl	lit_pos_mask, %eax
	movb	lc, %cl
	shll	%cl, %eax
	negb	%cl
	addb	$8, %cl
	movl	prev_byte, %edx
	shrl	%cl, %edx
#endif

	addl	%edx, %eax
	movl	$LZMA_LIT_SIZE, %edx
	mull	%edx
	addl	$Literal, %eax
	pushl	%eax

	incl	%edx			/* edx = 1 */

	movl	rep0, %eax
	negl	%eax
	pushl	(%edi, %eax)		/* matchByte */

	cmpb	$kNumLitStates, state
	jb	5f

	/* LzmaLiteralDecodeMatch */

3:
	cmpl	$0x100, %edx
	jae	4f

	xorl	%eax, %eax
	shlb	$1, (%esp)
	adcl	%eax, %eax

	pushl	%eax
	pushl	%edx

	shll	$8, %eax
	leal	0x100(%edx, %eax), %eax
	addl	12(%esp), %eax
	call	RangeDecoderBitDecode

	setc	%al
	popl	%edx
	adcl	%edx, %edx

	popl	%ecx
	cmpb	%cl, %al
	jz	3b

5:

	/* LzmaLiteralDecode */

	cmpl	$0x100, %edx
	jae	4f

	pushl	%edx
	movl	%edx, %eax
	addl	8(%esp), %eax
	call	RangeDecoderBitDecode
	popl	%edx
	adcl	%edx, %edx
	jmp	5b

4:
	addl	$16, %esp

	movb	%dl, %al
	call	WriteByte

	movb	state, %al
	cmpb	$4, %al
	jae	2f
	xorb	%al, %al
	jmp	3f
2:
	subb	$3, %al
	cmpb	$7, %al
	jb	3f
	subb	$3, %al
3:
	movb	%al, state
	jmp	lzma_decode_loop

1:
	movl	state, %eax
	addl	$IsRep, %eax
	call	RangeDecoderBitDecode
	jnc	1f

	movl	state, %eax
	addl	$IsRepG0, %eax
	call	RangeDecoderBitDecode
	jc	10f

	movl	(%esp), %eax
	addl	$IsRep0Long, %eax
	call	RangeDecoderBitDecode
	jc	20f

	cmpb	$7, state
	movb	$9, state
	jb	100f
	addb	$2, state
100:

	movl	$1, %ecx

3:
	movl	rep0, %edx
	negl	%edx

4:
	movb	(%edi, %edx), %al
	call	WriteByte
	loop	4b

	popl	%eax
	popl	%eax
	jmp	lzma_decode_loop

10:
	movl	state, %eax
	addl	$IsRepG1, %eax
	call	RangeDecoderBitDecode
	movl	rep1, %edx
	jnc	100f

	movl	state, %eax
	addl	$IsRepG2, %eax
	call	RangeDecoderBitDecode
	movl	rep2, %edx
	jnc	1000f
	movl	rep2, %edx
	xchgl	rep3, %edx
1000:
	pushl	rep1
	popl	rep2
100:
	xchg	rep0, %edx
	movl	%edx, rep1
20:

	movl	$RepLenCoder, %eax
	call	LzmaLenDecode

	cmpb	$7, state
	movb	$8, state
	jb	100f
	addb	$3, state
100:
	jmp	2f

1:
	movl	rep0, %eax
	xchgl	rep1, %eax
	xchgl	rep2, %eax
	movl	%eax, rep3

	cmpb	$7, state
	movb	$7, state
	jb	10f
	addb	$3, state
10:

	movl	$LenCoder, %eax
	call	LzmaLenDecode
	pushl	%edx

	movl	$(kNumLenToPosStates - 1), %eax
	cmpl	%eax, %edx
	jbe	100f
	movl	%eax, %edx
100:
	movb	$kNumPosSlotBits, %cl
	shll	%cl, %edx
	leal	PosSlot(%edx), %eax
	call	RangeDecoderBitTreeDecode

	movl	%edx, rep0
	cmpl	$kStartPosModelIndex, %edx
	jb	100f

	movl	%edx, %ecx
	shrl	$1, %ecx
	decl	%ecx

	movzbl	%dl, %eax
	andb	$1, %al
	orb	$2, %al
	shll	%cl, %eax
	movl	%eax, rep0

	cmpl	$kEndPosModelIndex, %edx
	jae	200f
	movl	rep0, %eax
	addl	$(SpecPos - 1), %eax
	subl	%edx, %eax
	jmp	300f
200:

	subb	$kNumAlignBits, %cl

	/* RangeDecoderDecodeDirectBits */
	xorl	%edx, %edx

1000:
	shrl	$1, range
	shll	$1, %edx

	movl	range, %eax
	cmpl	%eax, code
	jb	2000f
	subl	%eax, code
	orb	$1, %dl
2000:

	cmpl	$kTopValue, %eax
	jae	3000f
	shll	$8, range
	shll	$8, code
	lodsb
	movb	%al, code

3000:
	loop	1000b

	movb	$kNumAlignBits, %cl
	shll	%cl, %edx
	addl	%edx, rep0

	movl	$Align, %eax

300:
	call	RangeDecoderReverseBitTreeDecode
	addl	%ecx, rep0

100:
	incl	rep0
	popl	%edx

2:

	addl	$kMatchMinLen, %edx
	movl	%edx, %ecx

	jmp	3b
