/* 
 * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
 *
 * This file 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.
 *
 * Originally this code was part of ucl the data compression library
 * for upx the ``Ultimate Packer of eXecutables''.
 *
 * - Converted to gas assembly, and refitted to work with etherboot.
 *   Eric Biederman 20 Aug 2002
 *
 * - Structure modified to be a subroutine call rather than an
 *   executable prefix.
 *   Michael Brown 30 Mar 2004
 */

#ifdef VBOX
#include "etherboot-asm.h"
#endif /* VBOX */

	.text
	.arch i386
#ifdef VBOX
	.section .prefix, "ax"
#else /* !VBOX */
	.section ".prefix", "ax", @progbits
#endif /* !VBOX */
	.code32
	
#ifdef VBOX
	.globl GSYM(decompress)
GSYM(decompress):
#else /* !VBOX */
	.globl decompress
decompress:
#endif /* !VBOX */
	/* Save the initial register values */
	pushal

	/*
	 * See where I am running, and compute %ebp
	 * %ebp holds delta between physical and virtual addresses.
	 */
	call	1f
1:	popl	%ebp
	subl	$1b, %ebp

	/* "compressed" and "decompress_to" defined by linker script */
	/* move compressed image up to temporary area before decompressing */
	std
#ifdef VBOX
	movl	$GSYM(_compressed_size), %ecx
	leal	GSYM(_compressed)+4-1(%ebp, %ecx), %esi
	leal	GSYM(_compressed_copy)-1(%ebp, %ecx), %edi
#else /* !VBOX */
	movl	$_compressed_size, %ecx
	leal	_compressed+4-1(%ebp, %ecx), %esi
	leal	_compressed_copy-1(%ebp, %ecx), %edi
#endif /* !VBOX */
	rep movsb
	/* Setup to run the decompressor */
	cld
#ifdef VBOX
	leal	GSYM(_compressed_copy)(%ebp), %esi
	leal	GSYM(decompress_to)(%ebp), %edi
#else /* !VBOX */
	leal	_compressed_copy(%ebp), %esi
	leal	decompress_to(%ebp), %edi
#endif /* !VBOX */
	movl	$-1, %ebp	/* last_m_off = -1 */
	jmp	dcl1_n2b

/* ------------- DECOMPRESSION -------------

 Input:
   %esi - source
   %edi - dest
   %ebp - -1
   cld

 Output:
   %eax - 0
   %ecx - 0
*/

.macro getbit bits
.if	\bits == 1
	addl	%ebx, %ebx
	jnz	1f
.endif
	movl	(%esi), %ebx
	subl	$-4, %esi	/* sets carry flag */
	adcl	%ebx, %ebx
1:
.endm

decompr_literals_n2b:
	movsb

decompr_loop_n2b:
	addl	%ebx, %ebx
	jnz	dcl2_n2b
dcl1_n2b:
	getbit	32
dcl2_n2b:
	jc	decompr_literals_n2b
	xorl	%eax, %eax
	incl	%eax		/* m_off = 1 */
loop1_n2b:
	getbit	1
	adcl	%eax, %eax	/* m_off = m_off*2 + getbit() */
	getbit	1
	jnc	loop1_n2b	/* while(!getbit()) */
	xorl	%ecx, %ecx
	subl	$3, %eax
	jb	decompr_ebpeax_n2b	/* if (m_off == 2) goto decompr_ebpeax_n2b ? */
	shll	$8, %eax	
	movb	(%esi), %al	/* m_off = (m_off - 3)*256 + src[ilen++] */
	incl	%esi
	xorl	$-1, %eax	
	jz	decompr_end_n2b	/* if (m_off == 0xffffffff) goto decomp_end_n2b */
	movl	%eax, %ebp	/* last_m_off = m_off ?*/
decompr_ebpeax_n2b:
	getbit	1		
	adcl	%ecx, %ecx	/* m_len = getbit() */
	getbit	1
	adcl	%ecx, %ecx	/* m_len = m_len*2 + getbit()) */
	jnz	decompr_got_mlen_n2b	/* if (m_len == 0) goto decompr_got_mlen_n2b */
	incl	%ecx		/* m_len++ */
loop2_n2b:
	getbit	1	
	adcl	%ecx, %ecx	/* m_len = m_len*2 + getbit() */
	getbit	1
	jnc	loop2_n2b	/* while(!getbit()) */
	incl	%ecx
	incl	%ecx		/* m_len += 2 */
decompr_got_mlen_n2b:
	cmpl	$-0xd00, %ebp
	adcl	$1, %ecx	/* m_len = m_len + 1 + (last_m_off > 0xd00) */
	pushl	%esi
	leal	(%edi,%ebp), %esi	/* m_pos = dst + olen + -m_off  */
	rep
	movsb			/* dst[olen++] = *m_pos++ while(m_len > 0) */
	popl	%esi
	jmp	decompr_loop_n2b
decompr_end_n2b:
	/* Restore the initial register values */
	popal
	ret
