;; 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 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<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