; De-compressor for ZX02 files
; ----------------------------
;
; Decompress ZX02 data (6502 optimized format), optimized for speed and size
;  138 bytes code, 58.0 cycles/byte in test file.
;
; Compress with:
;    zx02 input.bin output.zx0
;
; (c) 2022 DMSC
; Code under MIT license, see LICENSE file.


;ZP=$80

;offset          = ZP+0
;ZX0_src         = ZP+2
;ZX0_dst         = ZP+4
;bitr            = ZP+6
;pntr            = ZP+7

            ; Initial values for offset, source, destination and bitr
;zx0_ini_block:
;            .byte $00, $00	; offset
;comp_data:
;		.byte $0, $0	; zx0_src
;out_addr:
;		.byte $0, $0	; zx0_dst
;		.byte $80	; bitr

;--------------------------------------------------
; Decompress ZX0 data (6502 optimized format)

	; destination page in A
full_decomp:
		sta	ZX0_dst+1

		ldy	#$80
		sty	bitr

		ldy	#0		; always on page boundary
		sty	ZX0_dst

		sty	offset
		sty	offset+1

		; Y needs to be 0 here

              ; Get initialization block
;              ldy #7

;copy_init:     lda zx0_ini_block-1, y
 ;             sta offset-1, y
  ;            dey
   ;           bne copy_init

; Decode literal: Ccopy next N bytes from compressed file
;    Elias(length)  byte[1]  byte[2]  ...  byte[N]
decode_literal:
              jsr   get_elias

cop0:         lda   (ZX0_src), y
              inc   ZX0_src
              bne   plus1
              inc   ZX0_src+1
plus1:             sta   (ZX0_dst),y
              inc   ZX0_dst
              bne   plus2
              inc   ZX0_dst+1
plus2:             dex
              bne   cop0

              asl   bitr
              bcs   dzx0s_new_offset

; Copy from last offset (repeat N bytes from last offset)
;    Elias(length)
              jsr   get_elias
dzx0s_copy:
              lda   ZX0_dst
              sbc   offset  ; C=0 from get_elias
              sta   pntr
              lda   ZX0_dst+1
              sbc   offset+1
              sta   pntr+1

cop1:
              lda   (pntr), y
              inc   pntr
              bne   plus3
              inc   pntr+1
plus3:             sta   (ZX0_dst),y
              inc   ZX0_dst
              bne   plus4
              inc   ZX0_dst+1
plus4:             dex
              bne   cop1

              asl   bitr
              bcc   decode_literal

; Copy from new offset (repeat N bytes from new offset)
;    Elias(MSB(offset))  LSB(offset)  Elias(length-1)
dzx0s_new_offset:
              ; Read elias code for high part of offset
              jsr   get_elias
              beq   exit  ; Read a 0, signals the end
              ; Decrease and divide by 2
              dex
              txa
              lsr   ; @
              sta   offset+1

              ; Get low part of offset, a literal 7 bits
              lda   (ZX0_src), y
              inc   ZX0_src
              bne   plus5
              inc   ZX0_src+1
plus5:
              ; Divide by 2
              ror   ; @
              sta   offset

              ; And get the copy length.
              ; Start elias reading with the bit already in carry:
              ldx   #1
              jsr   elias_skip1

              inx
              bcc   dzx0s_copy

; Read an elias-gamma interlaced code.
; ------------------------------------
get_elias:
              ; Initialize return value to #1
              ldx   #1
              bne   elias_start

elias_get:    ; Read next data bit to result
              asl   bitr
              rol   ; @
              tax

elias_start:
              ; Get one bit
              asl   bitr
              bne   elias_skip1

              ; Read new bit from stream
              lda   (ZX0_src), y
              inc   ZX0_src
              bne   plus6
              inc   ZX0_src+1
plus6:             ;sec   ; not needed, C=1 guaranteed from last bit
              rol   ;@
              sta   bitr

elias_skip1:
              txa
              bcs   elias_get
              ; Got ending bit, stop reading
exit:
              rts
