334 lines
9.1 KiB
NASM
334 lines
9.1 KiB
NASM
;DYNAMIC_TABLES
|
|
;FAST_LITERAL ;/!\ does not work with current implementation of krill loader, as it touches the lowbyte of the sector_pointers
|
|
;FAST_MATCH
|
|
|
|
;-------------------------------------------------------------------------------
|
|
;Regular version of the Lempel-Ziv decompressor
|
|
;-------------------------------------------------------------------------------
|
|
;lz_match = $f9 ;Match source pointer
|
|
;lz_dst = $fb ;Decompression destination pointer.
|
|
; ;Initialize this to whatever address
|
|
; ;you want to decompress to
|
|
;
|
|
;lz_bits = $fd ;Internal shift register
|
|
;
|
|
;lz_scratch = $fe ;Temporary zeropage storage
|
|
;
|
|
;lz_sector = $0400 ;The one-page buffer from which the
|
|
; ;compressed data is actually read,
|
|
; ;and which gets refilled by
|
|
; ;lz_fetch_sector
|
|
|
|
|
|
;-------------------------------------------------------------------------------
|
|
;This is the user's hook to replenish the sector buffer with some new bytes.
|
|
;
|
|
;A and Y are expected to be preserved while carry must remain set on exit.
|
|
;X should point to the first byte of the new data, e.g. zero for a full 256-byte
|
|
;page of data or two to skip past the sector and track links.
|
|
;
|
|
;When fetching from a larger in-memory array rather than a single sector buffer
|
|
;the lz_sector_ptr1..3 pointers will need to be patched up
|
|
;-------------------------------------------------------------------------------
|
|
;lz_fetch_sector
|
|
; inc lz_sector_ptr1+1
|
|
; inc lz_sector_ptr2+1
|
|
; inc lz_sector_ptr3+1
|
|
; rts
|
|
|
|
|
|
;-------------------------------------------------------------------------------
|
|
;This is the main lz_decrunch function which may be called to decompress an
|
|
;entire file.
|
|
;
|
|
;On entry and exit the X register points to the next available byte in the
|
|
;sector buffer, in ascending order from $00 to $ff.
|
|
;This implies that the initial sector must have already been fetched, and that a
|
|
;file ending with X wrapped to $00 will have needlessly fetched an extra sector
|
|
;(which may be taken advantage of when decoding a contiguous set of files.)
|
|
;-------------------------------------------------------------------------------
|
|
|
|
;******** Start the next match/literal run ********
|
|
|
|
lz_decrunch
|
|
sty lz_sector_ptr1+1
|
|
sty lz_sector_ptr2+1
|
|
sty lz_sector_ptr3+1
|
|
|
|
;fetch depack address
|
|
jsr _lz_refill_bits
|
|
sty lz_dst
|
|
jsr _lz_refill_bits
|
|
sty lz_dst+1
|
|
|
|
!ifdef DYNAMIC_TABLES {
|
|
;load 24 byte long tables
|
|
lda #<_lz_moff_length
|
|
sta _lz_cp+1
|
|
-
|
|
jsr _lz_refill_bits
|
|
_lz_cp sty _lz_moff_length
|
|
inc _lz_cp+1
|
|
lda _lz_cp+1
|
|
cmp #<(_lz_moff_length+$18)
|
|
bne -
|
|
;sec
|
|
} else {
|
|
sec
|
|
}
|
|
;This is the main entry point. Forcibly
|
|
_lz_type_refill jsr _lz_refill_bits ;fill up the the bit buffer on entry
|
|
bne _lz_type_cont ;(BRA)
|
|
|
|
!ifdef FAST_LITERAL {
|
|
_lz_maximum jsr lz_fetch_sector ;Grab a new sector for the literal loop. Carry is set, so we fall through next check
|
|
_lz_mfinish bcc *+4
|
|
inc lz_dst+1
|
|
} else {
|
|
_lz_mfinish bcc *+4
|
|
_lz_maximum inc lz_dst+1 ;This is also used by maximum length
|
|
}
|
|
;literals needing an explicit type bit
|
|
|
|
;Literal or match to follow?
|
|
asl lz_bits
|
|
_lz_type_cont bcc _lz_do_match
|
|
beq _lz_type_refill
|
|
|
|
;******** Process literal run ********
|
|
|
|
lda #%00000000 ;Decode run length
|
|
_lz_lrun_loop rol
|
|
asl lz_bits
|
|
bcs _lz_lrun_test
|
|
_lz_lrun_back asl lz_bits
|
|
bne _lz_lrun_loop
|
|
|
|
jsr _lz_refill_bits
|
|
bne _lz_lrun_loop ;(BRA)
|
|
|
|
_lz_lrun_test bne _lz_lrun_gotten
|
|
jsr _lz_refill_bits
|
|
bcc _lz_lrun_back
|
|
|
|
_lz_lrun_gotten
|
|
sta _lz_copy_cnt+1 ;Store LSB of run-length
|
|
ldy #$00
|
|
!ifdef FAST_LITERAL {
|
|
stx lz_sector_ptr2 ;Store x as lowbyte, so we can use y for lda + sta, and we don't need to bother about overruns
|
|
_lz_lcopy
|
|
lz_sector_ptr2 = *+1 ;Copy the literal data.
|
|
lda lz_sector,y
|
|
} else {
|
|
_lz_lcopy
|
|
lz_sector_ptr2 = *+1 ;Copy the literal data.
|
|
lda lz_sector,x
|
|
inx
|
|
bne *+5
|
|
jsr lz_fetch_sector ;Grab a new sector for the literal loop
|
|
}
|
|
sta (lz_dst),y
|
|
iny
|
|
_lz_copy_cnt cpy #$00
|
|
bne _lz_lcopy
|
|
|
|
;Time to advance the destination pointer.
|
|
;Maximum run length literals exit here as a type-bit needs
|
|
;to be fetched afterwards
|
|
tya
|
|
beq _lz_maximum ;maximum literal run, bump sector pointers and so on
|
|
clc
|
|
!ifdef FAST_LITERAL {
|
|
adc lz_sector_ptr2
|
|
tax ;fix x
|
|
bcc *+6
|
|
jsr lz_fetch_sector ;Grab a new sector for the literal loop
|
|
clc
|
|
tya
|
|
}
|
|
adc lz_dst+0
|
|
sta lz_dst+0
|
|
bcc _lz_do_match
|
|
inc lz_dst+1
|
|
|
|
;One literal run following another only makes sense if the
|
|
;first run is of maximum length and had to be split. As that
|
|
;case has been taken care of we can safely omit the type bit
|
|
;here
|
|
|
|
|
|
;******** Process match ********
|
|
|
|
_lz_do_match lda #%00100000 ;Determine offset length by a two-bit
|
|
_lz_moff_range asl lz_bits ;prefix combined with the first run
|
|
bne *+5 ;length bit (where a one identifies
|
|
jsr _lz_refill_bits ;a two-byte match).
|
|
rol ;The rest of the length bits will
|
|
bcc _lz_moff_range ;then follow *after* the offset data
|
|
|
|
tay
|
|
lda _lz_moff_length,y
|
|
beq _lz_moff_far
|
|
|
|
_lz_moff_loop asl lz_bits ;Load partial offset byte
|
|
bne *+9
|
|
sty lz_scratch
|
|
jsr _lz_refill_bits
|
|
ldy lz_scratch
|
|
|
|
rol
|
|
bcc _lz_moff_loop
|
|
|
|
bmi _lz_moff_near
|
|
|
|
_lz_moff_far sta lz_scratch ;Save the bits we just read as the
|
|
;high-byte
|
|
|
|
lz_sector_ptr3 = *+1
|
|
lda lz_sector,x ;For large offsets we can load the
|
|
inx ;low-byte straight from the stream
|
|
bne *+5 ;without going throught the shift
|
|
jsr lz_fetch_sector ;register
|
|
|
|
; sec
|
|
adc _lz_moff_adjust_lo,y ;y .. 2 .. 5? ?! necessary with a full lowbyte?!?!
|
|
bcs _lz_moff_pageok
|
|
dec lz_scratch
|
|
sec
|
|
_lz_moff_pageok adc lz_dst+0
|
|
sta lz_match+0
|
|
|
|
lda lz_scratch
|
|
adc _lz_moff_adjust_hi,y
|
|
sec
|
|
bcs _lz_moff_join ;(BRA)
|
|
|
|
_lz_moff_near
|
|
; sec ;Special case handling of <8 bit offsets.
|
|
adc _lz_moff_adjust_lo,y;We may can safely ignore the MSB from
|
|
; sec ;the base adjustment table as the
|
|
adc lz_dst+0 ;maximum base (for a 4/5/6/7 bit
|
|
sta lz_match+0 ;length sequence) is 113
|
|
lda #$ff
|
|
_lz_moff_join adc lz_dst+1
|
|
sta lz_match+1
|
|
|
|
cpy #$04 ;Get any remaning run length bits
|
|
lda #%00000001
|
|
bcs _lz_mrun_start ;Sentinel check can be skipped in that case
|
|
|
|
_lz_mrun_loop asl lz_bits
|
|
bne *+5
|
|
jsr _lz_refill_bits
|
|
rol
|
|
asl lz_bits
|
|
bcc _lz_mrun_loop
|
|
bne _lz_mrun_gotten
|
|
jsr _lz_refill_bits
|
|
bcc _lz_mrun_loop
|
|
|
|
;XXX TODO only needed on near matches as offset = 1 in that case
|
|
_lz_mrun_gotten tay ;A 257-byte (=>$00) run serves as a
|
|
beq _lz_end_of_file ;sentinel
|
|
_lz_mrun_start
|
|
sta _lz_mcopy_len
|
|
|
|
ldy #$ff ;The copy loop. This needs to be run
|
|
;forwards since RLE-style matches can overlap the destination
|
|
!ifdef FAST_MATCH {
|
|
lsr ;Check bit 0
|
|
bcc _lz_odd ;Odd or even number? Enter at right position of our loop that always copies two bytes in one go
|
|
}
|
|
_lz_mcopy
|
|
iny
|
|
lda (lz_match),y ;Copy one byte
|
|
!ifdef FAST_MATCH {
|
|
sta (lz_dst),y
|
|
_lz_odd
|
|
iny ;Next byte
|
|
lda (lz_match),y ;And another one
|
|
}
|
|
sta (lz_dst),y
|
|
_lz_mcopy_len = *+1
|
|
cpy #$ff
|
|
bne _lz_mcopy
|
|
|
|
tya ;Advance destination pointer
|
|
; sec
|
|
adc lz_dst+0
|
|
sta lz_dst+0
|
|
jmp _lz_mfinish
|
|
|
|
|
|
;******** Offset coding tables ********
|
|
|
|
;This length table is a bit funky. The idea here is to use the
|
|
;value as the initial value of the shift register instead of
|
|
;keeping a separate counter.
|
|
;In other words we iterate until the leading one is shifted out.
|
|
;Then afterwards the bit just below it (our new sign bit) is set
|
|
;if the offset is shorter than 8-bits, and conversely it's
|
|
;cleared if we need to fetch a separate low-byte
|
|
;as well.
|
|
;The fact that the sign bit is cleared as a flag is compensated
|
|
;for in the lz_moff_adjust_hi table
|
|
|
|
!ifndef DYNAMIC_TABLES {
|
|
_lz_moff_length
|
|
;Long (>2 byte matches)
|
|
!byte %00011111 ;4 bits
|
|
!byte %00000011 ;7 bits
|
|
!byte %01011111 ;10 bits
|
|
!byte %00001011 ;13 bits
|
|
;Short (2 byte matches)
|
|
!byte %01011111 ;10 bits
|
|
!byte %00000000 ;8 bits
|
|
!byte %00000111 ;6 bits
|
|
!byte %00111111 ;3 bits
|
|
_lz_moff_adjust_hi = *-2
|
|
;Long (>2 byte matches)
|
|
; !byte %11111111 ;1-16 (unreferenced)
|
|
; !byte %11111111 ;17-144 (unreferenced)
|
|
!byte %01111111 ;145-1168
|
|
!byte %01111011 ;1169-9360
|
|
;Short (2 byte matches)
|
|
!byte %01111110 ;329-1352
|
|
!byte %11111110 ;73-328
|
|
; !byte %11111111 ;9-72 (unreferenced)
|
|
; !byte %11111111 ;1-8 (unreferenced)
|
|
|
|
_lz_moff_adjust_lo = * - 1
|
|
;Long (>2 byte matches)
|
|
;!byte %11111110 ;1-16
|
|
!byte %11101110 ;17-144
|
|
!byte %01101110 ;145-1168
|
|
!byte %01101110 ;1169-9360
|
|
;Short (2 byte matches)
|
|
!byte %10110110 ;329-1352
|
|
!byte %10110110 ;73-328
|
|
!byte %11110110 ;9-72
|
|
!byte %11111110 ;1-8
|
|
;******** Fetch some more bits to work with ********
|
|
}
|
|
|
|
lz_sector_ptr1 = *+1
|
|
_lz_refill_bits ldy lz_sector,x
|
|
sty lz_bits
|
|
; sec
|
|
rol lz_bits
|
|
inx
|
|
beq lz_fetch_sector
|
|
_lz_end_of_file rts
|
|
|
|
lz_fetch_sector
|
|
inc lz_sector_ptr1+1
|
|
inc lz_sector_ptr2+1
|
|
inc lz_sector_ptr3+1
|
|
rts
|
|
|
|
!ifdef DYNAMIC_TABLES {
|
|
_lz_moff_length
|
|
_lz_moff_adjust_lo = * + 8
|
|
_lz_moff_adjust_hi = * + 16
|
|
}
|