;
; NuCrunch 0.1
; Christopher Jam
; February 2016
;

#define getByte1 \
	lda (zps),y :\
	inc zps+0   :\
	bne *+4     :\
	inc zps+1

#define getBit1 \
	.(         :\
	asl zbs1   :\
	bne nomore :\
	getByte1   :\
	sec        :\
	rol        :\
	sta zbs1   :\
nomore         :\
	.)

; get head of a pair of bits from the bitpair stream
; (must getBit2t precisely once before invoking again)

#define getBit2h \
	.(         :\
	asl zbs2   :\
	bne nomore :\
	getByte1   :\
	sec        :\
	rol        :\
	sta zbs2   :\
nomore         :\
	.)

; same, but preserving A/ trashing X.
#define getBit2hpa \
	.(         :\
	asl zbs2   :\
	bne nomore :\
	tax        :\
	getByte1   :\
	sec        :\
	rol        :\
	sta zbs2   :\
	txa        :\
nomore         :\
	.)

; get tail of a pair of bits from the bitpair stream
#define getBit2t \
	asl zbs2

; get head of a quad of bits from the bitpair stream
; (must getBit4t precisely three times before invoking again)

#define getBit4h \
	.(         :\
	asl zbs4   :\
	bne nomore :\
	getByte1   :\
	sec        :\
	rol        :\
	sta zbs4   :\
nomore         :\
	.)


; get tail of a quad of bits from the bitpair stream
#define getBit4t \
	asl zbs4

; note, trashes X.  Also, carry is clear when done
#define getExpGoulombTail \
	.(         :\
ndone          :\
	getBit2hpa :\
	rol        :\
	getBit2t   :\
	bcs ndone  :\
	.)

#define getExpGoulombTail_odd_aligned \
	.(         :\
ndone          :\
	getBit2t   :\
	rol        :\
	getBit2hpa :\
	bcs ndone  :\
	.)

#ifdef NUCRUNCH_ALIGN_FOR_SPEED
	.dsb <-$64-*,0  ; place decode_copy on a page boundary
#endif

decrunch_zpa=$e0   ;9 bytes required
decrunch
	.(
zps     = decrunch_zpa+$00
zpd     = decrunch_zpa+$02
zbs1    = decrunch_zpa+$04
zbs2    = decrunch_zpa+$05
zbs4    = decrunch_zpa+$06
offsetm1= decrunch_zpa+$07

	stx zps+0
	sta zps+1
	ldy #0
	sty zbs1
	sty zbs2
	sty zbs4


+decrunch_next_group
	ldy #0
next_segment
	jsr get_byte
	sta zpd+0
	jsr get_byte
	sta zpd+1

decode_literal

getEG0p1   ;get count [eg0+1] in x
	ldx#1
	getBit1
	bcc ret1
	lda#1
	getExpGoulombTail
	tax
ret1

literal_loop
	lda (zps),y
	sta (zpd),y
	iny
	dex
	bne literal_loop

	clc
	tya
	adc zps
	sta zps
	bcc *+5
	inc zps+1
	clc
	tya
	adc zpd
	sta zpd
	bcc *+4
	inc zpd+1
	ldy#0

	; literal is always followed by copy

decode_copy
	getBit2h
	bcc short_offset
	lda#1
	getExpGoulombTail_odd_aligned
	adc#255
	sta offsetm1+1
	getByte1
	sta offsetm1
	jmp got_high

short_offset
	lda#0
	sta offsetm1+1

	;ExpGoulomb k=3
	getBit4h
	lda#1
	bcc no_tail
	getExpGoulombTail_odd_aligned
no_tail
	adc#255

	sta offsetm1
	getBit4t
	rol offsetm1
	getBit4t
	rol offsetm1
	getBit4t
	rol offsetm1
got_high

	ldx#2
	getBit2t
	bcc length_two
	lda#1
	getExpGoulombTail
	tax
	inx
	beq end_of_segment  ; copy length of 256 marks end of segment
length_two

	; note carry is clear at this point; good as we want to subtract (offsetm1+1)
	lda zpd
	sta copy_dst+1
	sbc offsetm1
	sta copy_src+1

	lda zpd+1
	sta copy_dst+2
	sbc offsetm1+1
	sta copy_src+2

copy_loop
copy_src
	lda $f000,y
copy_dst
	sta $f000,y  ; replace with (zpd),y?? benchmark this!  Also in conjunction with 2x Duff's device
	iny
	dex
	bne copy_loop
	clc
	tya

	adc zpd
	sta zpd
	bcc *+4
	inc zpd+1

	ldy#0
	getBit1
	bcs jmp_decode_copy
	jmp decode_literal
jmp_decode_copy
	jmp decode_copy

get_byte
	getByte1
end_of_file
	rts
end_of_segment
	lda offsetm1
	cmp#0
	beq end_of_file
	jmp next_segment

.)
decrunch_end
