; =============================================================================
; CBS Cruncher v5.2 format 3 - Screen 5-8 RAM reference decruncher v0.2
; =============================================================================
; One decoder handles Screen 5, 6, 7 and 8 because the packed stream stores
; its actual byte width. It also accepts cropped images and imported BMP/PNG
; data whose DX is not the normal full-screen width.
;
; Input:
;   HL = three-byte CBS format-3 image header
;        byte 0 = format and destination
;        byte 1 = packed bytes per line; 0 means 256
;        byte 2 = number of lines; 0 means 256
;   DE = destination in RAM
;
; New-offset selector:
;   0 = complemented gamma line count; distance = count * bytesPerLine
;   1 = ordinary CBS compact 128-byte distance
;
; Matches may cross a line boundary. The line representation only makes
; distances equal to complete rows cheaper; it does not stop at row ends.
;
; The row width is copied once into the immediate operand at
; CBS_V52_SCREEN_WIDTH_LOAD. The routine must therefore execute from RAM.
; The 16-step multiply runs only when a new whole-line offset is introduced.
;
; This reference version expands into linear RAM and ignores byte 2 after
; skipping it. The RAM-stage/HMMC companion uses this same decoding path, then
; points HL to the completed buffer and performs one separate VDP transfer.
;
; The entry trusts compressor output and performs no format or bounds checks.
; License: zlib
; =============================================================================

CBS_V52_SCREEN58_RAM_TO_RAM:
	INC	HL			; skip format / destination byte
	LD	C,(HL)			; packed bytes per line; 0 = 256
	INC	HL
	LD	B,00h
	LD	A,C
	OR	A
	JR	NZ,CBS_V52_SCREEN_WIDTH_READY
	INC	B			; BC = 256
CBS_V52_SCREEN_WIDTH_READY:
	INC	HL			; skip line count; HL = payload
	LD	(CBS_V52_SCREEN_WIDTH_LOAD+1),BC

	LD	A,80h			; sentinel control-bit reservoir
	LD	BC,FFFFh			; default negative offset = -1
	PUSH	BC			; persistent last offset on stack

; The first token is always a literal run.
	CALL	CBS_V52_SCREEN_GET_GAMMA
	LDIR

; After a literal:
;   0 + gamma(1) + 0 = one-byte match using the previous offset
;   0 + gamma(1) + 1 = end marker
;   0 + gamma(2...) = match using the previous offset
;   1 = match introducing a new offset
CBS_V52_SCREEN_AFTER_LITERAL:
	ADD	A,A
	JR	NC,CBS_V52_SCREEN_REPEAT_OFFSET
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	C,CBS_V52_SCREEN_NEW_OFFSET

CBS_V52_SCREEN_REPEAT_OFFSET:
	CALL	CBS_V52_SCREEN_GET_GAMMA
	JR	C,CBS_V52_SCREEN_COPY_MATCH

	ADD	A,A			; gamma(1): match or EOS?
	JR	NC,CBS_V52_SCREEN_COPY_MATCH
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	C,CBS_V52_SCREEN_FINISHED

; Copy BC bytes from DE+negativeOffset to DE. Overlap is intentional.
CBS_V52_SCREEN_COPY_MATCH:
	EX	(SP),HL			; HL=negative offset, stack=packed HL
	PUSH	HL
	ADD	HL,DE			; match source
	LDIR
	POP	HL
	EX	(SP),HL			; restore packed HL and saved offset

; After a match:
;   0 + gamma(2...) = literal run
;   0 + gamma(1) + 0 = one literal byte
;   0 + gamma(1) + 1 = end marker
;   1 = match introducing a new offset
CBS_V52_SCREEN_AFTER_MATCH:
	ADD	A,A
	JR	NC,CBS_V52_SCREEN_LITERAL_AFTER_MATCH
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	C,CBS_V52_SCREEN_NEW_OFFSET

CBS_V52_SCREEN_LITERAL_AFTER_MATCH:
	CALL	CBS_V52_SCREEN_GET_GAMMA
	JR	C,CBS_V52_SCREEN_COPY_LITERALS

	ADD	A,A			; gamma(1): literal or EOS?
	JR	NC,CBS_V52_SCREEN_COPY_LITERALS
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	C,CBS_V52_SCREEN_FINISHED

CBS_V52_SCREEN_COPY_LITERALS:
	LDIR
	JR	CBS_V52_SCREEN_AFTER_LITERAL

; -----------------------------------------------------------------------------
; New offset: selector 0 = whole-line distance, selector 1 = compact general.
; -----------------------------------------------------------------------------
CBS_V52_SCREEN_NEW_OFFSET:
	ADD	A,A
	JR	NC,CBS_V52_SCREEN_ROW_OFFSET
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	C,CBS_V52_SCREEN_GENERAL_OFFSET

CBS_V52_SCREEN_ROW_OFFSET:
	CALL	CBS_V52_SCREEN_GET_NEG_GAMMA
	INC	BC			; BC = negative row count

; Preserve the bit reservoir, output pointer and packed pointer while
; calculating negativeRows * bytesPerLine modulo 65536.
	PUSH	AF
	PUSH	DE
	PUSH	HL

CBS_V52_SCREEN_WIDTH_LOAD:
	LD	DE,0000h			; patched from header byte 1
	LD	HL,0000h			; 16-bit product
	LD	A,16

CBS_V52_SCREEN_MULTIPLY:
	ADD	HL,HL
	SLA	C
	RL	B			; Carry = next multiplier bit
	JR	NC,CBS_V52_SCREEN_MULTIPLY_SKIP
	ADD	HL,DE

CBS_V52_SCREEN_MULTIPLY_SKIP:
	DEC	A
	JR	NZ,CBS_V52_SCREEN_MULTIPLY

	LD	B,H
	LD	C,L			; BC = negative byte distance
	POP	HL			; packed pointer
	POP	DE			; output pointer
	POP	AF			; control-bit reservoir

	EX	(SP),HL			; discard old persistent offset
	POP	HL
	PUSH	BC			; install row-scaled negative offset

	CALL	CBS_V52_SCREEN_GET_GAMMA
	INC	BC			; match length = gamma + 1
	JR	CBS_V52_SCREEN_COPY_MATCH

CBS_V52_SCREEN_GENERAL_OFFSET:
	CALL	CBS_V52_SCREEN_GET_NEG_GAMMA
	INC	BC			; BC = negative high offset part

	RR	B			; Carry = bit 8 of negative high
	LD	B,C
	LD	C,(HL)			; complemented low7 + length control
	INC	HL
	RR	B
	RR	C			; BC = negative distance

	EX	(SP),HL			; discard old persistent offset
	POP	HL
	PUSH	BC			; install new negative offset

; Packed-low bit 0 already supplies the first length control bit.
	CALL	CBS_V52_SCREEN_GET_GAMMA_FIRST
	INC	BC
	JR	CBS_V52_SCREEN_COPY_MATCH

CBS_V52_SCREEN_FINISHED:
	POP	BC			; discard persistent last offset
	RET

; -----------------------------------------------------------------------------
; Interleaved bit refill.
; -----------------------------------------------------------------------------
CBS_V52_SCREEN_REFILL_BITS:
	LD	A,(HL)
	INC	HL
	RLA
	RET

; -----------------------------------------------------------------------------
; Interlaced Elias gamma readers.
; -----------------------------------------------------------------------------
CBS_V52_SCREEN_GET_GAMMA:
	LD	BC,0001h
	JR	CBS_V52_SCREEN_GAMMA_READ_CONTROL

CBS_V52_SCREEN_GET_NEG_GAMMA:
	LD	BC,FFFEh

CBS_V52_SCREEN_GAMMA_READ_CONTROL:
	ADD	A,A
	JR	NC,CBS_V52_SCREEN_GAMMA_LOOP
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	NC,CBS_V52_SCREEN_GAMMA_LOOP
	CCF			; gamma(1) returns Carry clear
	RET

CBS_V52_SCREEN_GET_GAMMA_FIRST:
	LD	BC,0001h
	JR	NC,CBS_V52_SCREEN_GAMMA_LOOP
	CCF
	RET

CBS_V52_SCREEN_GAMMA_LOOP:
	ADD	A,A			; data bit
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	RL	C
	RL	B
	ADD	A,A			; next control bit
	JR	NC,CBS_V52_SCREEN_GAMMA_LOOP
	CALL	Z,CBS_V52_SCREEN_REFILL_BITS
	JR	NC,CBS_V52_SCREEN_GAMMA_LOOP
	RET
