; DEFLATE.ASM--
; Copyright (c) 2010 Hjort Nidudsson
;
; 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
; (at your option) 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.
;
; 06/30/2010
;
; This is from Info-ZIP's Zip 3.0:
;
; 	trees.c    - by Jean-loup Gailly
; 	match.asm  - by Jean-loup Gailly
; 	deflate.c  - by Jean-loup Gailly
;
; 	Copyright (c) 1990-2008 Info-ZIP. All rights reserved.
;
; That is free (but copyrighted) software. The original sources and license
; are available from Info-ZIP's home site at: http://www.info-zip.org/
;
include	deflate.inc

ifdef	DEBUG
	public clear_hash
	public clear_bl_count
	public clear_axdxcx
	public read_buf
	public flush_buf
	public putshort
	public ct_init
	public init_block
	public gen_bitlen
	public gen_codes
	public build_tree
	public pqdownheap
	public smaller
	public send_bits
	public s_tree_init
	public s_tree_loop
	public s_tree_set
	public scan_tree
	public send_tree
	public build_bl_tree
	public send_all_trees
	public flush_block
	public ct_tally
	public compress_block
	public bi_reverse
	public bi_windup
	public copy_block
	public update_hash
	public insert_string
	public lm_init
	public longest_match
	public fill_window
	public deflate_fast
	public deflate
endif

_TEXT	SEGMENT

clear_hash PROC
	mov	ax,[bp.head]
	mov	cx,HASH_SIZE
	xor	dx,dx
	jmp	clear_axdxcx
clear_hash ENDP

clear_bl_count PROC
	mov	ax,ds
	mov	cx,MAX_BITS+1
	mov	dx,offset bl_count
clear_bl_count ENDP

clear_axdxcx PROC
	assert	ax,0,jne,"null pointer"
	xchg	di,dx
	mov	es,ax
	xor	ax,ax
	cld
	rep	stosw
	mov	di,dx
	ret
clear_axdxcx ENDP

read_buf PROC
	add	ax,dx
	mov	word ptr STDI.ios_bp,ax
	mov	STDI.ios_size,cx
	xor	ax,ax
	assert	ax,cx,jne,"read_buf"
	mov	STDI.ios_c,ax
	mov	STDI.ios_i,ax
	call	ofread
	mov	word ptr STDI.ios_bp,0
  ifdef	DEBUG
	add	word ptr [bp.input_size],ax
	adc	word ptr [bp.input_size+2],0
	test	ax,ax
  endif
	ret
read_buf ENDP

flush_buf PROC
	mov	ax,word ptr [bp.fb_buf]
	mov	dx,word ptr [bp.fb_buf+2]
	mov	cx,word ptr [bp.fb_stored_ln]
	assert	cx,0,jne,"flush_buf"
	assert	dx,0,jne,"flush_buf"
	mov	es,dx
	mov	bx,ax
	mov	ax,cx
	test	ax,ax
	jz	flush_eof
	call	owrite
	ret
    flush_eof:
	inc	ax
	ret
flush_buf ENDP

putshort PROC
	mov	dx,ax
	call	oputc
	jz	putshort_eof
	mov	al,dh
	call	oputc
    putshort_eof:
	ret
putshort ENDP

ct_init	PROC
	assert	[bp.static_dtree.ct_len],0,je,"ct_init already called"
	push	si
	push	di
	mov	file_method,METHOD_DEFLATE
	xor	si,si
	mov	dx,si
    ct_init_length:
	cmp	dx,LENGTH_CODES-1
	jnb	ct_init_length_end
	mov	di,dx
	add	di,di
	mov	[bp+di.base_length],si
	xor	bx,bx
    ct_init_length_code:
	mov	cx,extra_lbits[di]
	mov	ax,1
	shl	ax,cl
	cmp	bx,ax
	jnb	ct_init_length_loop
	mov	[bp+si.length_code],dl
	inc	si
	inc	bx
	jmp	ct_init_length_code
    ct_init_length_loop:
	inc	dx
	jmp     ct_init_length
    ct_init_length_end:
	assert	si,256,je,"ct_init: length != 256"
	dec	si
	mov	[bp+si.length_code],dl
	xor	si,si
	mov	dx,si
    ct_init_dist:
	cmp	dx,16
	jnb	ct_init_dist_end
	mov	di,dx
	add	di,di
	mov	[bp+di.base_dist],si
	xor	bx,bx
    ct_init_dist_code:
	mov	cx,[di+extra_dbits]
	mov	ax,1
	shl	ax,cl
	cmp	bx,ax
	jnb	ct_init_dist_loop
	mov	[bp+si.dist_code],dl
	inc	si
	inc	bx
	jmp	ct_init_dist_code
    ct_init_dist_loop:
	inc	dx
	jmp     ct_init_dist
    ct_init_dist_end:
	assert	si,256,je,"ct_init: dist != 256"
	shr	si,7
    ct_init_dist_02:
	cmp	dx,D_CODES
	jnb	ct_init_dist_end_02
	mov	di,dx
	add	di,di
	mov	ax,si
	shl	ax,7
	mov	[bp+di.base_dist],ax
	xor	bx,bx
    ct_init_dist_code_02:
	mov	cx,extra_dbits[di]
	sub	cx,7
	mov	ax,1
	shl	ax,cl
	cmp	bx,ax
	jnb	ct_init_dist_loop_02
	mov	[bp+si.dist_code+256],dl
	inc	si
	inc	bx
	jmp	ct_init_dist_code_02
    ct_init_dist_loop_02:
	inc	dx
	jmp     ct_init_dist_02
    ct_init_dist_end_02:
	assert	si,256,je,"ct_init: 256+dist != 512"
	call	clear_bl_count
	lea	di,[bp.static_ltree]
	mov	cx,144
	mov	al,8
	mov	bx,16
	call	ct_init_code
	mov	cx,112
	inc	al
	mov	bl,18
	call	ct_init_code
	mov	cx,24
	mov	al,7
	mov	bl,14
	call	ct_init_code
	mov	cx,8
	inc	al
	mov	bl,16
	call	ct_init_code
	jmp	ct_init_gen_codes
    ct_init_code:
	inc	bl_count[bx]
	mov	[di.ct_len],ax
	add	di,SIZE CT_DATA
	dec	cx
	jnz	ct_init_code
	ret
    ct_init_gen_codes:
	lea	ax,[bp.static_ltree]
	mov	dx,L_CODES+1
	call	gen_codes
	xor	si,si
	lea	di,[bp.static_dtree]
    ct_init_dtree:
	mov	dx,5
	mov	[di.ct_len],dx
	mov	ax,si
	call	bi_reverse
	mov	[di.ct_code],ax
	add	di,SIZE CT_DATA
	inc	si
	cmp	si,D_CODES
	jb	ct_init_dtree
	pop	di
	pop	si
ct_init	ENDP

init_block PROC
	xor	ax,ax
	lea	bx,[bp.dyn_ltree]
	mov	cx,L_CODES
    init_block_ltree:
	mov	[bx.ct_freq],ax
	add	bx,SIZE CT_DATA
	dec	cx
	jnz	init_block_ltree
	lea	bx,[bp.dyn_dtree]
	mov	cx,D_CODES
    init_block_dtree:
	mov	[bx.ct_freq],ax
	add	bx,SIZE CT_DATA
	dec	cx
	jnz	init_block_dtree
	lea	bx,[bp.bl_tree]
	mov	cx,BL_CODES
    init_block_bltree:
	mov	[bx.ct_freq],ax
	add	bx,SIZE CT_DATA
	dec	cx
	jnz	init_block_bltree
	mov	bx,bp
	mov	[bx.dyn_ltree+END_BLOCK*4].ct_freq,1
	mov	word ptr [bx.opt_len],ax
	mov	word ptr [bx.opt_len+2],ax
	mov	word ptr [bx.static_len],ax
	mov	word ptr [bx.static_len+2],ax
	mov	[bx.last_lit],ax
	mov	[bx.last_dist],ax
	mov	[bx.last_flags],ax
	mov	[bx.flags],al
	inc	ax
	mov	[bx.flag_bit],al
	ret
init_block ENDP

gen_bitlen PROC
	push	si
	push	di
	mov	si,ax
	call	clear_bl_count
	mov	bx,bp
	mov	bp,[si.td_dyn_tree]
	mov	[bx.gb_overflow],ax
	mov	di,[bx.heap_max]
	add	di,di
	mov	di,[di+bx.heap]
	shl	di,2
	add	di,bp
	mov	[di.ct_len],ax
	mov	cx,[bx.heap_max]
	inc	cx
	jmp	gen_bitlen_next
    gen_bitlen_loop:
	mov	di,cx
	add	di,di
	mov	di,[di+bx.heap]
	mov	dx,di
	shl	di,2
	mov	ax,[di+bp.ct_dad]
	shl	ax,2
	xchg	ax,di
	mov	di,[di+bp.ct_len]
	xchg	ax,di
	inc	ax
	cmp	ax,[si.td_max_length]
	jbe	gen_bitlen_bits
	mov	ax,[si.td_max_length]
	inc	[bx.gb_overflow]
    gen_bitlen_bits:
	mov	[bx.gb_bits],ax
	mov	[di+bp.ct_len],ax
	cmp	dx,[si.td_max_code]
	ja	gen_bitlen_continue
	add	ax,ax
	mov	di,ax
	mov	ax,dx
	inc	bl_count[di]
	xor	dx,dx
	cmp	ax,[si.td_extra_base]
	jb	gen_bitlen_nxb
	mov	di,ax
	sub	di,[si.td_extra_base]
	add	di,di
	add	di,[si.td_extra_bits]
	mov	dx,[di]
    gen_bitlen_nxb:
	shl	ax,2
	mov	di,ax
	mov	ax,[di+bp.ct_freq]
	push	dx
	push	ax
	add	dx,[bx.gb_bits]
	mul	dx
	add	word ptr [bx.opt_len],ax
	adc     word ptr [bx.opt_len+2],dx
	pop	ax
	pop	dx
	cmp	[si.td_static_tree],0
	je	gen_bitlen_continue
	add	di,[si.td_static_tree]
	add	dx,[di.ct_len]
	mul	dx
	add	word ptr [bx.static_len],ax
	adc	word ptr [bx.static_len+2],dx
    gen_bitlen_continue:
	inc	cx
    gen_bitlen_next:
	cmp	cx,HEAP_SIZE
	jb	gen_bitlen_loop
	assert	cx,HEAP_SIZE,je,"gen_bitlen"
	mov	cx,[bx.gb_overflow]
	xor	ax,ax
	test	cx,cx
	jne	gen_bitlen_overflow
    gen_bitlen_end:
	mov	bp,bx
	pop	di
	pop	si
	ret
    gen_bitlen_overflow:
	mov	dx,[si.td_max_length]
	dec	dx
    gen_bitlen_of_00:
	mov	di,dx
	add	di,di
	cmp	bl_count[di],ax
	jne	gen_bitlen_of_01
	dec	dx
	jmp	gen_bitlen_of_00
    gen_bitlen_of_01:
	mov	al,2
	dec	bl_count[di]
	add	di,ax
	add	bl_count[di],ax
	mov	di,[si.td_max_length]
	add	di,di
	dec	bl_count[di]
	sub	cx,ax
	xor	ax,ax
	cmp	cx,ax
	jg	gen_bitlen_overflow
	mov	dx,[si.td_max_length]
	mov	cx,HEAP_SIZE
    gen_bitlen_recompute:
	mov	di,dx
	add	di,di
	mov	bp,bl_count[di]
    gen_bitlen_while_n:
	test	bp,bp
	jz	gen_bitlen_max_next
	dec	cx
	mov	di,cx
	add	di,di
	mov	ax,[di+bx.heap]
	cmp	ax,[si.td_max_code]
	ja	gen_bitlen_while_n
	shl	ax,2
	mov	di,ax
	add	di,[si.td_dyn_tree]
	mov	ax,[di.ct_len]
	cmp	ax,dx
	je	gen_bitlen_do
	mov	[di.ct_len],dx
	xchg	ax,dx
	sub	ax,dx
	imul	[di.ct_freq]
	add	word ptr [bx.opt_len],ax
	adc	word ptr [bx.opt_len+2],dx
	mov	ax,word ptr [bx.opt_len]
	mov	dx,word ptr [bx.opt_len+2]
	mov	dx,[di.ct_len]
    gen_bitlen_do:
	dec	bp
	jnz	gen_bitlen_while_n
    gen_bitlen_max_next:
	dec	dx
	jnz	gen_bitlen_recompute
	jmp	gen_bitlen_end
gen_bitlen ENDP

gen_codes PROC
	push	si
	push	di
	push	dx
	mov	si,ax
	lea	di,[bp.gc_next_code]
	xor	dx,dx
	mov	bx,dx
	mov	cx,MAX_BITS
	mov	ax,offset bl_count
    gen_codes_bits:
	mov	ax,bl_count[bx]
	add	bx,2
	add	ax,dx
	add	ax,ax
	mov	dx,ax
	mov	[di+bx],ax
	dec	cx
	jnz	gen_codes_bits
  ifdef DEBUG
	mov	ax,bl_count[2*MAX_BITS]
	add	ax,dx
	sub	ax,1
	assert	ax,<(1 shl MAX_BITS)-1>,je,"gen_codes"
  endif
	pop	cx
	inc	cx
	assert	cx,0,jne,"gen_codes"
    gen_codes_loop:
	mov	ax,[si.ct_len]
	test	ax,ax
	jz	gen_codes_next
	mov	bx,di
	add	bx,ax
	add	bx,ax
	mov	dx,ax
	mov	ax,[bx]
	inc	word ptr [bx]
	call	bi_reverse
	mov	[si.ct_code],ax
    gen_codes_next:
	add	si,SIZE CT_DATA
	dec	cx
	jnz	gen_codes_loop
    gen_codes_end:
	pop	di
	pop	si
	ret
gen_codes ENDP

build_tree PROC
	push	si
	push	di
	mov	si,ax
	mov	dx,-1
	xor	cx,cx
	mov	[bp.heap_len],cx
	mov	[bp.heap_max],HEAP_SIZE
	mov	di,cx
	mov	bx,[si.td_dyn_tree]
	jmp	build_tree_elems
    build_tree_zero:
	mov	[bx.ct_len],ax
	jmp	build_tree_next
    build_tree_loop:
	mov	ax,[bx.ct_freq]
	test	ax,ax
	jz	build_tree_zero
	xchg	cx,si
	mov	[bp+si.depth],0
	xchg	cx,si
	inc	di
	add	di,di
	mov	[bp+di.heap],cx
	shr	di,1
	mov	dx,cx
    build_tree_next:
	add	bx,SIZE CT_DATA
	inc	cx
    build_tree_elems:
	cmp	cx,[si.td_elems]
	jb	build_tree_loop
    build_tree_break:
	mov	[bp.bt_next_node],cx
	mov	cx,2
    build_tree_while_b:
	cmp	di,cx
	jnb	build_tree_b_end
	xor	ax,ax
	cmp	dx,cx
	jnl	build_tree_b_00
	inc	dx
	mov	ax,dx
    build_tree_b_00:
	inc	di
	mov	bx,di
	add	di,di
	mov	[bp+di.heap],ax
	mov	di,ax
	mov	[bp+di.depth],ch
	mov	di,bx
	mov	bx,[si.td_dyn_tree]
	shl	ax,cl
	add	bx,ax
	mov	[bx.ct_freq],1
	sub	word ptr [bp.opt_len],1
	sbb	word ptr [bp.opt_len+2],0
	cmp	[si.td_static_tree],0
	je	build_tree_while_b
	add	ax,[si.td_static_tree]
	mov	bx,ax
	mov	ax,[bx.ct_len]
	sub	word ptr [bp.static_len],ax
	sbb	word ptr [bp.static_len+2],0
	jmp	build_tree_while_b
    build_tree_b_end:
	mov	ax,dx
	mov	[si.td_max_code],ax
	mov	[bp.bt_max_code],ax
	mov     [bp.heap_len],di
	shr	di,1
	jz	build_tree_do
    build_tree_pqdown:
	mov	ax,[si.td_dyn_tree]
	mov	dx,di
	call	pqdownheap
	dec	di
	jnz	build_tree_pqdown
    build_tree_do:
	mov	bx,bp
	mov	di,[bx.heap_len]
	dec	[bx.heap_len]
	add	di,di
	mov	ax,[bx+di.heap]
	mov	di,[bx.heap+2]
	mov	[bx.heap+2],ax
	mov	dx,SMALLEST
	mov	ax,[si.td_dyn_tree]
	call	pqdownheap
	mov	ax,[bx.heap_max]
	dec	ax
	add	bx,ax
	add	bx,ax
	dec	ax
	mov	[bp.heap_max],ax
	mov	ax,[bp.heap+2]
	mov	[bx.heap],di
	mov	[bx.heap-2],ax
	push	di
	mov	bx,bp
	mov	dx,ax
	add	bx,ax
	mov	al,[bx.depth]
	mov	bx,bp
	mov	ah,[bx+di.depth]
	cmp	ah,al
	jb	build_tree_do_00
	mov	al,ah
    build_tree_do_00:
	inc	al
	mov	cx,[bx.bt_next_node]
	add	bx,cx
	mov	[bx.depth],al
	mov	bx,dx
	mov	dx,cx
	mov	cx,2
	mov	ax,[si.td_dyn_tree]
	shl	bx,cl
	add	bx,ax
	shl	dx,cl
	add	dx,ax
	shl	di,cl
	add	di,ax
	mov	ax,[bx.ct_freq]
	add	ax,[di.ct_freq]
	xchg	bx,dx
	mov	[bx.ct_freq],ax
	mov	ax,[bp.bt_next_node]
	mov	[di.ct_dad],ax
	mov	bx,dx
	mov	[bx.ct_dad],ax
	pop	di
	mov	bx,bp
	mov	[bx.heap+2],ax
	inc	ax
	mov	[bx.bt_next_node],ax
	mov	ax,[si.td_dyn_tree]
	mov	dx,SMALLEST
	call	pqdownheap
	cmp	[bx.heap_len],2
	jae	build_tree_do
	mov	ax,[bp.heap+2*SMALLEST]
	dec	[bp.heap_max]
	mov	bx,[bp.heap_max]
	add	bx,bx
	add	bx,bp
	mov	[bx.heap],ax
	mov	ax,si
	call	gen_bitlen
	mov	ax,[si.td_dyn_tree]
	mov	dx,[bp.bt_max_code]
	pop	di
	pop	si
	jmp	gen_codes
build_tree ENDP

pqdownheap PROC
	push	bp
	push	si
	push	di
	mov	si,bp
	mov	bx,ax
	mov	bp,dx
	add	bp,bp
	mov	di,bp
	add	di,di
	mov	cx,[si+bp.heap]
	xchg	si,bx
    pqdownheap_loop:
	cmp	bp,[bx.heap_len]
	ja	pqdownheap_break
	je	pqdownheap_00
	mov	ax,[bx+di.heap+2]
	call	smaller
	jz	pqdownheap_00
	inc	bp
    pqdownheap_00:
	mov	di,bp
	add	di,di
	mov	ax,cx
	call	smaller
	jnz	pqdownheap_break
	mov	ax,[bx+di.heap]
	add	dx,dx
	mov	di,dx
	mov	[bx+di.heap],ax
	mov	dx,bp
	add	bp,bp
	mov	di,bp
	add	di,di
	jmp	pqdownheap_loop
    pqdownheap_break:
	add	dx,dx
	mov	di,dx
	mov	[bx+di.heap],cx
	pop	di
	pop	si
	pop	bp
	ret
pqdownheap ENDP

smaller	PROC
	push	di
	push	cx
	mov	cx,[bx+di.heap]
	mov	di,ax
	mov	ah,[bx+di.depth]
	xchg	di,cx
	mov	al,[bx+di.depth]
	shl	cx,2
	shl	di,2
	xchg	si,bx
	mov	di,[bx+di.ct_freq]
	xchg	cx,di
	mov	di,[bx+di.ct_freq]
	cmp	di,cx;[bx+di.ct_freq]
	xchg	si,bx
	pop	cx
	pop	di
	jb	smaller_01
	jne	smaller_00
	cmp	ah,al
	ja	smaller_00
    smaller_01:
	or	ax,1
	ret
    smaller_00:
	xor	ax,ax
	ret
smaller	ENDP

send_bits PROC
	push	si
	push	di
	mov	si,ax
	mov	di,dx
  ifdef DEBUG
	assert	dx,0,ja,  "invalid length"
	assert	dx,15,jbe,"invalid length"
	add	word ptr [bp.bits_sent],dx
	adc	word ptr [bp.bits_sent+2],0
  endif
	mov	bx,[bp.bi_buf]
	mov	cx,[bp.bi_valid]
	shl	ax,cl
	or	ax,bx
	add	cx,dx
	mov	[bp.bi_buf],ax
	mov	[bp.bi_valid],cx
	cmp	cx,16
	jbe	send_bits_00
	call	putshort
	mov	ax,[bp.bi_valid]
	sub	ax,16
	mov	[bp.bi_valid],ax
	mov	cx,di
	sub	cx,ax
	shr	si,cl
	mov	[bp.bi_buf],si
    send_bits_00:
	pop	di
	pop	si
	ret
send_bits ENDP

s_tree_init PROC
	mov	[bp.st_max_code],dx
	mov	di,ax
	xor	ax,ax
	mov	si,ax
	dec	ax
	mov	[bp.st_prevlen],ax
	mov	ax,[di.ct_len]
	mov	[bp.st_nextlen],ax
	mov	[bp.st_count],si
	mov	[bp.st_max_count],7
	mov	[bp.st_min_count],4
	test	ax,ax
	jnz	s_tree_init_end
	mov	[bp.st_max_count],138
	dec	[bp.st_min_count]
    s_tree_init_end:
	ret
s_tree_init ENDP

s_tree_loop PROC
	cmp	si,[bp.st_max_code]
	ja	s_tree_exit
	mov	ax,[bp.st_nextlen]
	mov	[bp.st_curlen],ax
	mov	bx,si
	inc	bx
	shl	bx,2
	mov	ax,[di+bx.ct_len]
	mov	[bp.st_nextlen],ax
	inc	[bp.st_count]
	mov	bx,[bp.st_count]
	cmp	bx,[bp.st_max_count]
	jae	s_tree_elif
	cmp	ax,[bp.st_curlen]
	jne	s_tree_elif
	inc	si
	jmp	s_tree_loop
    s_tree_elif:
	or	al,1
	ret
    s_tree_exit:
	xor	ax,ax
	ret
s_tree_loop ENDP

s_tree_set PROC
	inc	si
	mov	ax,[bp.st_curlen]
	mov	[bp.st_prevlen],ax
	mov	[bp.st_count],0
	mov	[bp.st_max_count],7
	mov	[bp.st_min_count],4
	mov	ax,[bp.st_nextlen]
	test	ax,ax
	jz	s_tree_set_00
	cmp	ax,[bp.st_curlen]
	jne	s_tree_set_01
	dec	[bp.st_max_count]
	dec	[bp.st_min_count]
	ret
    s_tree_set_00:
	mov	[bp.st_max_count],138
	dec	[bp.st_min_count]
    s_tree_set_01:
	ret
s_tree_set ENDP

scan_tree PROC
	push	si
	push	di
	call	s_tree_init
	inc	dx
	shl	dx,2
	mov	bx,dx
	mov	[di+bx.ct_len],-1
    scan_tree_loop:
	call	s_tree_loop
	jz	scan_tree_exit
	cmp	bx,[bp.st_min_count]
	jae	scan_tree_elif_00
	lea	bx,[bp.bl_tree]
	mov	ax,[bp.st_curlen]
	shl	ax,2
	add	bx,ax
	mov	ax,[bp.st_count]
	add	[bx.ct_freq],ax
	jmp	scan_tree_next
    scan_tree_elif_00:
	mov	ax,[bp.st_curlen]
	test	ax,ax
	jz	scan_tree_elif_10
	cmp	ax,[bp.st_prevlen]
	je	scan_tree_REP_3_6
	shl	ax,2
	add	ax,bp
	mov	bx,ax
	inc	[bx.bl_tree.ct_freq]
      scan_tree_REP_3_6:
	mov	ax,REP_3_6*4
	jmp	scan_tree_inc
    scan_tree_elif_10:
	cmp	[bp.st_count],10
	ja	scan_tree_else
	mov	ax,REPZ_3_10*4
	jmp	scan_tree_inc
    scan_tree_else:
	mov	ax,REPZ_11_138*4
    scan_tree_inc:
	add	ax,bp
	mov	bx,ax
	inc	[bx.bl_tree.ct_freq]
    scan_tree_next:
	call	s_tree_set
	jmp	scan_tree_loop
    scan_tree_exit:
	pop	di
	pop	si
	ret
scan_tree ENDP

send_tree PROC
	push	si
	push	di
	call	s_tree_init
    send_tree_loop:
	call    s_tree_loop
	jz	send_tree_exit
	cmp	bx,[bp.st_min_count]
	jae	send_tree_elif_00
    send_tree_do:
	mov	bx,[bp.st_curlen]
	shl	bx,2
	add	bx,bp
	mov	ax,[bx.bl_tree.ct_code]
	mov	dx,[bx.bl_tree.ct_len]
	call	send_bits
	dec	[bp.st_count]
	jnz	send_tree_do
	jmp	send_tree_next
    send_tree_elif_00:
	mov	bx,[bp.st_curlen]
	test	bx,bx
	jz	send_tree_elif_10
	cmp	bx,[bp.st_prevlen]
	je	send_tree_elif_000
	shl	bx,2
	add	bx,bp
	mov	ax,[bx.bl_tree.ct_code]
	mov	dx,[bx.bl_tree.ct_len]
	call	send_bits
	dec	[bp.st_count]
      send_tree_elif_000:
	mov	ax,[bp.st_count]
	assert	ax,3,jae,"send_tree: 3_6?"
	assert	ax,6,jbe,"send_tree: 3_6?"
	mov	ax,[bp.bl_tree.ct_code+4*REP_3_6]
	mov	dx,[bp.bl_tree.ct_len +4*REP_3_6]
	call	send_bits
	mov	ax,[bp.st_count]
	sub	ax,3
	mov	dx,2
	call	send_bits
	jmp	send_tree_next
    send_tree_elif_10:
	cmp	[bp.st_count],10
	ja	send_tree_else
	mov	ax,[bp.bl_tree.ct_code+4*REPZ_3_10]
	mov	dx,[bp.bl_tree.ct_len +4*REPZ_3_10]
	call	send_bits
	mov	ax,[bp.st_count]
	sub	ax,3
	mov	dx,3
	call	send_bits
	jmp	send_tree_next
    send_tree_else:
	mov	ax,[bp.bl_tree.ct_code+4*REPZ_11_138]
	mov	dx,[bp.bl_tree.ct_len +4*REPZ_11_138]
	call	send_bits
	mov	ax,[bp.st_count]
	sub	ax,11
	mov	dx,7
	call	send_bits
    send_tree_next:
	call	s_tree_set
	jmp	send_tree_loop
    send_tree_exit:
	pop	di
	pop	si
	ret
send_tree ENDP

build_bl_tree PROC
	lea	ax,[bp.dyn_ltree]
	mov	dx,l_desc.td_max_code
	call	scan_tree
	lea	ax,[bp.dyn_dtree]
	mov	dx,d_desc.td_max_code
	call	scan_tree
	mov	ax,offset bl_desc
	call	build_tree
	mov	cx,BL_CODES-1
    build_bl_tree_loop:
	cmp	cx,4
	jb	build_bl_tree_end
	mov	bx,cx
	mov	al,bl_order[bx]
	mov	ah,0
	shl	ax,2
	add	ax,bp
	mov	bx,ax
	mov	ax,[bx.bl_tree.ct_len]
	test	ax,ax
	jnz	build_bl_tree_end
	dec	cx
	jmp	build_bl_tree_loop
    build_bl_tree_end:
	mov	ax,3
	inc	cx
	imul	cx
	assert	dx,0,je,"overflow"
	dec	cx
	add	ax,5+5+4
	adc	dx,0
	add	word ptr [bp.opt_len],ax
	adc	word ptr [bp.opt_len+2],dx
	mov	ax,cx
	ret
build_bl_tree ENDP

send_all_trees PROC
	push	si	; ax: lcodes
	push	di	; dx: dcodes
	push	bx	; bx: blcodes
	assert	ax,L_CODES,jbe,"too many codes"
	assert	dx,D_CODES,jbe,"too many codes"
	assert	bx,BL_CODES,jbe,"too many codes"
	assert	ax,257,jae,"not enough codes"
	assert	dx,1,jae,"not enough codes"
	assert	bx,4,jae,"not enough codes"
	mov	si,ax
	mov	di,dx
	sub	ax,257
	mov	dx,5
	call	send_bits
	mov	ax,di
	dec	ax
	mov	dx,5
	call	send_bits
	pop	ax
	push	di
	push	si
	push	ax
	mov	dx,4
	sub	ax,dx
	call	send_bits
	pop	di
	xor	si,si
    send_all_trees_loop:
	cmp	si,di
	jnb	send_all_trees_end
	mov	al,bl_order[si]
	mov	ah,0
	shl	ax,2
	add	ax,bp
	mov	bx,ax
	mov	ax,[bx.bl_tree.ct_len]
	mov	dx,3
	call	send_bits
	inc	si
	jmp	send_all_trees_loop
    send_all_trees_end:
	pop	dx
	dec	dx
	lea	ax,[bp.dyn_ltree]
	call	send_tree
	lea	ax,[bp.dyn_dtree]
	pop	dx
	dec	dx
	call	send_tree
	pop	di
	pop	si
	ret
send_all_trees ENDP

flush_block PROC
	mov	[bp.fb_eof],ax
  ifdef __386__
	xor	eax,eax
	cmp	word ptr [bp.block_start+2],ax
	jl	flush_block_00
	mov	eax,STDI.ios_bp
	mov	ax,word ptr [bp.block_start]
    flush_block_00:
	mov	[bp.fb_buf],eax
	movzx	eax,[bp.str_start]
	sub	eax,[bp.block_start]
	mov	[bp.fb_stored_ln],eax
  ifdef DEBUG
	add	[bp.input_len],eax
  endif
	push	si
	push	di
	mov	al,[bp.flags]
	mov	bx,[bp.last_flags]
	mov	flag_buf[bx],al
	mov	ax,offset l_desc
	call	build_tree
	mov	ax,offset d_desc
	call	build_tree
	call	build_bl_tree
	mov	bx,bp
	mov	si,ax
	mov	eax,[bx.opt_len]
	add	eax,3+7
	shr	eax,3
	mov	[bx.fb_opt_len],eax
	mov	eax,[bx.static_len]
	add	eax,3+7
	shr	eax,3
	mov	[bx.fb_static_ln],eax
	cmp	eax,[bx.fb_opt_len]
	ja	flush_block_01
	mov	[bx.fb_opt_len],eax
    flush_block_01:
    	mov	eax,[bx.fb_opt_len]
	cmp	[bx.fb_stored_ln],eax
	ja	flush_block_else_01
	xor	eax,eax
	cmp	[bx.fb_eof],ax
	je	flush_block_else_01
	cmp	file_method,al
	je	flush_block_else_01
	cmp	[bx.cmpr_bytelen],eax
	jne	flush_block_else_01
	cmp	[bx.cmpr_lenbits],eax
	jne	flush_block_else_01
	call	copy_block
	jz	flush_block_eof
	mov	bx,bp
        movmx	[bx.cmpr_bytelen],[bx.fb_stored_ln]
	mov	file_method,METHOD_STORE
	jmp	flush_block_endif
    flush_block_else_01:
	mov	eax,[bx.fb_buf]
	test	eax,eax
	jz	flush_block_else_02
	mov	eax,[bx.fb_stored_ln]
	add	eax,4
	cmp	eax,[bx.fb_opt_len]
	ja	flush_block_else_02
	mov	ax,[bx.fb_eof]
	add	ax,STORED_BLOCK*2
	mov	dx,3
	call	send_bits
	mov	bx,bp
	mov	eax,[bx.cmpr_lenbits]
	add	eax,3+7
        shr	eax,3
	add	eax,[bx.fb_stored_ln]
	add	eax,4
	add	[bx.cmpr_bytelen],eax
	xor	eax,eax
	mov	[bx.cmpr_lenbits],eax
	inc	ax
	call	copy_block
	jz	flush_block_eof
	mov	bx,bp
	jmp	flush_block_endif
    flush_block_else_02:
	cmpmm	[bx.fb_static_ln],[bx.fb_opt_len]
	jne	flush_block_else_03
	mov	ax,[bx.fb_eof]
	add	ax,STATIC_TREES*2
	mov	dx,3
	call	send_bits
	lea	ax,[bp.static_ltree]
	lea	dx,[bp.static_dtree]
	call	compress_block
	mov	eax,[bp.static_len]
	jmp	flush_block_else_04
    flush_block_else_03:
	mov	ax,[bp.fb_eof]
	add	ax,DYN_TREES*2
	mov	dx,3
	call	send_bits
	mov	ax,l_desc.td_max_code
	inc	ax
	mov	dx,d_desc.td_max_code
	inc	dx
	mov	bx,si
	inc	bx
	call	send_all_trees
	lea	ax,[bp.dyn_ltree]
	lea	dx,[bp.dyn_dtree]
	call	compress_block
	mov	bx,bp
	mov	eax,[bx.opt_len]
    flush_block_else_04:
	mov	bx,bp
	xor	cx,cx
	add	eax,3
	add	eax,[bx.cmpr_lenbits]
	mov	word ptr [bx.cmpr_lenbits+2],cx
	mov	cx,ax
	and	cx,7
	mov	word ptr [bx.cmpr_lenbits],cx
        shr	eax,3
	add	[bx.cmpr_bytelen],eax
    flush_block_endif:
	call	init_block
	mov	ax,[bp.fb_eof]
	test	ax,ax
	jz	flush_block_end
	call	bi_windup
	jz	flush_block_eof
	add	[bp.cmpr_lenbits],7
  else
	xor	ax,ax
	mov	dx,ax
	cmp	word ptr [bp.block_start+2],ax
	jl	flush_block_00
	mov	dx,word ptr STDI.ios_bp+2
	mov	ax,word ptr [bp.block_start]
    flush_block_00:
	mov	word ptr [bp.fb_buf],ax
	mov	word ptr [bp.fb_buf+2],dx
	xor	dx,dx
	mov	ax,[bp.str_start]
	sub	ax,word ptr [bp.block_start]
	sbb     dx,word ptr [bp.block_start+2]
	mov	word ptr [bp.fb_stored_ln+2],dx
	mov	word ptr [bp.fb_stored_ln],ax
  ifdef DEBUG
	add	word ptr [bp.input_len],ax
	adc     word ptr [bp.input_len+2],dx
  endif
	push	si
	push	di
	mov	al,[bp.flags]
	mov	bx,[bp.last_flags]
	mov	flag_buf[bx],al
	mov	ax,offset l_desc
	call	build_tree
	mov	ax,offset d_desc
	call	build_tree
	call	build_bl_tree
	mov	bx,bp
	mov	si,ax
	mov	ax,word ptr [bx.opt_len]
	mov	dx,word ptr [bx.opt_len+2]
	add	ax,3+7
	adc	dx,0
	mov	cx,3
	call	SHR32
	mov	word ptr [bx.fb_opt_len],ax
	mov	word ptr [bx.fb_opt_len+2],dx
	mov	ax,word ptr [bx.static_len]
	mov	dx,word ptr [bx.static_len+2]
	add	ax,3+7
	adc	dx,0
	mov	cx,3
	call	SHR32
	mov	word ptr [bx.fb_static_ln],ax
	mov	word ptr [bx.fb_static_ln+2],dx
	cmprm	[bx.fb_opt_len]
	ja	flush_block_01
	mov	word ptr [bx.fb_opt_len],ax
	mov	word ptr [bx.fb_opt_len+2],dx
    flush_block_01:
	cmpmm	[bx.fb_stored_ln],[bx.fb_opt_len]
	ja	flush_block_else_01
	xor	ax,ax
	cmp	[bx.fb_eof],ax
	je	flush_block_else_01
	cmp	file_method,al
	je	flush_block_else_01
	cmp	word ptr [bx.cmpr_bytelen],ax
	jne	flush_block_else_01
	cmp	word ptr [bx.cmpr_bytelen+2],ax
	jne	flush_block_else_01
	cmp	word ptr [bx.cmpr_lenbits],ax
	jne	flush_block_else_01
	cmp	word ptr [bx.cmpr_lenbits+2],ax
	jne	flush_block_else_01
	assert	word ptr [bx.fb_buf+2],ax,jne,"block vanished"
	call	copy_block
	jz	flush_block_eof
	mov	bx,bp
        movmx	[bx.cmpr_bytelen],[bx.fb_stored_ln]
	mov	file_method,METHOD_STORE
	jmp	flush_block_endif
    flush_block_else_01:
	mov	ax,word ptr [bx.fb_buf+2]
	or	ax,word ptr [bx.fb_buf]
	jz	flush_block_else_02
	mov	ax,word ptr [bx.fb_stored_ln]
	mov	dx,word ptr [bx.fb_stored_ln+2]
	add	ax,4
	adc	dx,0
	cmprm	[bx.fb_opt_len]
	ja	flush_block_else_02
	mov	ax,[bx.fb_eof]
	add	ax,STORED_BLOCK*2
	mov	dx,3
	call	send_bits
	mov	bx,bp
	mov	ax,word ptr [bx.cmpr_lenbits]
	mov	dx,word ptr [bx.cmpr_lenbits+2]
	add	ax,3+7
	adc	dx,0
	mov	cx,3
	call	SHR32
	add	ax,word ptr [bx.fb_stored_ln]
	adc	dx,word ptr [bx.fb_stored_ln+2]
	add	ax,4
	adc	dx,0
	add	word ptr [bx.cmpr_bytelen],ax
	adc	word ptr [bx.cmpr_bytelen+2],dx
	xor	ax,ax
	mov	word ptr [bx.cmpr_lenbits],ax
	mov	word ptr [bx.cmpr_lenbits+2],ax
	inc	ax
	call	copy_block
	jz	flush_block_eof
	mov	bx,bp
	jmp	flush_block_endif
    flush_block_else_02:
	cmpmm	[bx.fb_static_ln],[bx.fb_opt_len]
	jne	flush_block_else_03
	mov	ax,[bx.fb_eof]
	add	ax,STATIC_TREES*2
	mov	dx,3
	call	send_bits
	lea	ax,[bp.static_ltree]
	lea	dx,[bp.static_dtree]
	call	compress_block
	mov	ax,word ptr [bp.static_len]
	mov	dx,word ptr [bp.static_len+2]
	jmp	flush_block_else_04
    flush_block_else_03:
	mov	ax,[bp.fb_eof]
	add	ax,DYN_TREES*2
	mov	dx,3
	call	send_bits
	mov	ax,l_desc.td_max_code
	inc	ax
	mov	dx,d_desc.td_max_code
	inc	dx
	mov	bx,si
	inc	bx
	call	send_all_trees
	lea	ax,[bp.dyn_ltree]
	lea	dx,[bp.dyn_dtree]
	call	compress_block
	mov	bx,bp
	mov	ax,word ptr [bx.opt_len]
	mov	dx,word ptr [bx.opt_len+2]
    flush_block_else_04:
	mov	bx,bp
	xor	cx,cx
	add	ax,3
	adc	dx,cx
	add	ax,word ptr [bx.cmpr_lenbits]
	adc	dx,word ptr [bx.cmpr_lenbits+2]
	mov	word ptr [bx.cmpr_lenbits+2],cx
	mov	cx,ax
	and	cx,7
	mov	word ptr [bx.cmpr_lenbits],cx
	mov	cx,3
	call	SHR32
	add	word ptr [bx.cmpr_bytelen],ax
	adc	word ptr [bx.cmpr_bytelen+2],dx
    flush_block_endif:
  ifdef DEBUG
	mov	ax,word ptr [bx.cmpr_bytelen]
	mov	dx,word ptr [bx.cmpr_bytelen+2]
	mov	cx,3
	call	SHL32
	add	ax,word ptr [bx.cmpr_lenbits]
	adc	dx,word ptr [bx.cmpr_lenbits+2]
	xchg	ax,dx
	mov	cx,word ptr [bx.bits_sent]
	mov	bx,word ptr [bx.bits_sent+2]
	assert	ax,bx,je,"bad compressed size"
	assert	dx,cx,je,"bad compressed size"
  endif
	call	init_block
	mov	ax,[bp.fb_eof]
	test	ax,ax
	jz	flush_block_end
  ifdef	DEBUG
	mov	ax,word ptr [bp.input_len]
	mov	dx,word ptr [bp.input_len+2]
	assert	ax,word ptr [bp.input_size],je,"bad input size"
	assert	dx,word ptr [bp.input_size+2],je,"bad input size"
  endif
	call	bi_windup
	jz	flush_block_eof
	add	word ptr [bp.cmpr_lenbits],7
	adc	word ptr [bp.cmpr_lenbits+2],0
  endif
    flush_block_end:
	xor	ax,ax
	inc	ax
    flush_block_eof:
	pop	di
	pop	si
	ret
flush_block ENDP

ct_tally PROC
	push	si
	push	di
	mov	bx,bp
	les	di,[bx.l_buf]
	add	di,[bx.last_lit]
	mov	es:[di],dl
	inc	[bx.last_lit]
	test	ax,ax
	jnz	ct_tally_else
	assert	dh,0,je,"ct_tally"
	shl	dx,2
	mov	di,dx
	inc	[di+bx.dyn_ltree.ct_freq]
	jmp	ct_tally_02
    ct_tally_else:
	dec	ax
  ifdef	DEBUG
	assert	ax,MAX_DIST,jb,"ct_tally"
	push	dx
	shr	dx,2
	assert	dx,MAX_MATCH-MIN_MATCH,jbe,"ct_tally"
	mov	bx,ax
	cmp	ax,256
	jb	@@tally_1
	shr	bx,7
	add	bx,256
    @@tally_1:
	add	bx,bp
	mov	bl,[bx.dist_code]
	assert	bl,D_CODES,jb,"ct_tally"
	mov	bx,bp
	pop	dx
  endif
	mov	di,dx
	mov	dx,ax
	mov	ah,0
	mov	al,[di+bx.length_code]
	add	ax,LITERALS+1
	shl	ax,2
	mov	di,ax
	inc	[di+bx.dyn_ltree.ct_freq]
	mov	ax,256
	mov	di,dx
	cmp	dx,ax
	jb	ct_tally_01
	shr	di,7
	add	di,ax
    ct_tally_01:
	dec	ax
	mov	al,[di+bx.dist_code]
	shl	ax,2
	mov	di,ax
	inc	[di+bx.dyn_dtree.ct_freq]
	mov	ax,[bx.last_dist]
	add	ax,ax
	les	di,[bx.d_buf]
	add	di,ax
	inc	[bx.last_dist]
	mov	es:[di],dx
	mov	al,[bx.flag_bit]
	or	[bx.flags],al
    ct_tally_02:
	shl	[bx.flag_bit],1
	mov	ax,[bx.last_lit]
	and	ax,7
	jnz	ct_tally_03
	mov	al,[bx.flags]
	mov	di,[bx.last_flags]
	inc	[bx.last_flags]
	mov	flag_buf[di],al
	mov	[bx.flags],0
	mov	[bx.flag_bit],1
    ct_tally_03:
	cmp	[bx.compr_level],2
	jbe	ct_tally_04
	mov	ax,[bx.last_lit]
	mov	dl,ah
	and	ax,0FFFh
	jnz	ct_tally_04
	or	ah,dl
	mov	dh,al
	shl	ah,3
	shr	dl,5
	mov	si,dx
	mov	bp,ax
	xor	cx,cx
    ct_tally_loop:
	mov	di,cx
	shl	di,2
	mov	ax,[di+bx.dyn_dtree.ct_freq]
	shr	di,1
	mov	di,extra_dbits[di]
	add	di,5
	mul	di
	add	bp,ax
	adc	si,dx
	inc	cx
	cmp	cx,D_CODES
	jb	ct_tally_loop
	mov	cx,3
	mov	dx,si
	mov	ax,bp
	call	SHR32
	mov	bp,bx
	mov	di,ax
	mov	si,dx
	mov	ax,[bx.last_lit]
	mov	cx,1
	shr	ax,cl
	cmp	[bx.last_dist],ax
	jnb	ct_tally_04
	xor	dx,dx
	mov	ax,[bx.str_start]
	sub	ax,word ptr [bx.block_start]
	sbb	dx,word ptr [bx.block_start+2]
	shr	ax,cl
	shr	dx,cl
	jnc	ct_tally_cmp
	or	ah,80h
    ct_tally_cmp:
	xchg	cx,ax
	cmp	si,dx
	jne	ct_tally_cmp_00
	cmp	di,cx
    ct_tally_cmp_00:
	jb	ct_tally_end
    ct_tally_04:
	mov	ax,1
	cmp	[bx.last_lit],LIT_BUFSIZE-1
	je	ct_tally_end
	cmp	[bx.last_dist],DIST_BUFSIZE
	je	ct_tally_end
	dec	ax
    ct_tally_end:
	test	ax,ax
	pop	di
	pop	si
	ret
ct_tally ENDP

compress_block PROC
	push	si
	push	di
	mov	si,ax
	mov	di,dx
	xor	ax,ax
	mov	bx,bp
	mov	[bx.cb_lx],ax
	mov	[bx.cb_dx],ax
	mov	[bx.cb_fx],ax
	mov	[bx.cb_flag],al
	cmp	[bx.last_lit],ax
	je	compress_block_end
    compress_block_do:
	mov	ax,[bp.cb_lx]
	test	al,7
	jnz	compress_block_01
	mov	bx,[bp.cb_fx]
	inc	[bp.cb_fx]
	mov	bl,flag_buf[bx]
	mov	[bp.cb_flag],bl
    compress_block_01:
	inc	[bp.cb_lx]
	les	bx,[bp.l_buf]
	add	bx,ax
	mov	al,es:[bx]
	mov	ah,0
	mov	[bp.cb_lc],ax
	test	[bp.cb_flag],1
	jnz	compress_block_else
	shl	ax,2
	mov	bx,ax
	mov	ax,[si+bx.ct_code]
	mov	dx,[si+bx.ct_len]
	call	send_bits
	jmp	compress_block_endif
    compress_block_else:
	mov	bx,[bp.cb_lc]
	add	bx,bp
	mov	al,[bx.length_code]
	mov	ah,0
	mov	[bp.cb_code],ax
	add	ax,LITERALS+1
	shl	ax,2
	mov	bx,ax
	mov	ax,[si+bx.ct_code]
	mov	dx,[si+bx.ct_len]
	call	send_bits
	mov	bx,[bp.cb_code]
	add	bx,bx
	mov	ax,extra_lbits[bx]
	mov	[bp.cb_extra],ax
	test	ax,ax
	jz	compress_block_03
	mov	dx,ax
	mov	bx,[bp.cb_code]
	add	bx,bx
	add	bx,bp
	mov	ax,[bp.cb_lc]
	sub	ax,[bx.base_length]
	mov	[bp.cb_lc],ax
	call	send_bits
    compress_block_03:
	mov	ax,[bp.cb_dx]
	inc	[bp.cb_dx]
	add	ax,ax
	les	bx,[bp.d_buf]
	add	bx,ax
	mov	ax,es:[bx]
	mov	[bp.cb_dist],ax
	mov	bx,256
	cmp	ax,bx
	jb	compress_block_04
	shr	ax,7
	add	ax,bx
    compress_block_04:
	mov	bx,ax
	add	bx,bp
	mov	al,[bx.dist_code]
	mov	ah,0
	mov	[bp.cb_code],ax
	assert	ax,D_CODES,jb,"bad d_code"
	shl	ax,2
	mov	bx,ax
	mov	ax,[di+bx.ct_code]
	mov	dx,[di+bx.ct_len]
	call	send_bits
	mov	bx,[bp.cb_code]
	add	bx,bx
	mov	ax,extra_dbits[bx]
	mov	[bp.cb_extra],ax
	test	ax,ax
	jz	compress_block_endif
	mov	dx,ax
	mov	ax,[bp.cb_dist]
	add	bx,bp
	sub	ax,[bx.base_dist]
	mov	[bp.cb_dist],ax
	call	send_bits
    compress_block_endif:
	shr	[bp.cb_flag],1
	mov	ax,[bp.cb_lx]
	cmp	ax,[bp.last_lit]
	jnb	compress_block_end
	jmp	compress_block_do
    compress_block_end:
	mov	ax,[si+4*END_BLOCK+ct_code]
	mov	dx,[si+4*END_BLOCK+ct_len]
	call	send_bits
	pop	di
	pop	si
	ret
compress_block ENDP

bi_reverse PROC
	assert	dx,0,jne,	"bi_reverse"
	assert	dx,MAX_BITS,jbe,"bi_reverse"
	xor	bx,bx
    bi_reverse_loop:
	shr	ax,1
	adc	bx,0
	shl	bx,1
	dec	dx
	jnz	bi_reverse_loop
	mov	ax,bx
	shr	ax,1
	ret
bi_reverse ENDP

bi_windup PROC
	xor	ax,ax
	mov	cx,[bp.bi_valid]
	mov	dx,[bp.bi_buf]
	mov	[bp.bi_buf],ax
	mov	[bp.bi_valid],ax
	test	cx,cx
	jz	bi_windup_ok
	mov	al,dl
	call	oputc
	jz	bi_windup_eof
	cmp	cx,8
	jbe	bi_windup_ok
	mov	al,dh
	call	oputc
  ifdef DEBUG
	jnz	bi_windup_ok
  endif
	ret
    bi_windup_ok:
	inc	ax
  ifdef DEBUG
	mov	ax,word ptr [bp.bits_sent]
	mov	dx,word ptr [bp.bits_sent+2]
	add	ax,7
	adc	dx,0
	and	ax,not 7
	mov	word ptr [bp.bits_sent],ax
	mov	word ptr [bp.bits_sent+2],dx
	xor	ax,ax
	inc	ax
  endif
    bi_windup_eof:
	ret
bi_windup ENDP

copy_block PROC
	push	si
	mov	si,ax
	call	bi_windup
	jz	copy_block_eof
	test	si,si
	jz	copy_block_flush
	mov	ax,word ptr [bp.fb_stored_ln]
	call	putshort
	jz	copy_block_eof
	mov	ax,word ptr [bp.fb_stored_ln]
	not	ax
	call	putshort
	jz	copy_block_eof
  ifdef DEBUG
	add	word ptr [bp.bits_sent],2*16
	adc	word ptr [bp.bits_sent+2],0
  endif
    copy_block_flush:
	call	flush_buf
  ifdef DEBUG
	pushf
	xor	dx,dx
	mov	ax,word ptr [bp.fb_stored_ln]
	mov	cx,3
	call	SHL32
	add	word ptr [bp.bits_sent],ax
	adc	word ptr [bp.bits_sent+2],dx
	popf
  endif
    copy_block_eof:
	pop	si
	ret
copy_block ENDP

update_hash PROC
	les	ax,STDI
	assert	ax,0,je,"update_hash"
	mov	al,es:[bx]
	mov	dx,ax
	mov	ax,[bp.ins_h]
	shl	ax,H_SHIFT
	xor	ax,dx
	and	ax,HASH_MASK
	mov	[bp.ins_h],ax
	ret
update_hash ENDP

insert_string PROC
	mov	bx,[bp.str_start]
	add	bx,MIN_MATCH-1
	call	update_hash
	sub	bx,MIN_MATCH-1
	xchg	ax,bx
	add	bx,bx
	mov	dx,[bp.head]
	mov	es,dx
	mov	si,es:[bx]
	mov	es:[bx],ax
	mov	dx,[bp.prev]
	mov	es,dx
	and	ax,WMASK
	add	ax,ax
	mov	bx,ax
	mov	es:[bx],si
	ret
insert_string ENDP

lm_init	PROC
	xor	ax,ax
	cmp	word ptr [bp.window_size+2],ax
	jne	lm_init_config
	cmp	word ptr [bp.window_size],ax
	jne	lm_init_config
	mov	word ptr [bp.window_size],ax
	inc	ax
	mov	word ptr [bp.window_size+2],ax
	mov	[bp.sliding],ax
    lm_init_config:
	mov	ax,[bp.compr_level]
	shl	ax,3
	mov	bx,ax
	mov	ax,config_table[bx].max_lazy
	mov	[bp.max_lazy_match],ax
	mov	ax,config_table[bx].good_length
	mov	[bp.good_match],ax
  ifndef FULL_SEARCH
	mov	ax,config_table[bx].nice_length
	mov	[bp.nice_match],ax
  endif
	mov	ax,config_table[bx].max_chain
	mov	[bp.max_chain_len],ax
	mov	ax,[bp.compr_level]
	cmp	al,1
	jne	lm_init_slow
	mov	[bp.compr_flags],FAST
    lm_init_slow:
	cmp	al,9
	jne	lm_init_read
	mov	[bp.compr_flags],SLOW
    lm_init_read:
	call    ofread
  ifdef	DEBUG
	add	word ptr [bp.input_size],ax
	adc	word ptr [bp.input_size+2],0
	test	ax,ax
  endif
	mov	[bp.lookahead],ax
	jz	lm_init_eof
	cmp	ax,MIN_LOOKAHEAD
	jae	lm_init_hash
	call	fill_window
    lm_init_hash:
	xor	bx,bx
	mov	[bp.ins_h],bx
	call	update_hash
	inc	bx
	call	update_hash
	test	bx,bx
    lm_init_end:
	ret
    lm_init_eof:
	mov	[bp.eofile],1
	jmp	lm_init_end
lm_init	ENDP

longest_match PROC
	push	ds
	push	si
	push	di
	cld
	mov     si,ax
	mov	ax,word ptr STDI.ios_bp+2
	mov	es,ax
	mov     ax,[bp.str_start]
	assert	ax,0-MIN_LOOKAHEAD,jbe,"insufficient lookahead"
	mov	dx,ax
	mov	di,ax
	sub	dx,MAX_DIST
	jae	longest_match_limit_ok
	xor	dx,dx
    longest_match_limit_ok:
	add	di,2
	mov	bx,[bp.prev_length]
	mov     ax,[bp.max_chain_len]
	cmp     bx,[bp.good_match]
	jb      longest_match_chain
	shr     ax,2
    longest_match_chain:
	mov	[bp.lm_chain_length],ax
	mov	ax,[bp.prev]
	mov	ds,ax
	mov	ax,es:[bx+di-3]
	mov	cx,es:[di-2]
	jmp	longest_match_scan
    longest_match_long:
	mov     cx,[di-2]
	mov	ax,[bp.prev]		; hot spot
	mov	ds,ax			;
	mov     ax,es:[bx+di-3]		; *
    longest_match_short:
	add	si,si			; ********
	dec	[bp.lm_chain_length]	; *
	mov	si,[si]			; ******
	jz	longest_match_end	; ******************
	cmp	si,dx			;
	jbe	longest_match_end	; *******
    longest_match_scan:
	cmp	ax,es:[bx+si-1]		; ********
	jne	longest_match_short	; ****************************
	cmp	cx,es:[si]		;
	jne	longest_match_short	; **
	mov	cx,es			;
	add	si,2			; *
	mov	ds,cx			;
	mov	cx,(MAX_MATCH-2)/2	; *
	mov	ax,di			;
	repe	cmpsw			; **
	je	longest_match_max	; ***
    longest_match_mis:
	mov	cl,[di-2]
	xchg	ax,di
	sub	cl,[si-2]
	sub	ax,di
	sub	si,2
	sub	si,ax
	sub	cl,1
	adc	ax,0
	cmp	ax,bx
	jle	longest_match_long
	mov	[bp.match_start],si
	mov	bx,ax
	cmp	ax,[bp.nice_match]
	jl	longest_match_long
    longest_match_end:
	pop	di
	pop	si
	pop	ds
	mov	ax,bx
	ret
    longest_match_max:
	cmpsb
	jmp	longest_match_mis
longest_match ENDP

ifdef	DEBUG

mcheck	macro start, match, length
	mov	ax,start
	mov	dx,match
	mov	cx,length
	call	check_match
	assert	al,0,je,"invalid match"
	endm
  check_match:
	cld
	push	si
	push	di
	push	ds
	mov	si,ax
	mov	di,dx
	mov	ax,word ptr STDI.ios_bp+2
	mov	ds,ax
	mov	es,ax
    check_match_loop:
	lodsb
	sub	al,es:[di]
	jnz	check_match_failed
	inc	di
	loop	check_match_loop
    check_match_failed:
	pop	ds
	pop	di
	pop	si
	ret
else
mcheck	macro start, match, length
	endm
endif ; DEBUG

fill_window PROC
	push	si
	push	di
    fill_window_do:
	mov	ax,[bp.str_start]
	mov	dx,ax
	add	ax,[bp.lookahead]
	xor	di,di
	sub	di,ax
	cmp	di,-1
	jne	fill_window_else
	dec	di
	jmp	fill_window_endif
    fill_window_else:
	cmp	dx,WSIZE+MAX_DIST
	jb	fill_window_endif
	xor	ax,ax
	cmp	ax,[bp.sliding]
	je	fill_window_endif
	mov	dx,word ptr STDI.ios_bp+2
	push	dx
	push	ax
	push	dx
	push	WSIZE
	push	WSIZE
	call	memcpy
	mov	ax,WSIZE
	sub	[bp.match_start],ax
	sub	[bp.str_start],ax
	sub	word ptr [bp.block_start],ax
	sbb	word ptr [bp.block_start+2],0
	mov	ax,[bp.head]
	mov	cx,HASH_SIZE
	call	fill_loop
	mov	ax,[bp.prev]
	mov	cx,WSIZE
	call	fill_loop
	add	di,WSIZE
    fill_window_endif:
	xor	ax,ax
	cmp	[bp.eofile],ax
	jne	fill_window_end
	assert	di,2,jnb,"more < 2"
	mov	ax,[bp.str_start]
	mov	dx,[bp.lookahead]
	mov	cx,di
	call	read_buf
	jz	fill_window_eof
	add	[bp.lookahead],ax
	cmp	[bp.lookahead],MIN_LOOKAHEAD
	jb	fill_window_do
    fill_window_end:
	pop	di
	pop	si
	ret
    fill_window_eof:
	mov	[bp.eofile],1
	jmp	fill_window_end
      fill_loop:
	mov	es,ax
	xor	bx,bx
      fill_loop_next:
	xor	ax,ax
	mov	dx,es:[bx]
	cmp	dx,WSIZE
	jb	fill_loop_below
	mov	ax,dx
	sub	ax,WSIZE
      fill_loop_below:
	mov	es:[bx],ax
	add	bx,2
	dec	cx
	jnz	fill_loop_next
      fill_loop_break:
	ret
fill_window ENDP

deflate_fast PROC
	push	si
	push	di
	xor	ax,ax
	mov	si,ax
	mov	[bp.d_match_length],ax
	mov	[bp.prev_length],MIN_MATCH-1
    deflate_fast_do:
	xor	ax,ax
	mov	cx,[bp.lookahead]
	cmp	cx,ax
	jne	deflate_fast_while_lookahead
	inc	ax
	call	flush_block
    deflate_fast_end:
	pop	di
	pop	si
	ret
    deflate_fast_while_lookahead:
  ifndef DEFL_UNDETERM
	cmp	cx,MIN_MATCH
	jb	deflate_fast_no_string
  endif
	call	insert_string
    deflate_fast_no_string:
	test	si,si
	jz	deflate_fast_check_match
	mov	ax,[bp.str_start]
	sub	ax,si
	cmp	ax,MAX_DIST
	ja	deflate_fast_check_match
  ifndef HUFFMAN_ONLY
    ifndef DEFL_UNDETERM
	mov	ax,[bp.lookahead]
	cmp	[bp.nice_match],ax
	jna	deflate_fast_get_length
	mov	[bp.nice_match],ax
      deflate_fast_get_length:
    endif
	mov	ax,si
	call	longest_match
	mov	dx,[bp.lookahead]
	cmp	ax,dx
	jna	deflate_fast_set_length
	mov	ax,dx
    deflate_fast_set_length:
	mov	[bp.d_match_length],ax
  endif
    deflate_fast_check_match:
	mov	ax,[bp.d_match_length]
	cmp	ax,MIN_MATCH
	jb	deflate_fast_no_match
	mcheck	[bp.str_start],[bp.match_start],[bp.d_match_length]
	mov	ax,[bp.str_start]
	sub	ax,[bp.match_start]
	mov	dx,[bp.d_match_length]
	sub	dx,MIN_MATCH
	call	ct_tally
	mov	di,ax
	mov	ax,[bp.d_match_length]
	sub	[bp.lookahead],ax
	cmp	ax,[bp.max_insert_len]
	ja	deflate_fast_to_large
  ifndef DEFL_UNDETERM
	cmp	[bp.lookahead],MIN_MATCH
	jb	deflate_fast_to_large
  endif
	dec	[bp.d_match_length]
     deflate_fast_insert_loop:
	inc	[bp.str_start]
	call	insert_string
  ifdef DEFL_UNDETERM
  endif
	dec	[bp.d_match_length]
	jnz	deflate_fast_insert_loop
	inc	[bp.str_start]
	jmp	deflate_fast_flush
    deflate_fast_to_large:
	add	[bp.str_start],ax
	xor	ax,ax
	mov	[bp.d_match_length],ax
	les	bx,STDI
	mov	bx,[bp.str_start]
	mov	al,es:[bx]
	mov	[bp.ins_h],ax
	inc	bx
	call	update_hash
  if not (MIN_MATCH eq 3)
	Call UPDATE_HASH() MIN_MATCH-3 more times
  endif
	jmp	deflate_fast_flush
    deflate_fast_no_match:
	les	bx,STDI
	mov	bx,[bp.str_start]
	xor	ax,ax
	mov	dx,ax
	mov	dl,es:[bx]
	call	ct_tally
	mov	di,ax
	dec	[bp.lookahead]
	inc	[bp.str_start]
    deflate_fast_flush:
	test	di,di
	jz	deflate_fast_noflush
	xor	ax,ax
	call	flush_block
	jz	deflate_fast_end
	mov	ax,[bp.str_start]
	mov	word ptr [bp.block_start],ax
	mov	word ptr [bp.block_start+2],0
    deflate_fast_noflush:
	cmp	[bp.lookahead],MIN_LOOKAHEAD
	jae	deflate_fast_continue
	call	fill_window
	test	STDI.ios_flag,IO_ERROR
	jz	deflate_fast_continue
	xor	ax,ax
	jmp	deflate_fast_end
    deflate_fast_continue:
	jmp	deflate_fast_do
deflate_fast ENDP

deflate	PROC
	cmp	[bp.compr_level],3
	ja	deflate_slow
	jmp	deflate_fast
    deflate_slow:
	push	si
	push	di
	xor	ax,ax
	mov	si,ax
	mov	[bp.d_mavailable],ax
	mov	[bp.d_match_length],MIN_MATCH-1
    deflate_do:
	xor	ax,ax
	mov	cx,[bp.lookahead]
	cmp	cx,ax
	jne	deflate_while_lookahead
	cmp	[bp.d_mavailable],ax
	je	deflate_flush
	les	bx,STDI
	mov	bx,[bp.str_start]
	xor	ax,ax
	mov	dx,ax
	mov	dl,es:[bx-1]
	call	ct_tally
	xor	ax,ax
    deflate_flush:
	inc	ax
	call	flush_block
    deflate_end:
	pop	di
	pop	si
	ret
    deflate_while_lookahead:
  ifndef DEFL_UNDETERM
	cmp	cx,MIN_MATCH
	jb	deflate_no_string
  endif
	call	insert_string
    deflate_no_string:
	mov	ax,[bp.match_start]
	mov	[bp.d_prev_match],ax
	mov	ax,[bp.d_match_length]
	mov	[bp.prev_length],ax
	mov	[bp.d_match_length],MIN_MATCH-1
	test	si,si
	jz	deflate_if
	cmp	ax,[bp.max_lazy_match]
	jae	deflate_if
	mov	ax,[bp.str_start]
	sub	ax,si
	cmp	ax,MAX_DIST
	ja	deflate_if
	mov	ax,[bp.lookahead]
	cmp	[bp.nice_match],ax
	jb	deflate_00
	mov	[bp.nice_match],ax
    deflate_00:
	mov	ax,si
	call	longest_match
	mov	dx,[bp.lookahead]
	cmp	ax,dx
	jb	deflate_01
	mov	ax,dx
    deflate_01:
	mov	[bp.d_match_length],ax
	mov	ax,[bp.d_match_length]
	cmp	ax,MIN_MATCH
	jne	deflate_if
	mov	ax,[bp.str_start]
	sub	ax,[bp.match_start]
	cmp	ax,TOO_FAR
	jbe	deflate_if
	mov	[bp.d_match_length],MIN_MATCH-1
    deflate_if:
	mov	ax,[bp.prev_length]
	cmp	ax,MIN_MATCH
	jb 	deflate_else_if
	cmp	[bp.d_match_length],ax
	ja 	deflate_else_if
	mov	ax,[bp.str_start]
	add	ax,[bp.lookahead]
	sub	ax,MIN_MATCH
	push	ax
  ifdef	DEBUG
	mov	bx,[bp.str_start]
	dec	bx
	mcheck	bx,[bp.d_prev_match],[bp.prev_length]
  endif
	mov	ax,[bp.str_start]
	dec	ax
	sub	ax,[bp.d_prev_match]
	mov	dx,[bp.prev_length]
	sub	dx,MIN_MATCH
	call	ct_tally
	mov	di,ax
	mov	ax,[bp.prev_length]
	dec	ax
	sub	[bp.lookahead],ax
	dec	ax
	mov	[bp.prev_length],ax
	pop	cx
    deflate_do_insert:
	inc	[bp.str_start]
	cmp     [bp.str_start],cx
	ja	deflate_while_insert
	call	insert_string
    deflate_while_insert:
	dec	[bp.prev_length]
	jnz	deflate_do_insert
	inc	[bp.str_start]
	xor	ax,ax
	mov	[bp.d_mavailable],ax
	mov	[bp.d_match_length],MIN_MATCH-1
	test	di,di
	jz	deflate_endif
	call	flush_block
	jz	deflate_end
	mov	ax,[bp.str_start]
	mov	word ptr [bp.block_start],ax
	mov	word ptr [bp.block_start+2],0
	jmp	deflate_endif
    deflate_else_if:
	xor	ax,ax
	cmp	[bp.d_mavailable],ax
	jz	deflate_else
	les	bx,STDI
	mov	bx,[bp.str_start]
	dec	bx
	mov	dx,ax
	mov	dl,es:[bx]
	call	ct_tally
	jz	deflate_noflush
	xor	ax,ax
    deflate_doflush:
	call	flush_block
	jz	deflate_end
	mov	ax,[bp.str_start]
	mov	word ptr [bp.block_start],ax
	mov	word ptr [bp.block_start+2],0
	jmp	deflate_noflush
    deflate_else:
	mov	[bp.d_mavailable],1
    deflate_noflush:
	inc	[bp.str_start]
	dec	[bp.lookahead]
    deflate_endif:
	cmp	[bp.lookahead],MIN_LOOKAHEAD
	jae	deflate_continue
	call	fill_window
	test	STDI.ios_flag,IO_ERROR
	jz	deflate_continue
	xor	ax,ax
	jmp	deflate_end
    deflate_continue:
	jmp	deflate_do
deflate	ENDP

deflate_alloc PROC
	push	SEGM64K
	call	malloc
	jz	deflate_alloc_exit
	inc	dx
	mov	[bp.prev],dx
	push	HASH_SIZE*2+16
	call	malloc
	jz	deflate_free
	inc	dx
	mov	[bp.head],dx
	push	LIT_BUFSIZE+16
	call	malloc
	jz      deflate_free
	inc	dx
	mov	word ptr [bp.l_buf+2],dx
	push	DIST_BUFSIZE*2+16
	call	malloc
	jz      deflate_free
	inc	dx
	mov	word ptr [bp.d_buf+2],dx
	call	clear_hash
	inc	ax
    deflate_alloc_exit:
	ret
deflate_alloc ENDP

deflate_free PROC
	mov	ax,[bp.head]
	call	deflate_free_block
	mov	ax,[bp.prev]
	call	deflate_free_block
	mov	ax,word ptr [bp.l_buf+2]
	call	deflate_free_block
	mov	ax,word ptr [bp.d_buf+2]
	call	deflate_free_block
    deflate_free_end:
	xor	ax,ax
	ret
    deflate_free_block:
	test	ax,ax
	jz	deflate_free_end
	dec	ax
	push	ax
	push	ax
	call	free
	ret
deflate_free ENDP

_TEXT	ENDS

PPROC	zip_deflate, level:WORD
local	zip_stack:deflate_ss
	push	bp
	push	si
	push	level
	lea	bp,zip_stack
	push	ss
	push	bp
	push	SIZE deflate_ss
	call	memzero
	pop	ax
	mov	[bp.compr_level],ax
	lea	ax,[bp.dyn_ltree]
	mov	l_desc.td_dyn_tree,ax
	lea	ax,[bp.static_ltree]
	mov	l_desc.td_static_tree,ax
	lea	ax,[bp.dyn_dtree]
	mov	d_desc.td_dyn_tree,ax
	lea	ax,[bp.static_dtree]
	mov	d_desc.td_static_tree,ax
	lea	ax,[bp.bl_tree]
	mov	bl_desc.td_dyn_tree,ax
	xor	si,si
	call	deflate_alloc
	jz      zip_deflate_exit
	call	ct_init
	jz	zip_deflate_exit
	call	lm_init
	jz	zip_deflate_exit
	call	deflate
	mov	si,ax
	call	deflate_free
    zip_deflate_exit:
	mov	ax,si
	test	ax,ax
	pop	si
	pop	bp
	ret
PEND	zip_deflate

_DATA	SEGMENT

	public	file_method

file_method	DB ?

config_table label word
	;
	;      good lazy nice chain
	DW	 0,   0,   0,    0 ; 0 - store only
	DW	 4,   4,   8,    4 ; 1 - maximum speed, no lazy matches
	DW	 4,   5,  16,    8 ; 2
	DW	 4,   6,  32,   32 ; 3
	DW	 4,   4,  16,   16 ; 4 - lazy matches
	DW	 8,  16,  32,   32 ; 5
	DW	 8,  16, 128,  128 ; 6
	DW	 8,  32, 128,  256 ; 7
	DW	32, 128, 258, 1024 ; 8
	DW	32, 258, 258, 4096 ; 9 - maximum compression

bl_count label word
	DW 	MAX_BITS+1 dup(?)
bl_order label byte
	DB	16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15
extra_lbits label word
	DW	0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0
extra_dbits label word
	DW	0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13
extra_blbits label word
	DW	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7

l_desc label word
	DW	?	; dyn_ltree
	DW	?	; static_ltree
	DW	offset extra_lbits
	DW	LITERALS+1
	DW	L_CODES
	DW	MAX_BITS
	DW	0

d_desc label word
	DW	?	; dyn_dtree
	DW	?	; static_dtree
	DW	offset extra_dbits
	DW	0
	DW	D_CODES
	DW	MAX_BITS
	DW	0

bl_desc	label word
	DW	?	; bl_tree
	DW	0
	DW	offset extra_blbits
	DW	0
	DW	BL_CODES
	DW	MAX_BL_BITS
	DW	0

_DATA	ENDS

	END
