Add ZX02 and LZSA (1,2) decompressors
This commit is contained in:
308
libsrc/common/lzsa2.s
Normal file
308
libsrc/common/lzsa2.s
Normal file
@@ -0,0 +1,308 @@
|
||||
; void __fastcall__ decompress_lzsa2(const void *src, void *dest)
|
||||
;
|
||||
; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA2 format.
|
||||
;
|
||||
; Compress with:
|
||||
; lzsa -r -f 2 input.bin output.lzsa2
|
||||
;
|
||||
; Copyright John Brandwood 2021.
|
||||
;
|
||||
; Distributed under the Boost Software License, Version 1.0.
|
||||
; Boost Software License - Version 1.0 - August 17th, 2003
|
||||
;
|
||||
; Permission is hereby granted, free of charge, to any person or organization
|
||||
; obtaining a copy of the software and accompanying documentation covered by
|
||||
; this license (the "Software") to use, reproduce, display, distribute,
|
||||
; execute, and transmit the Software, and to prepare derivative works of the
|
||||
; Software, and to permit third-parties to whom the Software is furnished to
|
||||
; do so, all subject to the following:
|
||||
;
|
||||
; The copyright notices in the Software and this entire statement, including
|
||||
; the above license grant, this restriction and the following disclaimer,
|
||||
; must be included in all copies of the Software, in whole or in part, and
|
||||
; all derivative works of the Software, unless such copies or derivative
|
||||
; works are solely in the form of machine-executable object code generated by
|
||||
; a source language processor.
|
||||
;
|
||||
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
; FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
; SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
; FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
; DEALINGS IN THE SOFTWARE.
|
||||
|
||||
.export _decompress_lzsa2
|
||||
|
||||
.import popax
|
||||
.importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
|
||||
|
||||
lzsa_length = lzsa_winptr ; 1 word.
|
||||
|
||||
lzsa_cmdbuf = tmp1 ; 1 byte.
|
||||
lzsa_nibflg = tmp2 ; 1 byte.
|
||||
lzsa_nibble = tmp3 ; 1 byte.
|
||||
lzsa_offset = ptr1 ; 1 word.
|
||||
lzsa_winptr = ptr2 ; 1 word.
|
||||
lzsa_srcptr = ptr3 ; 1 word.
|
||||
lzsa_dstptr = ptr4 ; 1 word.
|
||||
|
||||
.proc _decompress_lzsa2
|
||||
sta lzsa_dstptr
|
||||
stx lzsa_dstptr+1
|
||||
jsr popax
|
||||
sta lzsa_srcptr
|
||||
stx lzsa_srcptr+1
|
||||
|
||||
lzsa2_unpack:
|
||||
ldx #$00 ; Hi-byte of length or offset.
|
||||
ldy #$00 ; Initialize source index.
|
||||
sty lzsa_nibflg ; Initialize nibble buffer.
|
||||
|
||||
;
|
||||
; Copy bytes from compressed source data.
|
||||
;
|
||||
; N.B. X=0 is expected and guaranteed when we get here.
|
||||
;
|
||||
|
||||
cp_length:
|
||||
lda (lzsa_srcptr),y
|
||||
inc lzsa_srcptr
|
||||
bne cp_skip0
|
||||
inc lzsa_srcptr+1
|
||||
|
||||
cp_skip0:
|
||||
sta lzsa_cmdbuf ; Preserve this for later.
|
||||
and #$18 ; Extract literal length.
|
||||
beq lz_offset ; Skip directly to match?
|
||||
|
||||
lsr ; Get 2-bit literal length.
|
||||
lsr
|
||||
lsr
|
||||
cmp #$03 ; Extended length?
|
||||
bcc cp_got_len
|
||||
|
||||
jsr get_length ; X=0 for literals, returns CC.
|
||||
stx cp_npages+1 ; Hi-byte of length.
|
||||
|
||||
cp_got_len:
|
||||
tax ; Lo-byte of length.
|
||||
|
||||
cp_byte:
|
||||
lda (lzsa_srcptr),y ; CC throughout the execution of
|
||||
sta (lzsa_dstptr),y ; of this .cp_page loop.
|
||||
inc lzsa_srcptr
|
||||
bne cp_skip1
|
||||
inc lzsa_srcptr+1
|
||||
cp_skip1:
|
||||
inc lzsa_dstptr
|
||||
bne cp_skip2
|
||||
inc lzsa_dstptr+1
|
||||
cp_skip2:
|
||||
dex
|
||||
bne cp_byte
|
||||
cp_npages:
|
||||
lda #0 ; Any full pages left to copy?
|
||||
beq lz_offset
|
||||
|
||||
dec cp_npages+1 ; Unlikely, so can be slow
|
||||
bcc cp_byte ; Always true!
|
||||
|
||||
;
|
||||
; Copy bytes from decompressed window.
|
||||
;
|
||||
; N.B. X=0 is expected and guaranteed when we get here.
|
||||
;
|
||||
; xyz
|
||||
; ===========================
|
||||
; 00z 5-bit offset
|
||||
; 01z 9-bit offset
|
||||
; 10z 13-bit offset
|
||||
; 110 16-bit offset
|
||||
; 111 repeat offset
|
||||
;
|
||||
|
||||
lz_offset:
|
||||
lda lzsa_cmdbuf
|
||||
asl
|
||||
bcs get_13_16_rep
|
||||
|
||||
get_5_9_bits:
|
||||
dex ; X=$FF for a 5-bit offset.
|
||||
asl
|
||||
bcs get_9_bits ; Fall through if 5-bit.
|
||||
|
||||
get_13_bits:
|
||||
asl ; Both 5-bit and 13-bit read
|
||||
php ; a nibble.
|
||||
jsr get_nibble
|
||||
plp
|
||||
rol ; Shift into position, clr C.
|
||||
eor #$E1
|
||||
cpx #$00 ; X=$FF for a 5-bit offset.
|
||||
bne set_offset
|
||||
sbc #2 ; 13-bit offset from $FE00.
|
||||
bne set_hi_8 ; Always NZ from previous SBC.
|
||||
|
||||
get_9_bits:
|
||||
asl ; X=$FF if CC, X=$FE if CS.
|
||||
bcc get_lo_8
|
||||
dex
|
||||
bcs get_lo_8 ; Always CS from previous BCC.
|
||||
|
||||
get_13_16_rep:
|
||||
asl
|
||||
bcc get_13_bits ; Shares code with 5-bit path.
|
||||
|
||||
get_16_rep:
|
||||
bmi lz_length ; Repeat previous offset.
|
||||
|
||||
get_16_bits:
|
||||
jsr get_byte ; Get hi-byte of offset.
|
||||
|
||||
set_hi_8:
|
||||
tax
|
||||
|
||||
get_lo_8:
|
||||
lda (lzsa_srcptr),y ; Get lo-byte of offset.
|
||||
inc lzsa_srcptr
|
||||
bne set_offset
|
||||
inc lzsa_srcptr+1
|
||||
|
||||
set_offset:
|
||||
sta lzsa_offset ; Save new offset.
|
||||
stx lzsa_offset+1
|
||||
|
||||
lz_length:
|
||||
ldx #1 ; Hi-byte of length+256.
|
||||
|
||||
lda lzsa_cmdbuf
|
||||
and #$07
|
||||
clc
|
||||
adc #$02
|
||||
cmp #$09 ; Extended length?
|
||||
bcc got_lz_len
|
||||
|
||||
jsr get_length ; X=1 for match, returns CC.
|
||||
inx ; Hi-byte of length+256.
|
||||
|
||||
got_lz_len:
|
||||
eor #$FF ; Negate the lo-byte of length.
|
||||
tay
|
||||
eor #$FF
|
||||
|
||||
get_lz_dst:
|
||||
adc lzsa_dstptr ; Calc address of partial page.
|
||||
sta lzsa_dstptr ; Always CC from previous CMP.
|
||||
iny
|
||||
bcs get_lz_win
|
||||
beq get_lz_win ; Is lo-byte of length zero?
|
||||
dec lzsa_dstptr+1
|
||||
|
||||
get_lz_win:
|
||||
clc ; Calc address of match.
|
||||
adc lzsa_offset ; N.B. Offset is negative!
|
||||
sta lzsa_winptr
|
||||
lda lzsa_dstptr+1
|
||||
adc lzsa_offset+1
|
||||
sta lzsa_winptr+1
|
||||
|
||||
lz_byte:
|
||||
lda (lzsa_winptr),y
|
||||
sta (lzsa_dstptr),y
|
||||
iny
|
||||
bne lz_byte
|
||||
inc lzsa_dstptr+1
|
||||
dex ; Any full pages left to copy?
|
||||
bne lz_more
|
||||
|
||||
jmp cp_length ; Loop around to the beginning.
|
||||
|
||||
lz_more:
|
||||
inc lzsa_winptr+1 ; Unlikely, so can be slow.
|
||||
bne lz_byte ; Always true!
|
||||
|
||||
;
|
||||
; Lookup tables to differentiate literal and match lengths.
|
||||
;
|
||||
|
||||
nibl_len_tbl:
|
||||
.byte 3 ; 0+3 (for literal).
|
||||
.byte 9 ; 2+7 (for match).
|
||||
|
||||
byte_len_tbl:
|
||||
.byte 18 - 1 ; 0+3+15 - CS (for literal).
|
||||
.byte 24 - 1 ; 2+7+15 - CS (for match).
|
||||
|
||||
;
|
||||
; Get 16-bit length in X:A register pair, return with CC.
|
||||
;
|
||||
|
||||
get_length:
|
||||
jsr get_nibble
|
||||
cmp #$0F ; Extended length?
|
||||
bcs byte_length
|
||||
adc nibl_len_tbl,x ; Always CC from previous CMP.
|
||||
|
||||
got_length:
|
||||
ldx #$00 ; Set hi-byte of 4 & 8 bit
|
||||
rts ; lengths.
|
||||
|
||||
byte_length:
|
||||
jsr get_byte ; So rare, this can be slow!
|
||||
adc byte_len_tbl,x ; Always CS from previous CMP
|
||||
bcc got_length
|
||||
beq finished
|
||||
|
||||
word_length:
|
||||
clc ; MUST return CC!
|
||||
jsr get_byte ; So rare, this can be slow!
|
||||
pha
|
||||
jsr get_byte ; So rare, this can be slow!
|
||||
tax
|
||||
pla
|
||||
bne got_word ; Check for zero lo-byte.
|
||||
dex ; Do one less page loop if so.
|
||||
got_word:
|
||||
rts
|
||||
|
||||
get_byte:
|
||||
lda (lzsa_srcptr),y ; Subroutine version for when
|
||||
inc lzsa_srcptr ; inlining isn't advantageous.
|
||||
bne got_byte
|
||||
inc lzsa_srcptr+1
|
||||
got_byte:
|
||||
rts
|
||||
|
||||
finished:
|
||||
pla ; Decompression completed, pop
|
||||
pla ; return address.
|
||||
rts
|
||||
|
||||
;
|
||||
; Get a nibble value from compressed data in A.
|
||||
;
|
||||
|
||||
get_nibble:
|
||||
lsr lzsa_nibflg ; Is there a nibble waiting?
|
||||
lda lzsa_nibble ; Extract the lo-nibble.
|
||||
bcs got_nibble
|
||||
|
||||
inc lzsa_nibflg ; Reset the flag.
|
||||
|
||||
lda (lzsa_srcptr),y
|
||||
inc lzsa_srcptr
|
||||
bne set_nibble
|
||||
inc lzsa_srcptr+1
|
||||
|
||||
set_nibble:
|
||||
sta lzsa_nibble ; Preserve for next time.
|
||||
lsr ; Extract the hi-nibble.
|
||||
lsr
|
||||
lsr
|
||||
lsr
|
||||
|
||||
got_nibble:
|
||||
and #$0F
|
||||
rts
|
||||
.endproc
|
||||
Reference in New Issue
Block a user