; =============================================================================
; CBS Cruncher v4.3 - CB5 and original-block RAM decruncher v0.1
; =============================================================================
; This source preserves the compact historical CBS Z80 decoding strategy and
; adds a small wrapper for the exact v4.3 .cb5 files produced by the website
; and Python command-line package.
;
; Public entries:
;   CBS_V43_CB5_TO_RAM   complete website/CLI .cb5 file -> RAM
;   CBS_V43_RAW_TO_RAM   one original seven-byte-header v4.3 block -> RAM
;
; Input for both entries:
;   HL = packed source in RAM
;   DE = destination in RAM
;
; The CB5 wrapper accepts one v4.3 block, or the two-block representation used
; for inputs above FF00h bytes. It trusts the compressor output: the CB5 magic,
; version, codec and block boundaries are not validated at run time.
;
; The raw decoder temporarily uses SP as its packed-data pointer. It disables
; interrupts, restores the caller stack before RET, and leaves interrupts
; disabled. The code patches its saved-SP and distance-scale operands, so the
; complete decoder must execute from writable RAM and is not re-entrant.
;
; The original compact technique came from the ZX Spectrum decruncher and is
; also suitable for Z80-based MSX software. Labels and comments were expanded,
; while the proven token, length and distance paths remain unchanged.
; =============================================================================

; -----------------------------------------------------------------------------
; CBS_V43_CB5_TO_RAM
;
; HL = complete v4.3 .cb5 file (43h,42h,35h,1Ah, version 1, codec 4)
; DE = destination in RAM
;
; The original length at CB5 bytes 8-11 determines whether one or two v4.3
; blocks follow the 24-byte container header. Every payload block begins with
; its four-byte packed length, followed by one original v4.3 stream.
; -----------------------------------------------------------------------------
CBS_V43_CB5_TO_RAM:
	LD	BC,0008h			; original length, little endian
	ADD	HL,BC
	LD	C,(HL)			; length bits 7-0
	INC	HL
	LD	B,(HL)			; length bits 15-8
	INC	HL
	LD	A,(HL)			; length bits 23-16
	INC	HL
	OR	(HL)			; length bits 31-24
	JR	NZ,CBS_V43_CB5_TWO_BLOCKS
	LD	A,B
	CP	FFh
	JR	C,CBS_V43_CB5_ONE_BLOCK
	LD	A,C
	OR	A
	JR	Z,CBS_V43_CB5_ONE_BLOCK			; exactly FF00h still fits one block

CBS_V43_CB5_TWO_BLOCKS:
	LD	A,02h
	JR	CBS_V43_CB5_BLOCK_COUNT_READY

CBS_V43_CB5_ONE_BLOCK:
	LD	A,01h

CBS_V43_CB5_BLOCK_COUNT_READY:
	LD	BC,000Dh			; byte 11 -> 24-byte payload start
	ADD	HL,BC

CBS_V43_CB5_BLOCK_LOOP:
	LD	C,(HL)			; packed block length bits 7-0
	INC	HL
	LD	B,(HL)			; packed block length bits 15-8
	INC	HL
	INC	HL			; packed lengths fit 16 bits here
	INC	HL			; HL = original v4.3 block
	PUSH	AF			; preserve number of blocks
	PUSH	HL
	ADD	HL,BC			; address of the next size field
	EX	(SP),HL			; save next field, restore block start
	CALL	CBS_V43_RAW_TO_RAM
	POP	HL			; next size field
	POP	AF
	DEC	A
	JR	NZ,CBS_V43_CB5_BLOCK_LOOP
	RET

; -----------------------------------------------------------------------------
; CBS_V43_RAW_TO_RAM
;
; HL = one original v4.3 stream with its seven-byte header
; DE = destination in RAM
;
; Raw header:
;   bytes 0-3  final four uncompressed bytes
;   bytes 4-5  independently incremented token-counter bytes
;   byte 6     far-distance scale: 40h, 20h, 10h, 08h or 04h
;   byte 7...  interleaved 16-bit control words and token data
; -----------------------------------------------------------------------------
CBS_V43_RAW_TO_RAM:
	DI			; SP becomes the packed-stream pointer
	PUSH	IY
	LD	IX,CBS_V43_MATCH_TOKEN
	LD	IY,CBS_V43_COPY_MATCH
	LD	(CBS_V43_RAW_STACK_RESTORE),SP
	LD	SP,HL
	POP	HL
	LD	(CBS_V43_FINAL_BYTES_0),HL
	POP	HL
	LD	(CBS_V43_FINAL_BYTES_2),HL
	EXX
	POP	DE			; encoded token counter
	DEC	SP
	POP	AF			; A = distance scale
	LD	(CBS_V43_DISTANCE_SCALE),A
	LD	HL,8000h			; sentinel control-bit reservoir

CBS_V43_TOKEN_LOOP:
	DEC	E			; independently incremented low counter
	JP	NZ,CBS_V43_TOKEN_READY
	DEC	D			; borrow into independently incremented high byte
	JR	Z,CBS_V43_END

CBS_V43_TOKEN_READY:
	OR	A
	LD	BC,0001h
	ADC	HL,HL
	CALL	Z,CBS_V43_REFILL_CONTROL
	JR	C,CBS_V43_LITERAL_TOKEN
	ADC	HL,HL
	CALL	Z,CBS_V43_REFILL_CONTROL
	JR	C,CBS_V43_LENGTH_4_TO_7
	ADC	HL,HL
	CALL	Z,CBS_V43_REFILL_CONTROL
	RL	C			; length 2 or 3
	JP	(IX)

CBS_V43_END:
	EXX
	LD	HL,CBS_V43_FINAL_BYTES_0
	LD	BC,0004h
	LDIR			; append the four saved tail bytes
	LD	SP,0000h
CBS_V43_RAW_STACK_RESTORE:	EQU	$-2
	POP	IY
	RET

CBS_V43_FINAL_BYTES_0:		DW	0000h
CBS_V43_FINAL_BYTES_2:		DW	0000h

; A 0-1 prefix reaches lengths 4-7, 8-23 or the long-length marker.
CBS_V43_LENGTH_4_TO_7:
	CALL	CBS_V43_READ_TWO_BITS
	CP	01h
	JR	Z,CBS_V43_LONG_LENGTH
	JR	C,CBS_V43_LENGTH_8_TO_23
	ADC	HL,HL
	CALL	Z,CBS_V43_REFILL_CONTROL
	RLA
	LD	C,A
	JP	(IX)

CBS_V43_LENGTH_8_TO_23:
	ADD	A,10h
	CALL	CBS_V43_READ_BITS
	ADD	A,08h
	LD	C,A
	JP	(IX)

CBS_V43_LITERAL_RUN_TEST:
	CP	7Fh
	JR	Z,CBS_V43_COPY_LITERAL_RUN

CBS_V43_LITERAL_TOKEN:
	PUSH	BC			; one literal byte

CBS_V43_COPY_LITERAL_RUN:
	EXX
	POP	BC
	LD	HL,0000h
	ADD	HL,SP			; source is the current packed-data SP
	LDIR
	LD	SP,HL
	EXX
	JP	CBS_V43_TOKEN_LOOP

CBS_V43_LONG_LENGTH:
	DEC	SP
	POP	AF			; marker byte
	SRL	A			; Carry selects match or literal run
	LD	C,A
	JR	NC,CBS_V43_LITERAL_RUN_TEST
	CP	7Fh
	JR	NZ,CBS_V43_MATCH_TOKEN
	POP	BC			; 16-bit match length

; Decode the distance for a match whose length is already in BC.
CBS_V43_MATCH_TOKEN:
	CALL	CBS_V43_READ_TWO_BITS
	CP	02h
	JR	Z,CBS_V43_DISTANCE_1_TO_32
	JR	NC,CBS_V43_DISTANCE_FAR
	EXX
	POP	HL			; medium-distance low byte
	DEC	SP
	LD	H,A			; one packed high bit
	LD	BC,0021h
	ADD	HL,BC			; distances 33-544
	JP	(IY)

CBS_V43_DISTANCE_1_TO_32:
	LD	A,08h
	CALL	CBS_V43_READ_BITS
	EXX
	INC	A
	LD	L,A
	LD	H,00h
	JP	(IY)

CBS_V43_DISTANCE_FAR:
	LD	A,04h
CBS_V43_DISTANCE_SCALE:		EQU	$-1
	CALL	CBS_V43_READ_BITS
	EXX
	POP	HL			; far-distance low byte
	DEC	SP
	ADD	A,02h
	CP	40h
	JR	NZ,CBS_V43_FAR_DISTANCE_READY
	DEC	SP
	POP	AF			; explicit 16-bit distance

CBS_V43_FAR_DISTANCE_READY:
	LD	H,A

; HL = positive distance, BC in the other register set = match length.
CBS_V43_COPY_MATCH:
	LD	A,E
	SUB	L
	LD	L,A
	LD	A,D
	SBC	A,H
	LD	H,A			; HL = output - distance
	EXX
	PUSH	BC
	EXX
	POP	BC
	LDIR			; overlapping expansion is intentional
	EXX
	JP	CBS_V43_TOKEN_LOOP

; Refill the 16-bit control reservoir from the stream held in SP.
CBS_V43_REFILL_CONTROL:
	POP	HL			; return address
	EX	(SP),HL			; restore return, obtain next control word
	SCF
	ADC	HL,HL			; insert sentinel and return first bit in Carry
	RET

CBS_V43_READ_TWO_BITS:
	LD	A,40h

; Read bits into A until its sentinel reaches Carry.
CBS_V43_READ_BITS:
	OR	A
	ADC	HL,HL
	JR	NZ,CBS_V43_READ_BITS_HAVE_CONTROL
	POP	HL
	EX	(SP),HL
	SCF
	ADC	HL,HL

CBS_V43_READ_BITS_HAVE_CONTROL:
	RLA
	JP	NC,CBS_V43_READ_BITS
	RET
