208 lines
8.3 KiB
ArmAsm
208 lines
8.3 KiB
ArmAsm
; void __fastcall__ decompress_lzsa1(const void *src, void *dest)
|
|
;
|
|
; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA1 format.
|
|
;
|
|
; Compress with:
|
|
; lzsa -r -f 1 input.bin output.lzsa1
|
|
;
|
|
; 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_lzsa1
|
|
|
|
.import popax
|
|
.importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
|
|
|
|
lzsa_cmdbuf = tmp1 ; 1 byte.
|
|
lzsa_winptr = ptr1 ; 1 word.
|
|
lzsa_srcptr = ptr2 ; 1 word.
|
|
lzsa_dstptr = ptr3 ; 1 word.
|
|
|
|
lzsa_offset = lzsa_winptr
|
|
|
|
.proc _decompress_lzsa1
|
|
sta lzsa_dstptr
|
|
stx lzsa_dstptr+1
|
|
jsr popax
|
|
sta lzsa_srcptr
|
|
stx lzsa_srcptr+1
|
|
|
|
lzsa1_unpack: ldy #0 ; Initialize source index.
|
|
ldx #0 ; Initialize hi-byte of length.
|
|
|
|
;
|
|
; 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 #$70 ; Extract literal length.
|
|
lsr ; Set CC before ...
|
|
beq lz_offset ; Skip directly to match?
|
|
|
|
lsr ; Get 3-bit literal length.
|
|
lsr
|
|
lsr
|
|
cmp #$07 ; Extended length?
|
|
bcc cp_got_len
|
|
|
|
jsr get_length ; X=0, CS from CMP, 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.
|
|
;
|
|
; Longer but faster.
|
|
;
|
|
; N.B. X=0 is expected and guaranteed when we get here.
|
|
;
|
|
|
|
lz_offset: lda (lzsa_srcptr),y ; Get offset-lo.
|
|
inc lzsa_srcptr
|
|
bne offset_lo
|
|
inc lzsa_srcptr+1
|
|
|
|
offset_lo: sta lzsa_offset
|
|
|
|
lda #$FF ; Get offset-hi.
|
|
bit lzsa_cmdbuf
|
|
bpl offset_hi
|
|
|
|
lda (lzsa_srcptr),y
|
|
inc lzsa_srcptr
|
|
bne offset_hi
|
|
inc lzsa_srcptr+1
|
|
|
|
offset_hi: sta lzsa_offset+1
|
|
|
|
lz_length: lda lzsa_cmdbuf ; X=0 from previous loop.
|
|
and #$0F
|
|
adc #$03 ; Always CC from .cp_page loop.
|
|
cmp #$12 ; Extended length?
|
|
bcc got_lz_len
|
|
|
|
jsr get_length ; X=0, CS from CMP, returns CC.
|
|
|
|
got_lz_len: inx ; Hi-byte of length+256.
|
|
|
|
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!
|
|
|
|
;
|
|
; Get 16-bit length in X:A register pair, return with CC.
|
|
;
|
|
; N.B. X=0 is expected and guaranteed when we get here.
|
|
;
|
|
|
|
get_length: clc ; Add on the next byte to get
|
|
adc (lzsa_srcptr),y ; the length.
|
|
inc lzsa_srcptr
|
|
bne skip_inc
|
|
inc lzsa_srcptr+1
|
|
|
|
skip_inc: bcc got_length ; No overflow means done.
|
|
clc ; MUST return CC!
|
|
tax ; Preserve overflow value.
|
|
|
|
extra_byte: jsr get_byte ; So rare, this can be slow!
|
|
pha
|
|
txa ; Overflow to 256 or 257?
|
|
beq extra_word
|
|
|
|
check_length: pla ; Length-lo.
|
|
bne got_length ; Check for zero.
|
|
dex ; Do one less page loop if so.
|
|
got_length: rts
|
|
|
|
extra_word: jsr get_byte ; So rare, this can be slow!
|
|
tax
|
|
bne check_length ; Length-hi == 0 at EOF.
|
|
|
|
finished: pla ; Length-lo.
|
|
pla ; Decompression completed, pop
|
|
pla ; return address.
|
|
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 ; Inc & test for bank overflow.
|
|
got_byte: rts
|
|
.endproc
|