;***********************************************
;	maketree.asm -- makes Huffman tree
;***********************************************
			page	0, 128

include	amscls.inc
$_init	GEN

TEXT	segment	byte	public	'CODE'
TEXT	ends

DATA	segment word	public	'DATA'
DATA	ends

BSS		segment word	public	'DATA'
extrn	right_:word
extrn	left_:word

extrn	nchar:word
extrn	n2:word
extrn	avail_mt:word
extrn	heapsize:word
extrn	freq:word
extrn	sort:word
extrn	bitlen:word
extrn	code:word
extrn	depth:word
extrn	heap:word
extrn	len_cnt:word
extrn	start:word
extrn	weight:word
BSS		ends

DGROUP	group	DATA, BSS

TEXT	segment	byte	public	'CODE'
	assume	cs:TEXT, ds:DGROUP
;---------------------------------------------------------------
;	static void count_len(short i)  /* call with i = root */
;
;---------------------------------------------------------------
public	count_len
count_len	proc	near
	$_if <cmp bx, n2>, B
		mov		bx, depth
		$_if <cmp bx, 16 * 2>, A
			mov		bx, 16 * 2
		$_endif
		inc		len_cnt[bx]
		ret
	$_endif
	add		depth, 2
	push	bx
	mov		bx, left_[bx]
	call	count_len
	pop		bx
	push	bx
	mov		bx, right_[bx]
	call	count_len
	pop		bx
	shr		left_[bx], 1
	shr		right_[bx], 1
	sub		depth, 2
	ret
count_len	endp

;---------------------------------------------------------------
;	static void downheap(short i)
;	                          bx
;---------------------------------------------------------------
public	downheap
downheap	proc	near
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	mov		si, heap[bx]
	push	si
	mov		bp, freq
	mov		cx, ds:[bp + si]
	mov		si, bx
	$_while <TRUE>
		shl		si, 1
		$_break , <cmp si, heapsize>, A
		mov		di, heap[si]
		mov		ax, ds:[bp + di]
		$_if , B, AND
			mov		di, heap[si + 2]
			mov		dx, ds:[bp + di]
		$_c  <cmp ax, dx>, A
			mov		ax, dx
			add		si, 2
		$_endif
		$_break , <cmp cx, ax>, BE
		mov		di, heap[si]
		mov		heap[bx], di
		mov		bx, si
	$_enddo
	pop		heap[bx]
	pop		bp
	pop		di
	pop		si
	pop		dx
	pop		cx
	ret
downheap	endp

;---------------------------------------------------------------
;	short make_tree(short nparm, ushort freqparm[], 
;	                         cx               bx
;	                uchar lenparm[], ushort codeparm[])
;	                           di                 ax
;---------------------------------------------------------------
public	make_tree_
make_tree_	proc	near
	push	cx
	push	dx
	push	si
	push	di
	push	bp
	mov		nchar, ax
	mov		freq, bx
	mov		bitlen, cx
	mov		code, dx
	shl		ax, 1
	mov		n2, ax
	mov		avail_mt, ax

	xor		ax, ax
	cld
	push	ds
	pop		es
	mov		di, cx
	mov		cx, nchar
	rep		stosb

	mov		cx, nchar
	mov		di, freq
	lea		dx, 2[di]
	mov		bx, offset DGROUP:heap
	jmp		make_tree_1
	$_do
		add		bx, 2
		mov		[bx], di
		sub		[bx], dx
make_tree_1:
		xor		ax, ax					; set ZERO flag
	$_until <repz scasw>, Z

	sub		bx, offset DGROUP:heap
	mov		heapsize, bx
	$_if <cmp bx, 4>, B
		mov		ax, heap[2]
		mov		di, ax
		add		di, code
		mov		word ptr [di], 0
		jmp		return
	$_endif
	shr		bx, 1
	and		bx, 0fffeh
	$_do
		push	bx
		call	downheap
		pop		bx
	$_until <sub bx, 2>, Z
	mov		di, code
	$_do
		mov		si, heap[2]
		$_if <cmp si, n2>, B
			mov		[di], si
			add		di, 2
		$_endif

		mov		bx, heapsize
		sub		heapsize, 2
		mov		ax, heap[bx]
		mov		heap[2], ax

		mov		bx, 2
		call	downheap

		mov		dx, heap[2]
		$_if <cmp dx, n2>, B
			mov		[di], dx
			add		di, 2
		$_endif

		mov		bx, avail_mt
		add		avail_mt, 2

		mov		left_[bx], si
		mov		right_[bx], dx
		mov		heap[2], bx

		mov		bp, freq
		mov		ax, [si + bp]
		mov		si, dx
		add		ax, [si + bp]
		mov		dx, bx
		add		bx, bp
		mov		[bx], ax

		mov		bx, 2
		call	downheap
	$_until <cmp heapsize, 2>, BE
	mov		bx, dx
	push	dx
;---------------------------------------------------------------
;	static void make_len(short root)
;---------------------------------------------------------------
public	make_len
make_len:
	xor		ax, ax
	mov		di, offset DGROUP:len_cnt
	mov		cx, 17
	rep		stosw
	mov		depth, ax
	call	count_len
	xor		dx, dx
	mov		cx, 15
	mov		si, offset DGROUP:len_cnt + 2
	$_do
		lodsw
		shl		ax, cl
		add		dx, ax
	$_until <LOOP>
	add		dx, [si]
	$_if , NZ
		sub		len_cnt[16 * 2], dx
		mov		cx, dx
		$_do
			mov		di, offset DGROUP:len_cnt + 15 * 2
			$_while <cmp word ptr [di], 0>, E
				sub		di, 2
			$_enddo
			dec		word ptr [di]
			add		word ptr [di + 2], 2
		$_until <LOOP>
	$_endif
	mov		di, offset DGROUP:len_cnt + 16 * 2
	mov		si, code
	mov		al, 16
	$_do
		mov		cx, [di]
		jcxz	make_len_1
		$_do
			mov		bx, [si]
			add		si, 2
			shr		bx, 1
			add		bx, bitlen
			mov		[bx], al
		$_until <LOOP>
make_len_1:
		sub		di, 2
		dec		al
	$_until , Z
;---------------------------------------------------------------
;	void make_code(short nchar, uchar bitlen[], ushort code[])
;---------------------------------------------------------------
public	make_code
make_code:
	xor		ax, ax
;;;
	mov		start, ax
	mov		weight, ax
;;;
	mov		si, offset DGROUP:len_cnt + 2
	mov		di, offset DGROUP:start + 2
	mov		bx, offset DGROUP:weight + 2
	mov		cx, 16
	$_do
		stosw
		mov		dx, [si]
		add		si, 2
		dec		cx
		shl		dx, cl
		add		ax, dx
		mov		dx, 1
		shl		dx, cl
		inc		cx
		mov		[bx], dx
		add		bx, 2
	$_until <LOOP>
	xor		bx, bx
	mov		cx, nchar
	mov		si, bitlen
	mov		di, code
	$_do
		lodsb
		shl		al, 1
		mov		bl, al
		mov		ax, start[bx]
		stosw
		mov		ax, weight[bx]
		add		start[bx], ax
	$_until <LOOP>
;---------------------------------------------------------------
	pop		ax
return:
	shr		ax, 1
	pop		bp
	pop		di
	pop		si
	pop		dx
	pop		cx
	ret
make_tree_	endp
TEXT	ends
		end
