Merge pull request #2847 from colinleroy/zx02-faster-decompressor

Add a 9% faster ZX02 decompressor
This commit is contained in:
Bob Andrews
2025-11-03 13:34:29 +01:00
committed by GitHub
6 changed files with 791 additions and 0 deletions

179
libsrc/common/zx02_fast.s Normal file
View File

@@ -0,0 +1,179 @@
; void __fastcall__ decompress_zx02_fast(const void *src, void *dest)
;
; De-compressor for ZX02 files
; 9% faster than decompress_zx02, 34 bytes bigger
;
; Compress with:
; zx02 input.bin output.zx0
;
; (c) 2022 DMSC
; Code under MIT license, see LICENSE file.
.export _decompress_zx02_fast
.import popax
.importzp ptr1, ptr2, ptr3, tmp1, tmp2
ZX0_src = ptr1
ZX0_dst = ptr2
pntr = ptr3
offset_hi = tmp1
bitr = tmp2
.proc _decompress_zx02_fast
sta ZX0_dst
stx ZX0_dst+1
jsr popax
sta ZX0_src
stx ZX0_src+1
; Init values
lda #$80
sta bitr
ldy #$FF
sty pntr
iny
sty offset_hi
beq decode_literal
cop0_inc_high:
inc ZX0_src+1
inc ZX0_dst+1
dex
bne cop0
beq cop0_done
; Decode literal: Copy next N bytes from compressed file
; Elias(length) byte[1] byte[2] ... byte[N]
decode_literal:
ldx #$01
jsr get_elias
cop0:
lda (ZX0_src),y
sta (ZX0_dst),y
iny
beq cop0_inc_high
dex
bne cop0
cop0_done:
; Update pointers from Y
tya
clc
adc ZX0_src
sta ZX0_src
bcc :+
inc ZX0_src+1
clc
: tya
adc ZX0_dst
sta ZX0_dst
bcc :+
inc ZX0_dst+1
:
ldy #$00
asl bitr
bcs dzx0s_new_offset
; Copy from last offset (repeat N bytes from last offset)
; Elias(length)
inx
jsr get_elias
dzx0s_copy:
lda ZX0_dst+1
sbc offset_hi ; C=0 from get_elias
sta pntr+1
cop1:
ldy ZX0_dst ; Align dest pointer
lda #$00
sta ZX0_dst
cop1_cont:
lda (pntr), y
sta (ZX0_dst),y
iny
beq cop1_inc_high
dex
bne cop1_cont
cop1_done:
sty ZX0_dst ; Update dest pointer
ldy #$00
asl bitr
bcc decode_literal
; Copy from new offset (repeat N bytes from new offset)
; Elias(MSB(offset)) LSB(offset) Elias(length-1)
dzx0s_new_offset:
; Read elias code for high part of offset
inx
jsr get_elias
beq exit ; Read a 0, signals the end
; Decrease and divide by 2
dex
txa
lsr
sta offset_hi
; Get low part of offset, a literal 7 bits
lda (ZX0_src), y
inc ZX0_src
bne :+
inc ZX0_src+1
: ; Divide by 2
ror
eor #$ff
sta pntr
; And get the copy length.
; Start elias reading with the bit already in carry:
ldx #1
jsr elias_skip1
inx
bcc dzx0s_copy ; C=0 here so equivalent to bra
; Read an elias-gamma interlaced code.
elias_get:
; Read next data bit to result
asl bitr
rol
tax
get_elias:
; Get one bit
asl bitr
bne elias_skip1
; Read new bit from stream
lda (ZX0_src), y
inc ZX0_src
bne :+
inc ZX0_src+1
: ; sec ; not needed, C=1 guaranteed from last bit
rol
sta bitr
elias_skip1:
txa
bcs elias_get
; Got ending bit, stop reading
exit:
rts
cop1_inc_high:
inc ZX0_dst+1
inc pntr+1
dex
bne cop1_cont
beq cop1_done
.endproc