mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-05 19:50:33 +00:00
243 lines
5.9 KiB
Plaintext
243 lines
5.9 KiB
Plaintext
|
********************************
|
||
|
* *
|
||
|
* LZ4FH uncompression for 6502 *
|
||
|
* By Andy McFadden *
|
||
|
* Version 1.0.1, August 2015 *
|
||
|
* *
|
||
|
* Refactored for size & speed *
|
||
|
* by Peter Ferrie. *
|
||
|
* *
|
||
|
* Developed with Merlin-16 *
|
||
|
* *
|
||
|
********************************
|
||
|
lst off
|
||
|
org $0300
|
||
|
|
||
|
*
|
||
|
* Constants
|
||
|
*
|
||
|
lz4fh_magic equ $66 ;ascii 'f'
|
||
|
tok_empty equ 253
|
||
|
tok_eod equ 254
|
||
|
|
||
|
overrun_check equ 0
|
||
|
|
||
|
*
|
||
|
* Variable storage
|
||
|
*
|
||
|
srcptr equ $3c ;2b a1l
|
||
|
dstptr equ $3e ;2b a1h
|
||
|
copyptr equ $00 ;2b
|
||
|
savmix equ $02 ;1b
|
||
|
savlen equ $03 ;1b
|
||
|
|
||
|
*
|
||
|
* ROM routines
|
||
|
*
|
||
|
bell equ $ff3a
|
||
|
monitor equ $ff69
|
||
|
|
||
|
*
|
||
|
* Parameters, stashed at the top of the text input
|
||
|
* buffer. We use this, rather than just having them
|
||
|
* poked directly into the code, so that the 6502 and
|
||
|
* 65816 implementations work the same way without
|
||
|
* either getting weird.
|
||
|
*
|
||
|
in_src equ $2fc ;2b
|
||
|
in_dst equ $2fe ;2b
|
||
|
|
||
|
entry
|
||
|
lda in_src ;copy source address to zero page
|
||
|
sta srcptr
|
||
|
lda in_src+1
|
||
|
sta srcptr+1
|
||
|
lda in_dst ;copy destination address to zero page
|
||
|
sta dstptr
|
||
|
lda in_dst+1
|
||
|
sta dstptr+1
|
||
|
sta _desthi+1
|
||
|
|
||
|
ldy #$00
|
||
|
lda (srcptr),y
|
||
|
cmp #lz4fh_magic ;does magic match?
|
||
|
beq goodmagic
|
||
|
|
||
|
fail
|
||
|
jsr bell
|
||
|
jmp monitor
|
||
|
|
||
|
* These stubs increment the high byte and then jump
|
||
|
* back. This saves a cycle because branch-not-taken
|
||
|
* becomes the common case. We assume that we're not
|
||
|
* unpacking data at $FFxx, so BNE is branch-always.
|
||
|
hi2
|
||
|
inc srcptr+1
|
||
|
bne nohi2
|
||
|
|
||
|
hi3
|
||
|
inc srcptr+1
|
||
|
clc
|
||
|
bcc nohi3
|
||
|
|
||
|
hi4
|
||
|
inc dstptr+1
|
||
|
bne nohi4
|
||
|
|
||
|
notempty
|
||
|
cmp #tok_eod
|
||
|
bne fail
|
||
|
rts ;success!
|
||
|
|
||
|
* handle "special" match values (value in A)
|
||
|
specialmatch
|
||
|
cmp #tok_empty
|
||
|
bne notempty
|
||
|
|
||
|
tya ;empty match, advance srcptr
|
||
|
adc srcptr ; past and jump to main loop
|
||
|
sta srcptr
|
||
|
bcc mainloop
|
||
|
inc srcptr+1
|
||
|
bne mainloop
|
||
|
|
||
|
hi5
|
||
|
inc srcptr+1
|
||
|
clc
|
||
|
bcc nohi5
|
||
|
|
||
|
goodmagic
|
||
|
inc srcptr
|
||
|
bne mainloop
|
||
|
inc srcptr+1
|
||
|
|
||
|
mainloop
|
||
|
* Get the mixed-length byte and handle the literal.
|
||
|
ldy #$00
|
||
|
lda (srcptr),y ;get mixed-length byte
|
||
|
sta savmix
|
||
|
lsr A ;get the literal length
|
||
|
lsr A
|
||
|
lsr A
|
||
|
lsr A
|
||
|
beq noliteral
|
||
|
cmp #$0f ;sets carry for >= 15
|
||
|
bne shortlit
|
||
|
|
||
|
inc srcptr
|
||
|
beq hi2
|
||
|
nohi2
|
||
|
lda (srcptr),y ;get length extension
|
||
|
adc #14 ;(carry set) add 15 - will not exceed 255
|
||
|
|
||
|
* At this point, srcptr holds the address of the "mix"
|
||
|
* word or the length extension, and dstptr holds the
|
||
|
* address of the next output location. So we want to
|
||
|
* read from (srcptr),y+1 and write to (dstptr),y.
|
||
|
* We can do this by sticking the DEY between the LDA
|
||
|
* and STA.
|
||
|
*
|
||
|
* We could save a couple of cycles by substituting
|
||
|
* addr,y in place of (dp),y, but the added setup cost
|
||
|
* would only benefit longer literal strings.
|
||
|
shortlit tax
|
||
|
tay
|
||
|
:litloop
|
||
|
lda (srcptr),y ;5
|
||
|
dey ;2 if len is 255, copy 0-254
|
||
|
sta (dstptr),y ;6
|
||
|
bne :litloop ;3 -> 16 cycles/byte
|
||
|
|
||
|
* Advance srcptr by savlen+1, and dstptr by savlen
|
||
|
txa
|
||
|
sec ;this gets us the +1
|
||
|
adc srcptr
|
||
|
sta srcptr
|
||
|
bcs hi3
|
||
|
nohi3 ;carry cleared by hi3
|
||
|
txa
|
||
|
adc dstptr
|
||
|
sta dstptr
|
||
|
bcs hi4
|
||
|
nohi4
|
||
|
dey ;Y=0; DEY so next INY goes to 0
|
||
|
|
||
|
* Handle match. Y holds an offset into srcptr such
|
||
|
* that we need to increment it once to get the next
|
||
|
* interesting byte.
|
||
|
noliteral
|
||
|
lda savmix
|
||
|
and #$0f
|
||
|
cmp #$0f
|
||
|
blt :shortmatch ;BCC
|
||
|
|
||
|
iny
|
||
|
lda (srcptr),y ;get length extension
|
||
|
cmp #237 ;"normal" values are 0-236
|
||
|
bge specialmatch ;BCS
|
||
|
adc #15 ;will not exceed 255
|
||
|
|
||
|
* Put the destination address into copyptr.
|
||
|
:shortmatch
|
||
|
adc #4 ;min match; won't exceed 255
|
||
|
sta savlen ;save match len for later
|
||
|
tax ;and keep it in X
|
||
|
iny
|
||
|
lda (srcptr),y ;match offset, lo
|
||
|
sta copyptr
|
||
|
iny
|
||
|
lda (srcptr),y ;match offset, hi
|
||
|
_desthi ora #$00 ;OR in hi-res page
|
||
|
sta copyptr+1
|
||
|
|
||
|
* Advance srcptr past the encoded match while we still
|
||
|
* remember how many bytes it took to encode. Y is
|
||
|
* indexing the last value used, so we want to go
|
||
|
* advance srcptr by Y+1.
|
||
|
|
||
|
tya
|
||
|
sec
|
||
|
adc srcptr
|
||
|
sta srcptr
|
||
|
bcs hi5
|
||
|
nohi5 ;hi5 clears carry
|
||
|
|
||
|
* Copy the match. The length is in X. Note this
|
||
|
* must be a forward copy so overlapped data works.
|
||
|
*
|
||
|
* We know the match is at least 4 bytes long, so
|
||
|
* we could save a few cycles by not doing the
|
||
|
* ADC #4 earlier, and unrolling the first 4
|
||
|
* load/store operations here.
|
||
|
ldy #$00
|
||
|
:copyloop
|
||
|
lda (copyptr),y ;5
|
||
|
sta (dstptr),y ;6
|
||
|
iny ;2
|
||
|
dex ;2
|
||
|
bne :copyloop ;3 -> 18 cycles/byte
|
||
|
|
||
|
* advance dstptr past copied data
|
||
|
lda dstptr
|
||
|
adc savlen ;carry is clear
|
||
|
sta dstptr
|
||
|
bcc mainloop
|
||
|
inc dstptr+1
|
||
|
|
||
|
DO overrun_check
|
||
|
LDA dstptr+1
|
||
|
CMP #$60
|
||
|
bcc mainloop
|
||
|
BRK
|
||
|
BRK
|
||
|
|
||
|
ELSE
|
||
|
|
||
|
bne mainloop ;always (not unpacking at $FFxx)
|
||
|
|
||
|
FIN
|
||
|
|
||
|
lst on
|
||
|
sav LZ4FH6502
|
||
|
lst off
|