# LZSS decoder for SEGA Megadrive (Genesis)
# /Mic, 2009

.IFNDEF LZSS_DICTIONARY_SIZE
.EQU LZSS_DICTIONARY_SIZE, 4096
.ENDIF
.IFNDEF LZSS_THRESHOLD
.EQU LZSS_THRESHOLD, 2
.ENDIF
.IFNDEF LZSS_LEN_BITS
.EQU LZSS_LEN_BITS, 4
.ENDIF
.IFNDEF LZSS_LEN_MASK
.EQU LZSS_LEN_MASK, 0x0f
.ENDIF
.IFNDEF LZSS_MAX_LEN
.EQU LZSS_MAX_LEN, 18
.ENDIF

.IFNDEF LZSS_RAM_BASE
.EQU LZSS_RAM_BASE, 0xffff8000
.ENDIF

.EQU LZSS_TEXTBUF, LZSS_RAM_BASE

.EQU LZSS_CNT, (LZSS_RAM_BASE+LZSS_DICTIONARY_SIZE)
.EQU LZSS_TEXTBUF_P, (LZSS_DICTIONARY_SIZE-LZSS_MAX_LEN)


.TEXT
.ALIGN 1
.GLOBAL lzss_decode_vram


# Function lzss_decode_vram
#
# Decompress LZSS packed data to VRAM
#
# In:
#
#  D0 = Size of compressed data
#  A2 = Pointer to compressed data
#  A3 = VRAM base address
#
# Out:
#  -
#
# Example:
#
#  move.l #(packed_patterns_end-packed_patterns),d0
#  move.l #packed_patterns,a2
#  move.l #0x2000,a3
#  jsr lzss_decode_vram
#
lzss_decode_vram:
.IF 1
	.IFNDEF LZSS_DECODE_RAM
	# Set the VRAM address
	move.l 	a3,d6
	move.l 	a3,d7
	and.w 	#0x3fff,d6
	lsr.w 	#7,d7
	lsr.w 	#7,d7
	add.w 	#0x4000,d6
	lsl.l 	#7,d6
	lsl.l 	#7,d6
	lsl.l 	#2,d6
	or.l 	d7,d6
	move.l 	d6,0xC00004
	
	move.l 	#0xC00000,a1
	moveq.l	#0,d7
	moveq.l	#0,d4
	.ENDIF	
	
	# Set all entries in textbuf to 0
	move.l	#LZSS_TEXTBUF,a3
	move.w	#LZSS_TEXTBUF_P-1,d1
_ldv_init_textbuf:
	move.b	#0,(a3)+
	dbra	d1,_ldv_init_textbuf

_ldv_loop:
	or.l	d0,d0
	beq.s	_ldv_done
	move.b	(a2)+,d2		/* d2 = flags */
	subq.l	#1,d0
	moveq.l	#7,d3			/* d3 = flag count */
_ldv_check_flags:
	lsr.b	#1,d2
	bcc.s	_ldv_flag_clear
	or.l	d0,d0
	beq.s	_ldv_done
	move.b	(a2)+,d6
	subq.l	#1,d0
	moveq.l	#0,d5
	bra.s	_ldv_output_byte
	
_ldv_flag_clear:
	or.l	d0,d0
	beq.s	_ldv_done
	move.b	(a2)+,d1		/* d1 = idx */
	move.b	(a2)+,d5		/* d5 = j */
	subq.l	#2,d0
	and.w	#0xFF,d5
	move.l	d5,d6
	lsr.b	#LZSS_LEN_BITS,d6
	lsl.w	#8,d6
	or.b	d1,d6			/* d6 = ((j & 0xF0) << 4) | idx */
	and.b	#LZSS_LEN_MASK,d5
	add.w	#LZSS_THRESHOLD,d5
	or.l	#LZSS_RAM_BASE,d6
	move.l	d6,a4
_ldv_copy_string:
	move.b	(a4)+,d6
	move.l	a4,d1
	and.w	#((LZSS_RAM_BASE & 0xFFFF)+LZSS_DICTIONARY_SIZE-1),d1
	move.l	d1,a4
_ldv_output_byte:
	move.b	d6,(a3)+		/* Write to TEXTBUF */
	.IFDEF LZSS_DECODE_RAM

	move.b	d6,(a1)+
	.ELSE
	btst.b	#0,d7
	beq.s	_ldv_no_vram_write
	lsl.w	#8,d4
	or.b	d6,d4
	move.w	d4,(a1)			/* Write to VRAM */
	bra.s	_ldv_wrap_adr
_ldv_no_vram_write:
	move.l	d6,d4
	.ENDIF
_ldv_wrap_adr:

	eor.b	#1,d7

	# Increase write address and wrap
	move.l	a3,d1
	and.w	#((LZSS_RAM_BASE & 0xFFFF)+LZSS_DICTIONARY_SIZE-1),d1
	move.l	d1,a3
_ldv_next_string_pos:
	dbra	d5,_ldv_copy_string
_ldv_next_flag:
	dbra	d3,_ldv_check_flags
	bra.s	_ldv_loop
_ldv_done:
	rts
.ENDIF

