/*
 **************************************************************************
 *
 * Boot-ROM-Code to load an operating system across a TCP/IP network.
 *
 * Module:  inflate.S86
 * Purpose: Decompress the rom image
 * Entries: inflate
 *
 **************************************************************************
 *
 * Copyright (C) 1995-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: inflate.S86,v 1.6 2007/01/06 18:30:47 gkminix Exp $
 *
 **************************************************************************
 */

#include <common.i86>
#include <romconfig.i86>
#include <system/bootrom.i86>

.file __FILE__
.line __LINE__



/*
 **************************************************************************
 *
 * Definitions local to this module
 */
#define STKSIZE		256			/* stack size for this module */
#define BUFSIZE		8192			/* text buffer size */
#define PRESENSE	256			/* pre-sense buffer size */
#define THRESHOLD	2			/* buffer threshold */
#define NCHAR		511			/* number of character codes */
#define PLENSIZE	64			/* size of p_len */
#define DLENSIZE	256			/* size of d_len */

#define SONSIZE		(NCHAR * 2 - 1)		/* size of son table */
#define ROOT		(NCHAR * 2 - 2)		/* root position */

#define ENDOF		256			/* code for end of input */
#define MAXFREQ		0x8000			/* maximum frequency code */


/*
 * All translation tables are stored in the data segment. However, putting
 * them into the BSS segment adds too much overhead to the rom startup code
 * so we define their offsets here. These offsets have to be added to the
 * pointer indicating the end of the data area, which is globally stored in
 * the BP register.
 */

		.struct	0
initab:		.struct	.+8			# initialization table
textbuf:	.struct	.+BUFSIZE		# text buffer
plen:		.struct	.+PLENSIZE		# p_len
dlen:		.struct	.+DLENSIZE		# d_len
codetab:	.struct	.+256			# code table
freq:		.struct	.+(SONSIZE + 1) * 2	# frequency table
prnt:		.struct	.+(SONSIZE + NCHAR) * 2	# parent node table
son:		.struct	.+(SONSIZE + 1) * 2	# son node table
txtptr:		.struct	.+2			# offset into text buffer
bitbuf:		.struct	.+2			# input bit buffer
bitlen:		.struct	.+1			# input bit length
cflag:		.struct	.+1			# flag indicating corrupt input
		.align	4			# table has to have a size of a
tblsize:	.struct	.			# multiple of 4




/*
 **************************************************************************
 *
 * Start of text segment
 */
	.text

	.global	inflate

	.extern	_end_of_text
	.extern	getbyte
	.extern	putbyte




/*
 **************************************************************************
 *
 * Get next number of bits from compressed input stream
 * Input:  CL  -  number of bits to get
 * Output: AX  -  result
 *         Carry flag set if error
 * Registers changed: AX
 */
getnbits:

	push	dx
	mov	ax,bitbuf[bp]
	mov	dl,bitlen[bp]
	cmp	dl,8
	ja	2f

/*
 * Get next character from compressed input stream and store it into the
 * bit buffer
 */

	push	cx
	call	getbyte			# get next byte
	jnz	1f
	inc	byte ptr cflag[bp]	# increment cflag if at end of file
1:	xor	ah,ah
	mov	cl,8
	sub	cl,dl
	shl	ax,cl			# update bit buffer
	or	ax,bitbuf[bp]
	add	dl,8			# update bit buffer length
	pop	cx

/*
 * Get the required number of bits from the bit buffer
 */

2:	sub	dl,cl
	mov	bitlen[bp],dl		# update bit buffer length
	mov	dx,ax
	xor	ax,ax
	shld	ax,dx,cl		# get required bits out of bit buffer
	shl	dx,cl
	mov	bitbuf[bp],dx
	pop	dx

	cmp	byte ptr cflag[bp],2	# set carry flag
	cmc
	ret



/*
 **************************************************************************
 *
 * Setup huffman decoding tables
 * Input:  none
 * Output: none
 * Registers changed: AX, BX, CX, DX, SI, DI
 */
starthuff:

	xor	si,si
	lea	di,plen[bp]
	mov	bx,di
1:	movzx	cx,byte ptr initab[bp + si]
	inc	si
	mov	ax,si
	rep	stosb			# fill p_len table with defaults
	cmp	si,7
	jbe	1b

	mov	dx,di
	sub	dx,bx			# put size of p_len into DX
	xor	si,si
	lea	di,dlen[bp]
	lea	bx,codetab[bp]
2:	mov	cl,8
	sub	cl,plen[bp + si]
	mov	ax,1
	shl	ax,cl
	mov	cx,ax
3:	mov	ax,si
	mov	[bx],al			# fill code table
	mov	al,plen[bp + si]
	stosb				# fill d_len table with length of
	inc	bx			# every code in code table
	loop	3b
	inc	si
	cmp	si,dx
	jb	2b

	mov	si,SONSIZE
	xor	di,di
	xor	cx,cx
5:	mov	word ptr freq[bp + di],1
	mov	word ptr son[bp + di],si
	mov	word ptr prnt[bp + di + (SONSIZE * 2)],cx
	add	di,2			# fill up frequency, son and parent
	inc	si			# tables for leaf entries
	inc	cx
	cmp	cx,NCHAR
	jb	5b

	xor	si,si
	xor	di,di
	mov	cx,NCHAR
	lea	bx,freq[bp]
6:	mov	ax,[bx + si + 0]
	add	ax,[bx + si + 2]
	mov	[bx + di + NCHAR * 2],ax
	mov	son[bp + di + NCHAR * 2],di
	mov	prnt[bp + si + 0],cx	# fill up frequency, son and parent
	mov	prnt[bp + si + 2],cx	# tables for root entries
	add	di,2
	add	si,4
	inc	cx
	cmp	cx,ROOT
	jbe	6b

	mov	word ptr freq[bp + (SONSIZE * 2)],0xFFFF
	mov	word ptr prnt[bp + (ROOT * 2)],0
	ret



/*
 **************************************************************************
 *
 * Update huffman decoding tables - dont ask me how this works, or to say
 * it with the original AT&T UNIX source code: you are not supposed
 * to understand this ;-))
 * Input:  DX  -  last read compressed input value
 * Output: none
 * Registers changed: AX, BX, CX, DX, SI, DI
 */
update:

	push	dx
	cmp	word ptr freq[bp + (ROOT * 2)],MAXFREQ
	jne	0f

/*
 * Correct leaf node into first half, and set these frequencies to (freq+1)/2
 */

	xor	si,si
	xor	di,di
	mov	cx,SONSIZE
1:	cmp	word ptr son[bp + si],SONSIZE
	jb	2f
	mov	ax,freq[bp + si]
	inc	ax
	shr	ax,1
	mov	freq[bp + di],ax
	mov	ax,son[bp + si]
	mov	son[bp + di],ax
	add	di,2
2:	add	si,2
	loop	1b

/*
 * Build tree. Link sons first.
 */

	xor	dx,dx			# i = DX
	mov	cx,NCHAR		# j = CX
3:	mov	si,dx
	shl	si,1
	lea	bx,freq[bp]		# &freq[0] = BX
	mov	ax,[bx + si + 0]
	add	ax,[bx + si + 2]
	mov	si,cx
	shl	si,1
	mov	[bx + si],ax		# f = AX

	mov	si,cx
	dec	si			# k*2 = SI
	shl	si,1
4:	cmp	ax,[bx + si]
	jae	5f
	sub	si,2
	jmp	4b

5:	add	si,2
	push	ax
	mov	di,cx			# e = SI
	shl	di,1			# p = DI
6:	cmp	di,si
	jbe	7f
	mov	ax,[bx + di - 2]
	mov	[bx + di],ax
	sub	di,2
	jmp	6b
7:	pop	ax			# f = AX
	mov	[bx + si],ax

	lea	bx,son[bp]		# &son[0] = BX
	mov	di,cx			# e = SI
	shl	di,1			# p = DI
8:	cmp	di,si
	jbe	9f
	mov	ax,[bx + di - 2]
	mov	[bx + di],ax
	sub	di,2
	jmp	8b
9:	mov	[bx + si],dx

	add	dx,2
	inc	cx
	cmp	cx,SONSIZE
	jb	3b

/*
 * Link parents
 */

	xor	dx,dx			# i = DX
	lea	bx,prnt[bp]		# &prnt[0] = BX
	lea	si,son[bp]		# &son[i*2] = SI
2:	mov	di,[si]			# k*2 = DI
	shl	di,1
	mov	[bx + di],dx
	cmp	di,SONSIZE * 2
	jae	3f
	mov	[bx + di + 2],dx
3:	add	si,2
	inc	dx
	cmp	dx,SONSIZE
	jb	2b

/*
 * Order frequencies
 */

0:	pop	si
	shl	si,1
	mov	dx,prnt[bp + si + (SONSIZE * 2)]

4:	mov	si,dx
	shl	si,1
	lea	bx,freq[bp + si]	# &freq[i*2] = BX
	inc	word ptr [bx]
	mov	ax,[bx]			# k = AX

/*
 * Swap nodes when it becomes a wrong frequency order
 */

	cmp	ax,[bx + 2]
	jbe	8f
	lea	si,[bx + 4]		# p = SI
	mov	bx,ax			# k = BX
5:	lodsw
	cmp	bx,ax
	ja	5b
	mov	cx,si
	lea	ax,freq[bp + 4]
	sub	cx,ax			# l = CX
	mov	di,cx			# l*2 = DI
	shr	cx,1

	xchg	bx,[si - 4]
	mov	si,dx			# c*2 = SI
	shl	si,1
	mov	freq[bp + si],bx
	mov	si,son[bp + si]
	mov	ax,si			# i = AX
	shl	si,1
	mov	prnt[bp + si + 0],cx
	cmp	ax,SONSIZE
	jae	6f
	mov	prnt[bp + si + 2],cx
6:	xchg	ax,son[bp + di]
	mov	si,ax			# j = AX
	shl	si,1			# j*2 = SI
	mov	prnt[bp + si + 0],dx
	cmp	ax,SONSIZE
	jae	7f
	mov	prnt[bp + si + 2],dx
7:	mov	si,dx			# c*2 = SI
	shl	si,1
	mov	son[bp + si],ax
	mov	dx,cx

8:	mov	si,dx
	shl	si,1
	mov	dx,prnt[bp + si]	# loop until we reach the root
	or	dx,dx
	jnz	4b
	ret



/*
 **************************************************************************
 *
 * Inflate the compressed image. This routine assumes DS = ES = SS.
 * Input:  DI  -  Offset to first byte usable for BSS
 * Output: AL  -  0=no error, -1=error
 * Registers changed: EAX, BX, CX, DX, SI, DI
 */
inflate:

	cld
	push	bp
	mov	bp,di				# let BP point to actual BSS
	mov	ax,di
	add	ax,_I(tblsize) + STKSIZE
	cmp	sp,ax				# check if we have enough space
	jb	8f
	xor	eax,eax
	mov	cx,_I(tblsize) / 4
	rep	stosd				# clear out all tables
	mov	word ptr txtptr[bp],BUFSIZE - PRESENSE

/*
 * Read the initialization table from the input file
 */

	call	getbyte
	jz	8f
	mov	bl,al			# read first word of input file into
	call	getbyte			# BX
	jz	8f
	mov	bh,al

	lea	di,initab[bp]		# DI points into initialization table
	mov	cx,1
	mov	dx,0x01FF		# bit mask in DH
1:	mov	al,bl
	and	al,dh			# mask off significant bits
	stosb
	shr	bx,cl			# get next set of bits from init word
	shl	dx,1			# adjust bit mask
	inc	cx
	cmp	cx,5
	jbe	1b			# continue with next bit set
	call	getbyte			# get last initialization byte
	jz	8f
	and	al,dh			# and save the relevant bits
	stosb

/*
 * Compute summaries from initialization table
 */

	lea	si,initab[bp]
	xor	bx,bx
	xor	dx,dx
	xor	ah,ah
2:	lodsb
	add	bx,ax			# addup all bytes in initialization tbl
	shl	dx,1
	add	dx,ax
	loop	2b

	shl	dx,2
	mov	ax,PLENSIZE - 2		# compute remaining size of p_len table
	sub	ax,bx
	jl	8f			# should not be negative
	mov	bx,ax

	mov	ax,DLENSIZE		# compute remaining size of d_len table
	sub	ax,dx
	sbb	ax,bx
	jl	8f			# should not be negative

	or	ah,bh			# both should not be larger than 255
	jnz	8f	
	stosb				# save d_len into initialization table
	sub	bx,ax
	jl	8f
	mov	[di],bl			# save difference into init table

/*
 * Check that the initialization table is correct. Its sum has to be 256.
 */

	lea	si,initab[bp]
	mov	cx,8
	xor	bx,bx
3:	lodsb				# load every byte of the init table
	xor	ah,ah
	shl	ax,cl			# shift value according to its position
	rcr	ax,1
	add	bx,ax			# add it up and continue with next
	loop	3b			# value
	cmp	bx,256			# sum has to be exactly 256
	jne	8f

/*
 * Setup the huffman decoding tables
 */

	call	starthuff		# setup huffman decoding table
	lea	di,textbuf[bp]
	mov	cx,txtptr[bp]		# clear text buffer
	mov	al,0x20
	rep	stosb

/*
 * Start the main loop reading all of the compressed data
 */

5:	mov	dx,son[bp + (ROOT * 2)]	# get root
6:	cmp	dx,SONSIZE		# check if leaf reached
	jae	1f
	mov	cl,1			# get next bit from compressed image
	call	getnbits
	jc	8f			# check if eof reached
	add	ax,dx			# get pointer to next son
	mov	si,ax
	shl	si,1
	mov	dx,son[bp + si]		# get next son
	jmp	6b

1:	xor	ax,ax
	sub	dx,SONSIZE		# compute value of son
	cmp	dx,ENDOF		# check if end of file reached
	je	9f

	push	dx
	call	update			# update huffman tables
	pop	ax

/*
 * Put output code into decompressed image
 */

	cmp	ax,256			# check if its a output code
	jae	2f
	mov	si,txtptr[bp]
	mov	textbuf[bp + si],al	# put character into text table
	inc	si
	and	si,BUFSIZE - 1		# increment text pointer
	mov	txtptr[bp],si
	call	putbyte			# write char into decompressed image
	jmp	5b			# continue with next character

/*
 * Decode upper 6 bits from table and copy decompressed characters from text
 * text buffer into destination image
 */

2:	push	ax
	mov	cl,8			# get next byte from compressed image
	call	getnbits
	jc	7f			# check if eof reached
	mov	si,ax
	movzx	dx,byte ptr codetab[bp + si]
	shl	dx,7
	mov	cl,dlen[bp + si]	# get number of bits telling offset
	dec	cl			# of string in text buffer
	shl	ax,cl
	mov	bx,ax
	call	getnbits		# get index
	jc	7f			# check if eof reached
	or	ax,bx
	and	ax,0x007F		# compute final index into text buffer
	or	ax,dx
	inc	ax
	mov	si,txtptr[bp]
	mov	di,si
	sub	si,ax
	pop	cx
	sub	cx,256 - THRESHOLD	# compute final length

	lea	bx,textbuf[bp]
3:	and	si,BUFSIZE - 1		# wrap text buffer pointers at the end
	and	di,BUFSIZE - 1
	mov	al,[bx + si]		# get next character from text buffer
	mov	[bx + di],al		# and store it into new position
	call	putbyte			# write it into decompressed image
	inc	si
	inc	di			# continue with next character
	loop	3b
	and	di,BUFSIZE - 1
	mov	txtptr[bp],di		# update new text pointer
	jmp	5b			# continue with next compressed byte

/*
 * Thats it, return to caller
 */

7:	pop	ax
8:	mov	al,-1			# return with error
9:	pop	bp
	ret




/*
 **************************************************************************
 */
	.end

