	;; Crunched RAM disk driver by Marko Mäkelä
	;; Based on the stand-alone pucrunch decompressor by Pasi Ojala

	;; This file can be assembled with DASM 2.12.04.

	;; The game "Crush Crumble and Chomp" is linked as follows:
	;; ccc.data:	$1201-$1bff
	;; init:	$1c01-$37ec (slightly modified)
	;; this part:	right after init

	;; There are three entry points in this program.
	;; * The pucrunch entry is for starting the BASIC program.
	;; * "decrunch" loads a map file to $1201-$160f.
	;; - The file number (1..4) is in $f7.
	;; * "loadccc1" loads the actual game file to $1c01 and starts it.

	processor 6502
	org $37ED

	lda #91
	sta 1
	lda #23
	sta 2			; set the USR vector
	lda #1
	sta 649
	lda #$1c
	sta $2c			; set the start of BASIC
	lda #$54
	sta $2e			; relocate the area for variables
	sta $30
	sta $32
	jsr $c659		; clr
	jmp $c7ae		; run

	;; the crunched files
	;; created with
	;; pucrunch -c0 $in | \
	;; perl -e 'undef $/;$_=<>;s/^..pu..(.)..(....)../$1$2/os;print' > $out
block1	incbin "new york city.crunched"
block2	incbin "golden gate.crunched"
block3	incbin "washington dc.crunched"
block4	incbin "tokyo.crunched"

ccc1	incbin "ccc1.crunched"	; BASIC code relocated from $1201 to $1c01

	;; start addresses of crunched data
posl	dc.b <block1, <block2, <block3, <block4
posh	dc.b >block1, >block2, >block3, >block4

LZPOS	equ $9e		; 2 ZeroPage temporaries
table	equ $200	; RLE table
bitstr	equ $f7
esc	equ $f8
prgtype	equ $f9
OUTPOS	equ $fb

decrunch			; decrunch a data file
	ldx #block_stack_end-block_stack_
cpaux$	lda block_stack-1,x
	sta.wx block_stack_-1,x
	dex
	bne cpaux$

	ldy $f7
	lda posl-1,y
	sta INPOS
	lda posh-1,y
	sta INPOS+1

decruncher
	jsr getbyt
	sta esc		; starting escape

	jsr getbyt	; read # of escape bits
	sta escB0+1
	sta escB1+1
	lda #8		; C=1 after getbyt
	sec
	sbc escB1+1
	sta noesc+1	; 8-escBits

	jsr getbyt
	sta mg+1	; maxGamma + 1
	lda #9		; C=1 after getbyt
	sec
	sbc mg+1	; 8 - maxGamma == (8 + 1) - (maxGamma + 1)
	sta longrle+1
	jsr getbyt
	sta mg1+1	; (1<<maxGamma)
	asl
	clc
	sbc #0
	sta mg21+1	; (2<<maxGamma) - 1
	jsr getbyt
	sta elzpb+1	; extraLZPosBits

	jsr getbyt	; rleUsed
	ldx #0
	tay
	beq done$	; Y == 0 ?
cprle$	jsr getbyt
	sta table,x	; initialize the RLE table
	inx
	dey
	bne cprle$
done$	lda #$80
	sta bitstr
	jmp main

loadccc1
	ldx #block_stack_end-block_stack_
cpaux$	lda block_stack-1,x
	sta.wx block_stack_-1,x
	dex
	bne cpaux$
	lda #<ccc1
	sta INPOS
	lda #>ccc1
	sta INPOS+1
	lda #$1c
	sta OUTPOS+1
	jsr decruncher
	jsr $c68e		; stxpt
	jmp $c7ae		; run

getbyt	jsr getnew
	lda bitstr
	ror
	rts

newesc	ldy esc		; remember the old code (top bits for escaped byte)
escB0	ldx #2		; ** PARAMETER	0..8
	jsr getchkf	; get & save the new escape code
	sta esc
	tya		; pre-set the bits
	; Fall through and get the rest of the bits.
noesc	ldx #6		; ** PARAMETER	8..0
	jsr getchkf
	jsr putch	; output the escaped/normal byte
	; Fall through and check the escape bits again
main	ldy #0		; Reset to a defined state
	tya		; A = 0
escB1	ldx #2		; ** PARAMETER	0..8
	jsr getchkf	; X = 0
	cmp esc
	bne noesc
	; Fall through to packed code

	jsr getval	; X = 0
	sta LZPOS	; xstore - save the length for a later time
	lsr		; cmp #1	; LEN == 2 ? (A is never 0)
	bne lz77	; LEN != 2	-> LZ77
	;tya		; A = 0

	jsr getbit	; X = 0
	bcc lz77_2	; A=0 -> LZPOS+1
	; e..e01
	jsr getbit	; X = 0
	bcc newesc	; e..e010
	; e..e011
	iny		; Y is 1 bigger than MSB loops
	jsr getval	; Y is 1, get len, X = 0
	sta LZPOS	; xstore - Save length LSB
mg1	cmp #64		; ** PARAMETER 63-64 -> C clear, 64-64 -> C set..
	bcc chrcode	; short RLE, get bytecode

longrle	ldx #2		; ** PARAMETER	111111xxxxxx
	jsr getbits	; get 3/2/1 more bits to get a full byte, X = 0
	sta LZPOS	; xstore - Save length LSB

	jsr getval	; length MSB, X = 0
	tay		; Y is 1 bigger than MSB loops

chrcode	jsr getval	; Byte Code, X = 0
	tax		; this is executed most of the time anyway
	lda table-1,x	; Saves one jump if done here (loses one txa)

	cpx #32		; 31-32 -> C clear, 32-32 -> C set..
	bcc 1$		; 1..31, we got the right byte from the table

	; Ranks 32..64 (11111°xxxxx), get byte..
	txa		; get back the value (5 valid bits)
	ldx #3
	jsr getbits	; get 3 more bits to get a full byte, X = 0

1$	ldx LZPOS	; xstore - get length LSB
	inx		; adjust for cpx#$ff;bne -> bne
dorle	jsr putch	;+dex
	bne dorle	; xstore 0..255 -> 1..256
	dey
	bne dorle	; Y was 1 bigger than wanted originally
mainbeq	beq main	; reverse condition -> jump always

lz77	jsr getval	; X = 0
mg21	cmp #127	; ** PARAMETER	Clears carry (is maximum value)
	beq gbend

	sbc #0		; C is clear -> subtract 1  (1..126 -> 0..125)
elzpb	ldx #0		; ** PARAMETER (more bits to get)
	jsr getchkf	; clears Carry, X = 0

lz77_2	sta LZPOS+1	; offset MSB
	ldx #8
	jsr getbits	; clears Carry, X = 0
	; Note: Already eor:ed in the compressor..
	;eor #255	; offset LSB 2's complement -1 (i.e. -X = ~X+1)
	adc OUTPOS	; -offset -1 + curpos (C is clear)
	ldx LZPOS	; xstore = LZLEN (read before it's overwritten)

	sta LZPOS
	lda OUTPOS+1
	sbc LZPOS+1	; takes C into account
	sta LZPOS+1	; copy X+1 number of chars from LZPOS to OUTPOS
	;ldy #0		; Y was 0 originally, we don't change it

	inx		; adjust for cpx#$ff;bne -> bne
lzloop	lda (LZPOS),y	; using abs,y is 3 bytes longer, only 1 cycle/byte faster
	iny		; Y does not wrap because X=0..255 and Y initially 0
	jsr putch	;+dex
	bne lzloop	; X loops, (256,1..255)
	beq mainbeq

getbit	asl bitstr
	bne gbend
getnew	pha
INPOS = .+1
	lda $aaaa	; ** PARAMETER
	;; xxxxxxxx 101yyyyy yyyyyyyy
	inc INPOS
	bne 0$
	inc INPOS+1
0$	rol		; Shift out the next bit and
			; shift in C=1 (last bit marker)
	sta bitstr	; bitstr initial value = $80 == empty
	pla
gbend	rts

; getval : Gets a 'static huffman coded' value
; ** Scratches X, returns the value in A **
getval	inx		; X <- 1
	txa		; set the top bit (value is 1..255)
gv0	jsr getbit
	bcc getchk	; got 0-bit
	inx
mg	cpx #7		; ** PARAMETER unary code maximum length + 1
	bne gv0
	beq getchk	; inverse condition -> jump always

; getbits: Gets X bits from the stream
; ** Scratches X, returns the value in A **
getbits	jsr getbit
	rol
getchk	dex
getchkf	bne getbits
	clc
	rts

block_stack
	rorg $fa
block_stack_

putch	sta $1201	; ** parameter OUTPOS
	inc OUTPOS	; ZP
	bne 0$
	inc OUTPOS+1	; ZP
0$	dex
	rts
block_stack_end
	rend
