; =============================================================================
; CBS Cruncher v5.5 - Screen 5-8 reusable-buffer decruncher v1.1
; =============================================================================
; Decrunches a C3h v5.5 image or a C4h sequence of independent v5.5 chunks.
; Every EOS uploads the completed RAM chunk with HMMC. C4h then advances Y and
; reuses the same caller-selected buffer until the complete image is restored.
;
; Input:
;   HL = CBS image container
;        C3h: width, height, one EOS-terminated payload
;        C4h: width, total height, chunk count, then repeated
;             chunk height + independent EOS-terminated payload
;   DE = packed destination coordinate
;        D = X in packed bytes
;        E = Y within the selected 256-line command page
;   IX = start of the selected 1/2/4/8/16 KB reusable RAM buffer
;   A  = call route
;        bits 1-0 = source: 0 direct RAM/continuous ROM
;                              1 ROM mapper
;                              2 mapped RAM
;                              3 reserved
;        bits 3-2 = VRAM command page 0-3
;
; This first source implements the direct reader used by source type 0.
; Mapper-specific generated readers can later replace the direct HL reads
; without changing the RAM output core or the final HMMC transfer.
;
; This standalone source is assembled for its execution address in writable
; RAM. If it is embedded inside a larger ROM source, use the equivalent
; PHASE/DEPHASE feature of that assembler around this routine instead.
	ORG	E000h
;
; Public entry table:
;   E000h / CBS_INIT_MSX    call once after copying the decoder to RAM
;   E003h / CBS_INIT_SCREEN A = 5, 6, 7 or 8; call when Screen mode changes
;   E006h / CBS_DECRUNCH    normal image-decompression entry
;
; CBS_INIT_MSX reads the real VDP write base port from BIOS address 0007h and
; patches every immediate VDP output and OTIR port. CBS_INIT_SCREEN patches the
; packed-byte to dot-coordinate shift. Neither initializer is in a token loop.
CBS_INIT_MSX:
	JP	CBS_V52_INIT_MSX
CBS_INIT_SCREEN:
	JP	CBS_V52_INIT_SCREEN
CBS_DECRUNCH:
;
; DX and NX are VDP dot coordinates, so setup multiplies packed X and width by
; 1, 2 or 4 for the selected Screen mode. Page n is stored in the high byte of
; DY. The first buffered byte seeds R44; then R45=0 and R46=F0h start HMMC.
; The remaining bytes are copied by the supplied, openMSX-tested self-modifying
; DJNZ loop exactly as written. There is no per-byte CALL, TR polling or
; command-completion wait.
;
; The routine and its parameter block must execute from writable RAM.
; Interrupts are disabled only for VDP setup and transfer. They remain disabled
; on return, matching the supplied game routine; the caller may EI when safe.
; The entry trusts compressor output and performs no format or bounds checks.
; License: zlib
; =============================================================================

CBS_V52_SCREEN58_RAM_TO_VRAM:
	LD	(CBS_V52_VRAM_ROUTE_LOAD+1),A
	LD	A,E
	LD	(CBS_V52_VRAM_DY),A
	EXX
	LD	E,A			; alternate E = current destination Y
	EXX
	LD	A,D
	LD	(CBS_V52_VRAM_X_LOAD+1),A

	LD	A,(HL)
	CP	C4h
	JR	Z,CBS_V52_VRAM_CHUNKED

	INC	HL			; skip C3h route byte
	LD	A,(HL)			; packed width; 0 = 256
	INC	HL
	LD	(CBS_V52_VRAM_WIDTH_BYTE+1),A
	LD	A,(HL)			; height; 0 = 256
	INC	HL			; HL = first payload byte
	LD	(CBS_V52_VRAM_HEIGHT_BYTE+1),A
	EXX
	LD	B,01h			; one ordinary C3h stream
	EXX
	JR	CBS_V52_VRAM_PAYLOAD

CBS_V52_VRAM_CHUNKED:
	INC	HL			; skip C4h route byte
	LD	A,(HL)			; common packed width
	INC	HL
	LD	(CBS_V52_VRAM_WIDTH_BYTE+1),A
	INC	HL			; skip total image height
	LD	A,(HL)			; independent EOS-terminated chunks
	INC	HL
	EXX
	LD	B,A			; alternate B = chunks remaining
	EXX

CBS_V52_VRAM_CHUNK_START:
	EXX
	LD	A,E
	EXX
	LD	(CBS_V52_VRAM_DY),A
	LD	A,(HL)			; this chunk's line count
	INC	HL			; HL = first payload byte
	LD	(CBS_V52_VRAM_HEIGHT_BYTE+1),A

CBS_V52_VRAM_PAYLOAD:
	PUSH	IX
	POP	DE			; reuse the selected RAM buffer
	LD	A,80h			; sentinel control-bit reservoir
	LD	BC,FFFFh			; default negative offset = -1
	PUSH	BC			; persistent last offset on stack
; -----------------------------------------------------------------------------
; v5.5 line-aware parser.
; -----------------------------------------------------------------------------
CBS_V52_VRAM_ROW_START:
	CALL	CBS_V52_VRAM_GET_GAMMA			; first token is always literals
	JR	CBS_V52_VRAM_COPY_LITERALS

; 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_VRAM_AFTER_LITERAL:
	ADD	A,A
	JR	NC,CBS_V52_VRAM_REPEAT_OFFSET
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	C,CBS_V52_VRAM_NEW_OFFSET

CBS_V52_VRAM_REPEAT_OFFSET:
	CALL	CBS_V52_VRAM_GET_GAMMA
	JR	C,CBS_V52_VRAM_COPY_MATCH

	ADD	A,A
	JR	NC,CBS_V52_VRAM_COPY_MATCH
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	C,CBS_V52_VRAM_FINISHED

; Copy BC bytes from DE+negativeOffset to DE. Overlap is intentional.
CBS_V52_VRAM_COPY_MATCH:
	EX	(SP),HL			; HL=offset, stack=packed pointer
	PUSH	HL
	ADD	HL,DE			; HL = RAM match source
	LDIR
	POP	HL			; negative offset
	EX	(SP),HL			; restore packed pointer and 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_VRAM_AFTER_MATCH:
	ADD	A,A
	JR	NC,CBS_V52_VRAM_LITERAL_AFTER_MATCH
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	C,CBS_V52_VRAM_NEW_OFFSET

CBS_V52_VRAM_LITERAL_AFTER_MATCH:
	CALL	CBS_V52_VRAM_GET_GAMMA
	JR	C,CBS_V52_VRAM_COPY_LITERALS

	ADD	A,A
	JR	NC,CBS_V52_VRAM_COPY_LITERALS
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	C,CBS_V52_VRAM_FINISHED

; Copy BC raw packed bytes to the RAM staging buffer.
CBS_V52_VRAM_COPY_LITERALS:
	LDIR
	JR	CBS_V52_VRAM_AFTER_LITERAL

; -----------------------------------------------------------------------------
; New offset: selector 0 = whole-line distance, selector 1 = compact general.
; -----------------------------------------------------------------------------
CBS_V52_VRAM_NEW_OFFSET:
	ADD	A,A
	JR	NC,CBS_V52_VRAM_ROW_OFFSET
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	C,CBS_V52_VRAM_GENERAL_OFFSET

CBS_V52_VRAM_ROW_OFFSET:
	CALL	CBS_V52_VRAM_GET_NEG_GAMMA
	INC	BC			; BC = negative row count

	PUSH	AF
	PUSH	DE
	PUSH	HL

	LD	A,(CBS_V52_VRAM_WIDTH_BYTE+1)
	LD	E,A
	LD	D,00h
	OR	A
	JR	NZ,CBS_V52_VRAM_MULTIPLY_WIDTH_READY
	INC	D			; DE = 256
CBS_V52_VRAM_MULTIPLY_WIDTH_READY:
	LD	HL,0000h
	LD	A,10h
CBS_V52_VRAM_MULTIPLY:
	ADD	HL,HL
	SLA	C
	RL	B
	JR	NC,CBS_V52_VRAM_MULTIPLY_SKIP
	ADD	HL,DE
CBS_V52_VRAM_MULTIPLY_SKIP:
	DEC	A
	JR	NZ,CBS_V52_VRAM_MULTIPLY

	LD	B,H
	LD	C,L			; BC = negative byte distance
	POP	HL
	POP	DE
	POP	AF

	EX	(SP),HL			; replace persistent offset
	POP	HL
	PUSH	BC

	CALL	CBS_V52_VRAM_GET_GAMMA
	INC	BC
	JR	CBS_V52_VRAM_COPY_MATCH

CBS_V52_VRAM_GENERAL_OFFSET:
	CALL	CBS_V52_VRAM_GET_NEG_GAMMA
	INC	BC

	RR	B
	LD	B,C
	LD	C,(HL)
	INC	HL
	RR	B
	RR	C			; BC = negative distance

	EX	(SP),HL
	POP	HL
	PUSH	BC

	CALL	CBS_V52_VRAM_GET_GAMMA_FIRST
	INC	BC
	JR	CBS_V52_VRAM_COPY_MATCH

CBS_V52_VRAM_FINISHED:
	POP	BC			; discard persistent last offset

; -----------------------------------------------------------------------------
; The CBS stream is now completely expanded in RAM at IX. Build the HMMC
; rectangle, point HL to the buffer and upload it in one tight transfer.
; -----------------------------------------------------------------------------
	PUSH	HL			; preserve next packed chunk pointer

CBS_V52_VRAM_X_LOAD:
	LD	L,00h			; patched packed X from D
	LD	H,00h
CBS_V52_SCREEN_SHIFT_X:
	ADD	HL,HL
	NOP
	LD	(CBS_V52_VRAM_DX),HL

CBS_V52_VRAM_ROUTE_LOAD:
	LD	A,00h			; patched source/page call route
	AND	Ch
	RRCA
	RRCA
	LD	(CBS_V52_VRAM_DY+1),A

CBS_V52_VRAM_WIDTH_BYTE:
	LD	A,00h			; patched packed width; 0 = 256
	LD	L,A
	LD	H,00h
	OR	A
	JR	NZ,CBS_V52_VRAM_WIDTH_NONZERO
	INC	H
CBS_V52_VRAM_WIDTH_NONZERO:
CBS_V52_SCREEN_SHIFT_NX:
	ADD	HL,HL
	NOP
	LD	A,H
	AND	1			; command NX wraps 512 dots to 000h
	LD	H,A
	LD	(CBS_V52_VRAM_NX),HL

CBS_V52_VRAM_HEIGHT_BYTE:
	LD	A,00h			; patched line count; 0 = 256
	LD	L,A
	LD	H,00h
	OR	A
	JR	NZ,CBS_V52_VRAM_HEIGHT_READY
	INC	H
CBS_V52_VRAM_HEIGHT_READY:
	LD	(CBS_V52_VRAM_NY),HL

	PUSH	IX
	POP	HL			; HL = start of completed RAM image
	LD	A,(CBS_V52_VRAM_WIDTH_BYTE+1)
	LD	(CBS_V52_VRAM_COPY_SIZE),A
	LD	A,(CBS_V52_VRAM_HEIGHT_BYTE+1)
	LD	(CBS_V52_VRAM_COPY_SIZE+2),A

	PUSH	HL			; source compressed stream

	DI
	LD	HL,CBS_V52_VRAM_COMMAND_DATA
CBS_V52_PORT_CONTROL:
	LD	C,99h			; patched once from BIOS VDP.DW
	LD	A,24h
	OUT	(C),A
	LD	A,11h + 80h			; indirect access with increment
	OUT	(C),A
	INC	C
	INC	C			; control port + 2 = indirect-command port
	LD	B,08h
	OTIR

	LD	HL,CBS_V52_VRAM_COPY_SIZE
	LD	B,(HL)			; .nx
	INC	HL
	INC	HL
	LD	D,(HL)			; .ny

	LD	A,CBS_V52_VRAM_R45 - CBS_V52_VRAM_DJNZ - 1			; enable VDP command start
	LD	(CBS_V52_VRAM_DJNZ),A

	POP	HL

CBS_V52_VRAM_COPY:

CBS_V52_VRAM_COPY_L:

	LD	A,(HL)
	INC	HL

	OUT	(C),A			; 1st execution color register with increment

	DJNZ	CBS_V52_VRAM_COPY_L
CBS_V52_VRAM_DJNZ		EQU	$ - 1

	DEC	D
	JR	NZ,CBS_V52_VRAM_COPY

CBS_V52_VRAM_EXIT:
	DEC	C
	DEC	C			; indirect-command port - 2 = control port
	LD	A,02h			; select status register 2
	OUT	(C),A
	LD	A,8Fh
	OUT	(C),A
CBS_V52_VRAM_EXIT_WAIT:
	IN	A,(C)
	RRCA				; Carry = CE; HMMC may finish after its final byte
	JR	C,CBS_V52_VRAM_EXIT_WAIT
	XOR	A			; restore status register 0
	OUT	(C),A
	LD	A,8Fh
	OUT	(C),A

	POP	HL			; next chunk header after this EOS
	LD	A,(CBS_V52_VRAM_HEIGHT_BYTE+1)
	EXX
	ADD	A,E
	LD	E,A			; advance destination Y without RAM state
	DEC	B
	JR	Z,CBS_V52_VRAM_EXIT_DONE
	EXX
	EI				; parsing may run with BIOS interrupts enabled
	JP	CBS_V52_VRAM_CHUNK_START
CBS_V52_VRAM_EXIT_DONE:
	EXX
	RET

CBS_V52_VRAM_R45:
	XOR	A			; R45
	OUT	(C),A
	LD	A,F0h			; R46 - HMMC
	OUT	(C),A

	DEC	C
	DEC	C			; select the control port
	LD	A,44 + 80h			; color register without increment
	OUT	(C),A
	LD	A,11h + 80h
	OUT	(C),A
	INC	C
	INC	C			; restore the indirect-command port

	LD	A,100h - (CBS_V52_VRAM_DJNZ - CBS_V52_VRAM_COPY_L + 1)
	LD	(CBS_V52_VRAM_DJNZ),A

	JR	CBS_V52_VRAM_COPY_L

; -----------------------------------------------------------------------------
; Multiply HL by 2^C. Used only during HMMC setup.
; -----------------------------------------------------------------------------


; -----------------------------------------------------------------------------
; Interleaved bit refill and interlaced gamma readers.
; -----------------------------------------------------------------------------
CBS_V52_VRAM_REFILL_BITS:
	LD	A,(HL)
	INC	HL
	RLA
	RET

CBS_V52_VRAM_GET_GAMMA:
	LD	BC,0001h
	JR	CBS_V52_VRAM_GAMMA_READ_CONTROL

CBS_V52_VRAM_GET_NEG_GAMMA:
	LD	BC,FFFEh

CBS_V52_VRAM_GAMMA_READ_CONTROL:
	ADD	A,A
	JR	NC,CBS_V52_VRAM_GAMMA_LOOP
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	NC,CBS_V52_VRAM_GAMMA_LOOP
	CCF
	RET

CBS_V52_VRAM_GET_GAMMA_FIRST:
	LD	BC,0001h
	JR	NC,CBS_V52_VRAM_GAMMA_LOOP
	CCF
	RET

CBS_V52_VRAM_GAMMA_LOOP:
	ADD	A,A
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	RL	C
	RL	B
	ADD	A,A
	JR	NC,CBS_V52_VRAM_GAMMA_LOOP
	CALL	Z,CBS_V52_VRAM_REFILL_BITS
	JR	NC,CBS_V52_VRAM_GAMMA_LOOP
	RET

; -----------------------------------------------------------------------------
; One-time machine initializer. 0007h is VDP.DW, the BIOS-supplied VDP write
; base port. Port 1 is register/control; port 3 is indirect register access.
; -----------------------------------------------------------------------------
CBS_V52_INIT_MSX:
	LD	A,(0007h)
	INC	A
	LD	(CBS_V52_PORT_CONTROL+1),A
	RET

; A = Screen mode 5-8. The trusted caller supplies a valid mode. The table
; stores log2(pixels per packed byte): S5=1, S6=2, S7=1, S8=0.
CBS_V52_INIT_SCREEN:
	SUB	5
	ADD	A,A
	LD	C,A
	LD	B,00h
	LD	HL,CBS_V52_SCREEN_SHIFT_TABLE
	ADD	HL,BC
	LD	A,(HL)
	INC	HL
	LD	H,(HL)
	LD	L,A
	LD	(CBS_V52_SCREEN_SHIFT_X),HL
	LD	(CBS_V52_SCREEN_SHIFT_NX),HL
	RET

CBS_V52_SCREEN_SHIFT_TABLE:
	DW	0029h,2929h,0029h,0000h

; -----------------------------------------------------------------------------
; Writable R36-R43 parameter block.
; -----------------------------------------------------------------------------
CBS_V52_VRAM_COMMAND_DATA:
CBS_V52_VRAM_DX:
	DW	0
CBS_V52_VRAM_DY:
	DW	0
CBS_V52_VRAM_NX:
	DW	0
CBS_V52_VRAM_NY:
	DW	0

; Packed byte width, spacer and line count used by the tested copy loop.
CBS_V52_VRAM_COPY_SIZE:
	DB	0,0,0
