;
; NuCrunch 0.1
; Christopher Jam
; February 2016
;

; next byte is fetched from mem[ mem[zps+1]*256+(mem[zps]-1)%256 ]
; ie, init pointer to start byte then increment low byte of pointer.
; Low byte is decremented before each fetch, then if it hits zero the high byte is predecremented for the next fetch
; note that getByte requires y to be zero
; also note that this mangled pointer is a tad inimical to the literal fetches -/

#define getByte0 \
	dec zps+0   :\
	bne *+7     :\
	jsr nextPage:\
	bvc *+4     :\
	lda (zps),y :\

#define _getByte0 \
	bit zps+0   :\
	bne *+4     :\
	dec zps+1   :\
	dec zps+0   :\
	lda (zps),y :\

#if 0
#define getByte1 jsr get_byte
#else
#define getByte1 getByte0
#endif


#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         :\
	.)

#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 <-$79-*,0  ; place decode_copy on a page boundary
#endif

decrunch_zpa=$e0   ;7 bytes required
decrunch
	.(
zps     = decrunch_zpa+$00
zbs1    = decrunch_zpa+$02
zbs2    = decrunch_zpa+$03
zbs4    = decrunch_zpa+$04
offsetm1= decrunch_zpa+$05

	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 copy_dst+1
	jsr get_byte
	sta copy_dst+2

decode_literal

getEG0p1   ;get count [eg0+1] in x
	ldx#1
	getBit1
	lda#1
	bcc ret1
	lda#1
	getExpGoulombTail
	tax
ret1
	txa
	sec
	eor#255
	adc copy_dst+1
	sta copy_dst+1
	sta literal_dst+1
	bcs *+5
	dec copy_dst+2
	lda copy_dst+2
	sta literal_dst+2



literal_loop
literal_src
	getByte1
literal_dst
	sta $f000,x
	dex
	bne literal_loop


	; 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

	txa
	eor#255
	sec
	adc copy_dst+1
	sta copy_dst+1
	bcs *+6
	dec copy_dst+2
	sec

	; note carry is set at this point; good as we want to add (offsetm1+1)
	lda copy_dst+1
	adc offsetm1
	sta copy_src+1

	lda copy_dst+2
	adc offsetm1+1
	sta copy_src+2

copy_loop
copy_src
	lda $f000,x
copy_dst
	sta $f000,x
	dex
	bne copy_loop


	ldy#0
	getBit1
	bcs jmp_decode_copy
	jmp decode_literal
jmp_decode_copy
	jmp decode_copy

get_byte
	getByte0
end_of_file
	rts
end_of_segment
	lda offsetm1
	cmp#0
	beq end_of_file
	jmp next_segment
nextPage
	lda (zps),y
	dec zps+1
	clv
	rts

.)
decrunch_end
