;***********************************************
;	huf_.asm -- new static Huffman
;***********************************************
			page	0, 128

include	amscls.inc
$_init	GEN

NC		=	(200h - 2)
CBIT	=	9

NP		=	14
NT		=	19
PBIT	=	4
TBIT	=	5
;NPT	=	080h

CGROUP	group	TEXT
DGROUP	group	DATA, BSS

TEXT	segment	public byte 'CODE'
extrn	fillbuf_:near
extrn	getbits_:near
extrn	init_getbits_:near
extrn	init_putbits_:near
extrn	make_table_:near
extrn	make_tree_:near
extrn	putbits_:near
extrn	putcode_:near
TEXT	ends

DATA	segment	public word 'DATA'
DATA	ends

BSS		segment	public word 'DATA'
buf_			dd		1 dup (?)
buf_limit_		dw		1 dup (?)
blocksize_		dw		1 dup (?)
output_pos_		dd		1 dup (?)
cpos_			dw		1 dup (?)
output_mask_	db		1 dup (?)
BSS		ends

extrn	c_freq_:word
extrn	p_freq_:word
extrn	t_freq_:word
extrn	left_:word
extrn	right_:word
extrn	c_table_:word
extrn	c_code_:word
extrn	pt_table_:word
extrn	pt_code_:word
extrn	c_len_:byte
extrn	pt_len_:byte

public	buf_
public	buf_limit_

extrn	bitbuf_:word
extrn	unpackable_:word

assume	cs:CGROUP, ds:DGROUP

TEXT	segment	public byte 'CODE'
;
;	static void count_t_freq(void)
;

;public	count_t_freq_
count_t_freq_	proc	near
	push	cx
	push	si
	push	di
	cld
	push	ds
	pop		es
	mov		cx, NT
	mov		di, offset DGROUP:t_freq_
	xor		ax, ax
	rep		stosw
	mov		cx, NC
	mov		di, offset DGROUP:c_len_[NC - 1]
	std
	repe	scasb
	cld
	inc		di
	mov		si, di
	mov		di, offset DGROUP:c_len_
	$_do
		mov		bl, [di]
		inc		di
		$_if <or bl, bl>, Z
			mov		cx, -1
			repe	scasb
			dec		di
			not		cx
			$_switch
			$_case <cmp cx, 2>, BE
				add		t_freq_[0 * 2], cx
				$_break
			$_case <cmp cx, 19>, BE
				$_if , E
					inc		t_freq_[0 * 2]
				$_endif
				inc		t_freq_[1 * 2]
				$_break
			$_default
				inc		t_freq_[2 * 2]
			$_endswitch
		$_else
			xor		bh, bh
			shl		bx, 1
			inc		t_freq_[bx + 2 * 2]
		$_endif
	$_until <cmp di, si>, A
	pop		di
	pop		si
	pop		cx
	ret
count_t_freq_	endp

;
;	static void write_pt_len(short n, short nbit, short i_special)
;

;public	write_pt_len_
write_pt_len_	proc	near
	push	cx
	push	si
	push	di

	push	cx					; i_special
	push	bx					; nbit
	mov		si, offset DGROUP:pt_len_
	lea		di, [si - 1]
	add		di, ax
	mov		cx, ax
	inc		cx
	xor		al, al
	std
	repe	scasb
	cld
	mov		bx, cx
	pop		ax					; nbit
	call	putbits_
	inc		di
	pop		cx					; i_special
	add		cx, si
	$_do
		xor		bh, bh
		mov		bl, [si]
		inc		si
		$_if <cmp bl, 6>, BE
			mov		al, 3
		$_else
			mov		ax, bx
			sub		ax, 3
			mov		bx, 0fffeh
		$_endif
		call	putbits_
		$_if <cmp si, cx>, E
			$_while <cmp si, offset DGROUP:pt_len_ + 6>, B, AND
			$_c     <cmp byte ptr [si], 0>, E
				inc		si
			$_enddo

			mov		al, 2
			mov		bx, si
			sub		bx, offset DGROUP:pt_len_ + 3;
			call	putbits_
		$_endif
	$_until <cmp si, di>, A

	pop		di
	pop		si
	pop		cx
	ret
write_pt_len_	endp

;
;	static void write_c_len(void)
;

;public	write_c_len_
write_c_len_	proc	near
	push	cx
	push	si
	push	di
	mov		di, offset DGROUP:c_len_ + NC - 1
	mov		cx, NC + 1
	xor		al, al
	std
	repe	scasb
	cld
	inc		di
	mov		si, di
	mov		di, offset DGROUP:c_len_
	mov		bx, cx
	mov		al, CBIT
	call	putbits_
	$_do
		mov		bl, [di]
		inc		di
		$_if <or bl, bl>, Z
			xor		al, al
			mov		cx, -1
			repe	scasb
			dec		di
			not		cx
			$_switch
			$_case <cmp cx, 2>, BE
				$_do
					mov		al, pt_len_[0]
					mov		bx, pt_code_[0 * 2]
					call	putcode_
				$_until <LOOP>
				$_break
			$_case <cmp cx, 19>, BE
				$_if , E
					mov		al, pt_len_[0]
					mov		bx, pt_code_[0 * 2]
					call	putcode_
					dec		cx
				$_endif
				mov		al, pt_len_[1]
				mov		bx, pt_code_[1 * 2]
				call	putcode_
				mov		al, 4
				mov		bx, cx
				sub		bx, 3
				call	putbits_
				$_break
			$_default
				mov		al, pt_len_[2]
				mov		bx, pt_code_[2 * 2]
				call	putcode_
				mov		al, CBIT
				mov		bx, cx
				sub		bx, 20
				call	putbits_
			$_endswitch
		$_else
			xor		bh, bh
			mov		al, pt_len_[bx + 2]
			shl		bx, 1
			mov		bx, pt_code_[bx + 4]
			call	putcode_
		$_endif
	$_until <cmp di, si>, A
	pop		di
	pop		si
	pop		cx
	ret
write_c_len_	endp

;
;	static void send_block(void)
;

;public	send_block_
send_block_	proc	near
	push	cx
	push	dx
	push	si
	push	di
	push	bp

	mov		ax, NC
	mov		bx, offset DGROUP:c_freq_
	mov		cx, offset DGROUP:c_len_
	mov		dx, offset DGROUP:c_code_
	call	make_tree_
	mov		bx, ax
	mov		cx, ax
	shl		bx, 1
	mov		bx, c_freq_[bx]
	mov		al, 16
	call	putbits_
	$_if <cmp cx, NC>, AE
		call	count_t_freq_
		mov		ax, NT
		mov		bx, offset DGROUP:t_freq_
		mov		cx, offset DGROUP:pt_len_
		mov		dx, offset DGROUP:pt_code_
		call	make_tree_
		$_if <cmp ax, NT>, AE
			mov		ax, NT
			mov		bx, TBIT
			mov		cx, 3
			call	write_pt_len_
		$_else
if 1
			mov		bx, ax
			mov		al, TBIT * 2
			call	putbits_
else
			mov		cx, ax
			xor		bx, bx
			mov		al, TBIT
			call	putbits_
			mov		bx, cx
			mov		al, TBIT
			call	putbits_
endif
		$_endif
		call	write_c_len_
	$_else
if 1
		xor		bx, bx
		mov		al, CBIT + TBIT
		call	putbits_
		mov		bx, cx
		mov		al, CBIT + TBIT
		call	putbits_
else
		xor		bx, bx
		mov		al, TBIT
		call	putbits_
		xor		bx, bx
		mov		al, TBIT
		call	putbits_
		xor		bx, bx
		mov		al, CBIT
		call	putbits_
		mov		bx, cx
		mov		al, CBIT
		call	putbits_
endif
	$_endif
	mov		ax, NP
	mov		bx, offset DGROUP:p_freq_
	mov		cx, offset DGROUP:pt_len_
	mov		dx, offset DGROUP:pt_code_
	call	make_tree_
	$_if <cmp ax, NP>, AE
		mov		ax, NP
		mov		bx, PBIT
		mov		cx, -1
		call	write_pt_len_
	$_else
if 1
		mov		bx, ax
		mov		al, PBIT * 2
		call	putbits_
else
		mov		cx, ax
		mov		al, PBIT
		xor		bx, bx
		call	putbits_
		mov		al, PBIT
		mov		bx, cx
		call	putbits_
endif
	$_endif
	les		si, buf_
	xor		cx, cx
	mov		bp, word ptr output_pos_
	$_do
		$_if <and cl, 07h>, Z
			lods	byte ptr es:[si]
			mov		ch, al
		$_endif
		xor		bh, bh
		lods	byte ptr es:[si]
		mov		bl, al
		add		ch, 80h
		rcl		bh, 1

;	static void encode_c(short c)
		mov		al, c_len_[bx]
		shl		bx, 1
		mov		bx, c_code_[bx]
		call	putcode_
;
		$_if <shl ch, 1>, NC
			lods	word ptr es:[si]

;	static void encode_p(ushort p)
			mov		dx, ax
			and		ax, 0000fh
			mov		di, ax
			mov		al, pt_len_[di]
			shl		di, 1
			mov		bx, pt_code_[di]
			call	putcode_
			shr		di, 1
			$_if <dec di>, G
				mov		ax, di
				mov		bx, dx
				and		bl, 0f0h
				call	putcode_
			$_endif
;

		$_endif
		inc		cx
	$_until <cmp si, bp>, AE
	push	ds
	pop		es
	xor		ax, ax
	mov		cx, NC
	mov		di, offset DGROUP:c_freq_
	rep		stosw
	mov		cx, NP
	mov		di, offset DGROUP:p_freq_
	rep		stosw

	pop		bp
	pop		di
	pop		si
	pop		dx
	pop		cx
	ret
send_block_	endp

;
;	void output_st1(ushort c, ushort p)
;

public	output_st1_
output_st1_	proc	near
	push	di
	les		di, output_pos_
	$_if <ror output_mask_, 1>, C
		$_if <cmp di, buf_limit_>, AE
			push	ax
			push	bx
			push	es
			call	send_block_
			pop		es
			pop		bx
			pop		ax
			cmp		unpackable_, 0
			jne		os1_end
			xor		di, di
		$_endif
		mov		cpos_, di
		mov		byte ptr es:[di], 0
		inc		di
	$_endif
	stosb
	xchg	bx, ax
	shl		bx, 1
	inc		c_freq_[bx]
	$_if <shr bh, 1>, NZ

		xor		bx, bx
		shl		ax, 1
		jz		keta1
		shl		ax, 1
		shl		ax, 1
		mov		bx, 13 + 1
	keta0:
		dec		bx
		shl		ax, 1
		jc		keta1
		dec		bx
		shl		ax, 1
		jc		keta1
		dec		bx
		shl		ax, 1
		jc		keta1
		dec		bx
		shl		ax, 1
		jc		keta1
		dec		bx
		shl		ax, 1
		jnc		keta0
	keta1:

		or		ax, bx					; 左詰めの位置 + 桁数
		stosw

		shl		bx, 1
		inc		p_freq_[bx]

		mov		al, output_mask_
		mov		bx, cpos_
		or		es:[bx], al
	$_endif
	mov		word ptr output_pos_, di
os1_end:
	pop		di
	ret
output_st1_	endp

if 0
;
;	void far *alloc_buf(void)
;

public	alloc_buf_
alloc_buf_	proc	near
	mov		bx, 0ffffh
	mov		ah, 48h
	int		21h
	$_if <cmp bx, 256>, AE
		mov		ax, 1024
		$_if <cmp bx, ax>, B
			mov		ax, bx
		$_endif
		mov		bx, ax
		shl		ax, 1
		shl		ax, 1
		shl		ax, 1
		shl		ax, 1
		sub		ax, 24
		mov		buf_limit_, ax
		mov		ah, 48h
		int		21h
		mov		bx, ax
	$_else
		xor		bx, bx
	$_endif
	xor		ax, ax
	mov		word ptr buf_ + 2, bx
	mov		word ptr buf_, ax
	ret
alloc_buf_	endp
endif

;
;	void encode_start_st1(void)
;

public	encode_start_st1_
encode_start_st1_	proc	near
	push	cx
	push	di
	push	ds
	pop		es
	xor		ax, ax
	mov		cx, NC
	mov		di, offset DGROUP:c_freq_
	rep		stosw
	mov		cx, NP
	mov		di, offset DGROUP:p_freq_
	rep		stosw
	les		bx, buf_
	mov		word ptr output_pos_ + 2, es
	mov		word ptr output_pos_, bx
	mov		byte ptr es:[bx], 0
	mov		output_mask_, 01h
	call	init_putbits_
	pop		di
	pop		cx
	ret
encode_start_st1_	endp

;
;	void encode_end_st1(void)
;

public	encode_end_st1_
encode_end_st1_	proc	near
	$_if <cmp unpackable_, 0>, E
		call	send_block_
		mov		ax, 7
		xor		bx, bx
		call	putcode_
	$_endif
	ret
encode_end_st1_	endp

;	/***** decoding *****/

;
;	static void read_pt_len(short nn, short nbit, short i_special)
;

;public	read_pt_len_
read_pt_len_	proc	near
	push	cx
	push	dx
	push	si
	push	di

	push	ax
	mov		si, ax
	mov		dx, bx
	mov		ax, bx
	call	getbits_
	cmp		ax, si
	ja		tbl_error_

	mov		di, offset DGROUP:pt_len_
	$_if <or ax, ax>, Z
		mov		di, offset DGROUP:pt_len_
		pop		cx
		rep		stosb
		mov		ax, dx
		call	getbits_
		mov		cx, 256
		mov		di, offset DGROUP:pt_table_
		rep		stosw
	$_else
		mov		dx, cx			; dl = i_special
		add		dx, di
		mov		si, di
		add		si, ax			; ax = n
		$_do
			mov		ax, 3
			call	getbits_
			$_if <cmp al, 7>, E
				mov		bx, bitbuf_
				$_while <shl bx, 1>, C
					inc		ax
				$_enddo
				push	ax
				sub		ax, 6
				call	fillbuf_
				pop		ax
			$_endif
			stosb
			$_if <cmp di, dx>, E
				mov		ax, 2
				call	getbits_
				mov		cx, ax
				xor		al, al
				rep		stosb
			$_endif
		$_until <cmp di, si>, AE
		pop		cx						; nn
		mov		si, cx
		add		cx, offset DGROUP:pt_len_
		sub		cx, di
		jb		tbl_error_
		xor		al, al
		rep		stosb
		mov		ax, si
		mov		bx, offset DGROUP:pt_len_
		mov		cx, 8
		mov		dx, offset DGROUP:pt_table_
		call	make_table_
	$_endif

	pop		di
	pop		si
	pop		dx
	pop		cx
	ret
read_pt_len_	endp

extrn	error_:near
extrn	BROKENARC_:byte

tbl_error_	proc	near
	mov		ax, offset DGROUP:BROKENARC_
	xor		bx, bx
	jmp		error_
tbl_error_	endp

;
;	static void read_c_len(void)
;

;public	read_c_len_
read_c_len_	proc	near
	push	cx
	push	dx
	push	di

	mov		ax, CBIT
	call	getbits_
	cmp		ax, NC
	ja		tbl_error_

	mov		di, offset DGROUP:c_len_
	$_if <or ax, ax>, Z
		mov		cx, NC
		rep		stosb
		mov		ax, CBIT
		call	getbits_
		mov		cx, 4096
		mov		di, offset DGROUP:c_table_
		rep		stosw
	$_else
		mov		dx, di
		add		dx, ax			; ax = n
		$_do
			mov		ax, bitbuf_
			mov		bl, ah
			xor		bh, bh
			shl		bx, 1
			mov		bx, pt_table_[bx]
			$_while <cmp bx, NT>, AE
				$_if <shl al, 1>, C
					mov		bx, right_[bx]
				$_else
					mov		bx, left_[bx]
				$_endif
			$_enddo
			push	bx
			mov		al, pt_len_[bx]
			call	fillbuf_
			pop		ax
			$_if <sub ax, 2>, BE
				$_if , Z
					mov		ax, CBIT
					call	getbits_
					add		ax, 20
					mov		cx, ax
				$_else
					$_if <inc ax>, Z
						mov		ax, 4
						call	getbits_
						add		ax, 3
						mov		cx, ax
					$_else
						mov		cx, 1
					$_endif
				$_endif
				xor		al, al
				rep		stosb
			$_else
				stosb
			$_endif
		$_until <cmp di, dx>, AE
		mov		cx, offset DGROUP:c_len_ + NC
		sub		cx, di
		jb		tbl_error_
		xor		al, al
		rep		stosb
		mov		ax, NC
		mov		bx, offset DGROUP:c_len_
		mov		cx, 12
		mov		dx, offset DGROUP:c_table_
		call	make_table_
	$_endif
	pop		di
	pop		dx
	pop		cx
	ret
read_c_len_	endp

;
;	ushort decode_c_st1(void)
;

decode_c	proc	near
;	not entry here
decode_c_st1_2:
	push	ax
	push	bx
	push	cx
	mov		ax, 16
	call	getbits_
	dec		ax
	mov		blocksize_, ax
	mov		ax, NT
	mov		bx, TBIT
	mov		cx, 3
	call	read_pt_len_
	call	read_c_len_
	mov		ax, NP
	mov		bx, PBIT
	mov		cx, -1
	call	read_pt_len_
	pop		cx
	pop		bx
	pop		ax
	jmp		decode_c_st1_3
;
;	entry here
;
public	decode_c_st1_
decode_c_st1_:
	push	cx
	sub		blocksize_, 1
	jc		decode_c_st1_2
decode_c_st1_3:
	mov		bx, bitbuf_
	mov		cl, 4
	shr		bx, cl
	shl		bx, 1
	mov		bx, c_table_[bx]
	$_if	<cmp bx, NC>, B
decode_c_st1_1:
		push	bx
		mov		al, c_len_[bx]
		call	fillbuf_
		pop		ax
		pop		cx
		ret
	$_endif
	mov		ax, bitbuf_
	shl		al, cl
	$_do
		$_if <shl al, 1>, C
			mov		bx, right_[bx]
		$_else
			mov		bx, left_[bx]
		$_endif
	$_until <cmp bx, NC>, B
	jmp		decode_c_st1_1
decode_c	endp

;
;	ushort decode_p_st1(void)
;

public	decode_p_st1_
decode_p_st1_	proc	near
	push	cx
	xor		bh, bh
	mov		bl, byte ptr bitbuf_ + 1
	shl		bx, 1
	mov		bx, pt_table_[bx]
	$_if	<cmp bx, NP>, B
decode_p_st1_1:
		push	bx
		mov		al, pt_len_[bx]
		call	fillbuf_
		pop		ax
		$_if <or ax, ax>, NZ
			dec		ax
			mov		cx, ax
			call	getbits_
			mov		bx, 1
			shl		bx, cl
			or		ax, bx
		$_endif
		pop		cx
		ret
	$_endif
	mov		al, byte ptr bitbuf_
	$_do
		$_if <shl al, 1>, C
			mov		bx, right_[bx]
		$_else
			mov		bx, left_[bx]
		$_endif
	$_until <cmp bx, NP>, B
	jmp		decode_p_st1_1
decode_p_st1_	endp

;
;	void decode_start_st1(void)
;

public	decode_start_st1_
decode_start_st1_	proc	near
	mov		blocksize_, 0
	jmp		init_getbits_
decode_start_st1_	endp

TEXT	ends
		end
