From 1ae6ddac2060f261136b0d925b078a386c7c6651 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Mon, 6 Jun 2022 00:53:52 -0400 Subject: [PATCH] compression: add some compression tests --- compression/comparison/Makefile | 47 +- compression/comparison/RESULTS | 9 + compression/comparison/exodecrunch.s | 575 +++++++++++++++++++++++++ compression/comparison/exomizer_test.s | 79 ++++ compression/comparison/lzsa_test.s | 5 +- compression/comparison/zx02_optim.s | 144 +++++++ compression/comparison/zx02_test.s | 32 ++ 7 files changed, 882 insertions(+), 9 deletions(-) create mode 100644 compression/comparison/RESULTS create mode 100644 compression/comparison/exodecrunch.s create mode 100644 compression/comparison/exomizer_test.s create mode 100644 compression/comparison/zx02_optim.s create mode 100644 compression/comparison/zx02_test.s diff --git a/compression/comparison/Makefile b/compression/comparison/Makefile index 41515bca..4a052245 100644 --- a/compression/comparison/Makefile +++ b/compression/comparison/Makefile @@ -3,6 +3,8 @@ PNG2GR = ../../utils/gr-utils/png2gr PNG2HGR = ../../utils/hgr-utils/png2hgr LZSA = ~/research/lzsa/lzsa/lzsa B2D = ../../utils/bmp2dhr/b2d +EXOMIZER = ~/research/6502_compression/exomizer.git/src/exomizer +ZX02 = ~/research/6502_compression/zx02.git/build/zx02 DOS33 = ../../utils/dos33fs-utils/dos33 TOKENIZE = ../../utils/asoft_basic-utils/tokenize_asoft @@ -13,10 +15,13 @@ LINKERSCRIPTS = ../../linker_scripts/ all: compression_test.dsk -compression_test.dsk: HELLO LZSA_TEST +compression_test.dsk: HELLO LZSA_TEST EXOMIZER_TEST ZX02_TEST cp $(EMPTYDISK) compression_test.dsk $(DOS33) -y compression_test.dsk SAVE A HELLO $(DOS33) -y compression_test.dsk BSAVE -a 0x6000 LZSA_TEST + $(DOS33) -y compression_test.dsk BSAVE -a 0x6000 EXOMIZER_TEST + $(DOS33) -y compression_test.dsk BSAVE -a 0x6000 ZX02_TEST + #### @@ -29,22 +34,48 @@ HELLO: hello.bas LZSA_TEST: lzsa_test.o ld65 -o LZSA_TEST lzsa_test.o -C $(LINKERSCRIPTS)/apple2_6000.inc -lzsa_test.o: lzsa_test.s graphics_level5.inc +lzsa_test.o: lzsa_test.s decompress_fast_v2.s level5.lzsa ca65 -o lzsa_test.o lzsa_test.s -l lzsa_test.lst ### -graphics_level5.inc: level5.lzsa - echo "level5_lzsa: .incbin \"level5.lzsa\"" > graphics_level5.inc - -### - level5.lzsa: level5.hgr $(LZSA) -r -f2 level5.hgr level5.lzsa +### + +EXOMIZER_TEST: exomizer_test.o + ld65 -o EXOMIZER_TEST exomizer_test.o -C $(LINKERSCRIPTS)/apple2_6000.inc + +exomizer_test.o: exomizer_test.s exodecrunch.s level5.exo + ca65 -o exomizer_test.o exomizer_test.s -l exomizer_test.lst + +### + +level5.exo: level5.hgr +# $(EXOMIZER) mem level5.hgr -o level5.exo + $(EXOMIZER) mem -l "$$6000" "level5.hgr,$$2000" -o level5.exo + + +### + +ZX02_TEST: zx02_test.o + ld65 -o ZX02_TEST zx02_test.o -C $(LINKERSCRIPTS)/apple2_6000.inc + +zx02_test.o: zx02_test.s zx02_optim.s level5.zx02 + ca65 -o zx02_test.o zx02_test.s -l zx02_test.lst + +### + +level5.zx02: level5.hgr + $(ZX02) level5.hgr level5.zx02 + + +### + level5.hgr: level5.png $(PNG2HGR) level5.png > level5.hgr clean: - rm -f HELLO LZSA_TEST *~ *.o *.lst level5.lzsa + rm -f HELLO LZSA_TEST EXOMIZER_TEST *~ *.o *.lst level5.lzsa diff --git a/compression/comparison/RESULTS b/compression/comparison/RESULTS new file mode 100644 index 00000000..36c4e801 --- /dev/null +++ b/compression/comparison/RESULTS @@ -0,0 +1,9 @@ + +LEVEL5.PNG (hires background image from Lemmings, 8k) + + Compressed Decompression + File size Routine Size Time (cycles) FPS + =========================================================== +lzsa 4012 259 bytes 7cc5e = 511,070 ~2.0 +exomizer 3851(?) ??? ????? +zx02_opt 3869 138 bytes 7076f = 460,655 ~2.2 diff --git a/compression/comparison/exodecrunch.s b/compression/comparison/exodecrunch.s new file mode 100644 index 00000000..bc1c9644 --- /dev/null +++ b/compression/comparison/exodecrunch.s @@ -0,0 +1,575 @@ +; +; Copyright (c) 2002 - 2020 Magnus Lind. +; +; This software is provided 'as-is', without any express or implied warranty. +; In no event will the authors be held liable for any damages arising from +; the use of this software. +; +; Permission is granted to anyone to use this software for any purpose, +; including commercial applications, and to alter it and redistribute it +; freely, subject to the following restrictions: +; +; 1. The origin of this software must not be misrepresented; you must not +; claim that you wrote the original software. If you use this software in a +; product, an acknowledgment in the product documentation would be +; appreciated but is not required. +; +; 2. Altered source versions must be plainly marked as such, and must not +; be misrepresented as being the original software. +; +; 3. This notice may not be removed or altered from any distribution. +; +; 4. The names of this software and/or it's copyright holders may not be +; used to endorse or promote products derived from this software without +; specific prior written permission. +; +; ------------------------------------------------------------------- +; Known quirks: +; Can't handle a sequence reference that ends at $ffff. It is left in +; since it is a corner case and fixing it impacts negatively on +; performance or backwards compatibility. +; A simple way to work around this is to not decrunch to address $ffff. +; ------------------------------------------------------------------- +; Controls if the shared get_bits routines should be inlined or not. +;INLINE_GET_BITS=1 +.IFNDEF INLINE_GET_BITS +INLINE_GET_BITS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if literal sequences is not used (the data was crunched with the -c +; flag) then the following line can be uncommented for shorter and. +; slightly faster code. +;LITERAL_SEQUENCES_NOT_USED = 1 +.IFNDEF LITERAL_SEQUENCES_NOT_USED +LITERAL_SEQUENCES_NOT_USED = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length is limited to 256 (the data was crunched with +; the -M256 flag) then the following line can be uncommented for +; shorter and slightly faster code. +;MAX_SEQUENCE_LENGTH_256 = 1 +.IFNDEF MAX_SEQUENCE_LENGTH_256 +MAX_SEQUENCE_LENGTH_256 = 0 +.ENDIF +; ------------------------------------------------------------------- +; if the sequence length 3 has its own offset table (the data was +; crunched with the -P+16 flag) then the following +; line must be uncommented. +;EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 1 +.IFNDEF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE +EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE = 0 +.ENDIF +; ------------------------------------------------------------------- +; if sequence offsets are not reused (the data was crunched with the +; -P-32 flag) then the following line must be uncommented. Uncommenting the +; line will also result in shorter and slightly faster code. +;DONT_REUSE_OFFSET = 1 +.IFNDEF DONT_REUSE_OFFSET +DONT_REUSE_OFFSET = 0 +.ENDIF +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +; ------------------------------------------------------------------- +; if split encoding is used (the data is crunched with the -E flag) +; then the following line must be uncommented. +;ENABLE_SPLIT_ENCODING = 1 +.IFNDEF ENABLE_SPLIT_ENCODING +ENABLE_SPLIT_ENCODING = 0 +.ENDIF + +; ------------------------------------------------------------------- +; The decruncher jsr:s to the get_crunched_byte address when it wants to +; read a crunched byte into A. This subroutine has to preserve X and Y +; register and must not modify the state of the carry nor the overflow flag. +; ------------------------------------------------------------------- +;.import get_crunched_byte +; ------------------------------------------------------------------- +; This function is the heart of the decruncher. (for non split crunched files) +; It initializes the decruncher zeropage locations and precalculates the +; decrunch tables and decrunches the data +; This function will not change the interrupt status bit and it will not +; modify the memory configuration. +; ------------------------------------------------------------------- +.export decrunch +.IF ENABLE_SPLIT_ENCODING <> 0 +; ------------------------------------------------------------------- +; To decrunch files crunched with the split feature (-E) you can't use the +; decrunch function. Instead you call the split_decrunch function. But you +; can only do this if the decrunch table contains the encoding used by the +; file you are decrunching. To generate the correct content for the decrunch +; table call set the get_crunched_byte function to point to the encoding data +; and then call the split_gentable function. +; ------------------------------------------------------------------- +.export split_gentable +.export split_decrunch +.ENDIF +; ------------------------------------------------------------------- +; zero page addresses used +; ------------------------------------------------------------------- +zp_len_lo = $9e +zp_len_hi = $9f + +zp_src_lo = $ae +zp_src_hi = zp_src_lo + 1 + +zp_bits_hi = $a7 +.IF DONT_REUSE_OFFSET = 0 +zp_ro_state = $a8 +.ENDIF + +zp_bitbuf = $fd +zp_dest_lo = zp_bitbuf + 1 ; dest addr lo +zp_dest_hi = zp_bitbuf + 2 ; dest addr hi + +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +encoded_entries = 68 +.ELSE +encoded_entries = 52 +.ENDIF + +tabl_bi = decrunch_table +tabl_lo = decrunch_table + encoded_entries +tabl_hi = decrunch_table + encoded_entries * 2 + + ;; refill bits is always inlined +.MACRO mac_refill_bits + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +.ENDMACRO + +.MACRO mac_get_bits +.IF INLINE_GET_BITS <> 0 +.SCOPE + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvc skip +gb_get_hi: + sec + sta zp_bits_hi + jsr get_crunched_byte +skip: +.ENDSCOPE +.ELSE + jsr get_bits +.ENDIF +.ENDMACRO + +.MACRO mac_init_zp +.SCOPE +; ------------------------------------------------------------------- +; init zeropage and x reg. (8 bytes) +; +init_zp: + jsr get_crunched_byte + sta zp_bitbuf - 1,x + dex + bne init_zp +.ENDSCOPE +.ENDMACRO + +.IF INLINE_GET_BITS = 0 +get_bits: + adc #$80 ; needs c=0, affects v + asl + bpl gb_skip +gb_next: + asl zp_bitbuf + bne gb_ok + mac_refill_bits +gb_ok: + rol + bmi gb_next +gb_skip: + bvs gb_get_hi + rts +gb_get_hi: + sec + sta zp_bits_hi + jmp get_crunched_byte +.ENDIF +; ------------------------------------------------------------------- +; no code below this comment has to be modified in order to generate +; a working decruncher of this source file. +; However, you may want to relocate the tables last in the file to a +; more suitable address. +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; jsr this label to decrunch, it will in turn init the tables and +; call the decruncher +; no constraints on register content, however the +; decimal flag has to be cleared (it almost always is, otherwise do a cld) +decrunch: +.IF ENABLE_SPLIT_ENCODING <> 0 + ldx #3 + jsr internal_gentable + jmp normal_decrunch +split_gentable: + ldx #1 +internal_gentable: + jsr split_init_zp +.ELSE + ldx #3 + mac_init_zp +.ENDIF +; ------------------------------------------------------------------- +; calculate tables (64 bytes) + get_bits macro +; x must be #0 when entering +; + ldy #0 + clc +table_gen: + tax + tya + and #$0f + sta tabl_lo,y + beq shortcut ; start a new sequence +; ------------------------------------------------------------------- + txa + adc tabl_lo - 1,y + sta tabl_lo,y + lda zp_len_hi + adc tabl_hi - 1,y +shortcut: + sta tabl_hi,y +; ------------------------------------------------------------------- + lda #$01 + sta 0 + rts +split_decrunch: + ldx #3 + jsr split_init_zp +; X reg must be 0 here + sec +normal_decrunch: +.ENDIF +; ------------------------------------------------------------------- +; prepare for main decruncher +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state + sec +.ENDIF + ldy zp_dest_lo + stx zp_dest_lo + stx zp_bits_hi +; ------------------------------------------------------------------- +; copy one literal byte to destination (11 bytes) +; +literal_start1: +.IF DECRUNCH_FORWARDS = 0 + tya + bne no_hi_decr + dec zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + dec zp_src_hi +.ENDIF +no_hi_decr: + dey +.ENDIF + jsr get_crunched_byte + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne skip_hi_incr + inc zp_dest_hi +.IF DONT_REUSE_OFFSET = 0 + inc zp_src_hi +.ENDIF +skip_hi_incr: +.ENDIF +; ------------------------------------------------------------------- +; fetch sequence length index (15 bytes) +; x must be #0 when entering and contains the length index + 1 +; when exiting or 0 for literal byte +next_round: +.IF DONT_REUSE_OFFSET = 0 + ror zp_ro_state +.ENDIF + dex + lda zp_bitbuf +no_literal1: + asl + bne nofetch8 + jsr get_crunched_byte + rol +nofetch8: + inx + bcc no_literal1 + sta zp_bitbuf +; ------------------------------------------------------------------- +; check for literal byte (2 bytes) +; + beq literal_start1 +; ------------------------------------------------------------------- +; check for decrunch done and literal sequences (4 bytes) +; + cpx #$11 +.IF INLINE_GET_BITS <> 0 + bcc skip_jmp + jmp exit_or_lit_seq +skip_jmp: +.ELSE + bcs exit_or_lit_seq +.ENDIF +; ------------------------------------------------------------------- +; calulate length of sequence (zp_len) (18(11) bytes) + get_bits macro +; + lda tabl_bi - 1,x + mac_get_bits + adc tabl_lo - 1,x ; we have now calculated zp_len_lo + sta zp_len_lo +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_bits_hi + adc tabl_hi - 1,x ; c = 0 after this. + sta zp_len_hi +; ------------------------------------------------------------------- +; here we decide what offset table to use (27(26) bytes) + get_bits_nc macro +; z-flag reflects zp_len_hi here +; + ldx zp_len_lo +.ELSE + tax +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda #0 +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; here we decide to reuse latest offset or not (13(15) bytes) +; + bit 0 + cpx #$04 +.ELSE + cpx #$03 +.ENDIF + bcs gbnc2_next + lda tabl_bit - 1,x +gbnc2_next: + asl zp_bitbuf + bne gbnc2_ok + tax + jsr get_crunched_byte + rol + sta zp_bitbuf + txa +gbnc2_ok: + rol + bcs gbnc2_next + tax +; ------------------------------------------------------------------- +; calulate absolute offset (zp_src) (17 bytes) + get_bits macro +; + lda tabl_bi,x + mac_get_bits +.IF DECRUNCH_FORWARDS = 0 + adc tabl_lo,x + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + adc zp_dest_hi + sta zp_src_hi +.ELSE + clc + adc tabl_lo,x + eor #$ff + sta zp_src_lo + lda zp_bits_hi + adc tabl_hi,x + eor #$ff + adc zp_dest_hi + sta zp_src_hi + clc +.ENDIF +; ------------------------------------------------------------------- +; prepare for copy loop (2 bytes) +; + ldx zp_len_lo +; ------------------------------------------------------------------- +; main copy loop (30 bytes) +; +copy_next: +.IF DECRUNCH_FORWARDS = 0 + tya + bne copy_skip_hi + dec zp_dest_hi + dec zp_src_hi +copy_skip_hi: + dey +.ENDIF +.IF LITERAL_SEQUENCES_NOT_USED = 0 + bcs get_literal_byte +.ENDIF + lda (zp_src_lo),y +literal_byte_gotten: + sta (zp_dest_lo),y +.IF DECRUNCH_FORWARDS <> 0 + iny + bne copy_skip_hi + inc zp_dest_hi + inc zp_src_hi +copy_skip_hi: +.ENDIF + dex + bne copy_next +.IF MAX_SEQUENCE_LENGTH_256 = 0 + lda zp_len_hi +.IF INLINE_GET_BITS <> 0 + bne copy_next_hi +.ENDIF +.ENDIF + stx zp_bits_hi +.IF INLINE_GET_BITS = 0 + beq next_round +.ELSE + jmp next_round +.ENDIF +.IF MAX_SEQUENCE_LENGTH_256 = 0 +copy_next_hi: + dec zp_len_hi + jmp copy_next +.ENDIF +.IF DONT_REUSE_OFFSET = 0 +; ------------------------------------------------------------------- +; test for offset reuse (11 bytes) +; +test_reuse: + bvs no_reuse +.IF MAX_SEQUENCE_LENGTH_256 <> 0 + lda #$00 ; fetch one bit +.ENDIF + asl zp_bitbuf + bne gbnc1_ok + pha + jsr get_crunched_byte + rol + sta zp_bitbuf + pla +gbnc1_ok: + rol + beq no_reuse ; bit == 0 => C=0, no reuse + bne copy_next ; bit != 0 => C=0, reuse previous offset +.ENDIF +; ------------------------------------------------------------------- +; exit or literal sequence handling (16(12) bytes) +; +exit_or_lit_seq: +.IF LITERAL_SEQUENCES_NOT_USED = 0 + beq decr_exit + jsr get_crunched_byte +.IF MAX_SEQUENCE_LENGTH_256 = 0 + sta zp_len_hi +.ENDIF + jsr get_crunched_byte + tax + bcs copy_next +decr_exit: +.ENDIF + rts +.IF LITERAL_SEQUENCES_NOT_USED = 0 +get_literal_byte: + jsr get_crunched_byte + bcs literal_byte_gotten +.ENDIF +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1, 2 and 3 (3 bytes) +; bits 2, 4, 4 and offsets 64, 48, 32 corresponding to +; %10010000, %11100011, %11100010 +tabl_bit: + .BYTE $90, $e3, $e2 +.ELSE +; ------------------------------------------------------------------- +; the static stable used for bits+offset for lengths 1 and 2 (2 bytes) +; bits 2, 4 and offsets 48, 32 corresponding to %10001100, %11100010 +tabl_bit: + .BYTE $8c, $e2 +.ENDIF + +.IF ENABLE_SPLIT_ENCODING <> 0 +split_init_zp: + mac_init_zp + rts +.ENDIF +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- + +; ------------------------------------------------------------------- +; this 156 (204) byte table area may be relocated. It may also be +; clobbered by other data between decrunches. +; ------------------------------------------------------------------- +decrunch_table: + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.IF EXTRA_TABLE_ENTRY_FOR_LENGTH_THREE <> 0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.ENDIF + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0,0,0,0,0 +; ------------------------------------------------------------------- +; end of decruncher +; ------------------------------------------------------------------- diff --git a/compression/comparison/exomizer_test.s b/compression/comparison/exomizer_test.s new file mode 100644 index 00000000..dde21784 --- /dev/null +++ b/compression/comparison/exomizer_test.s @@ -0,0 +1,79 @@ +; ------------------------------------------------------------------- +; this file is intended to be assembled and linked with the cc65 toolchain. +; It has not been tested with any other assemblers or linkers. +; ------------------------------------------------------------------- +; ------------------------------------------------------------------- +; example usage of the standard decruncher +; this program decrunches data to memory +; ------------------------------------------------------------------- +; if decrunching forwards then the following line must be uncommented. +;DECRUNCH_FORWARDS = 1 +.IFNDEF DECRUNCH_FORWARDS +DECRUNCH_FORWARDS = 0 +.ENDIF +; ------------------------------------------------------------------- +;.import decrunch +;.export get_crunched_byte +;.import end_of_data + +; .byte $01,$08,$0b,$08,<2003,>2003,$9e,'2','0','6','1',0,0,0 +; ------------------------------------------------------------------- +; we begin here +; ------------------------------------------------------------------- + + lda #$20 + sta zp_dest_hi + lda #$00 + sta zp_dest_lo + + +.IF DECRUNCH_FORWARDS = 0 + lda $04 + sta _byte_lo + lda $05 + sta _byte_hi +.ELSE + lda #end_of_data + sta _byte_hi + +; lda $02 + ; sta _byte_lo + ; lda $03 + ; sta _byte_hi +.ENDIF + jsr decrunch + +end: + jmp end + +; ------------------------------------------------------------------- +get_crunched_byte: +.IF DECRUNCH_FORWARDS = 0 + lda _byte_lo + bne _byte_skip_hi + dec _byte_hi +_byte_skip_hi: + dec _byte_lo +.ENDIF +_byte_lo = * + 1 +_byte_hi = * + 2 + lda $ffff ; needs to be set correctly before + +.IF DECRUNCH_FORWARDS <> 0 + inc _byte_lo + bne _byte_skip_hi + inc _byte_hi +_byte_skip_hi: +.ENDIF + rts ; decrunch_file is called. +; end_of_data needs to point to the address just after the address +; of the last byte of crunched data. +; ------------------------------------------------------------------- + +.include "exodecrunch.s" + +.incbin "level5.exo" + +end_of_data: diff --git a/compression/comparison/lzsa_test.s b/compression/comparison/lzsa_test.s index 2d051b08..f28c0433 100644 --- a/compression/comparison/lzsa_test.s +++ b/compression/comparison/lzsa_test.s @@ -21,6 +21,9 @@ end: jmp end -.include "graphics_level5.inc" + .include "decompress_fast_v2.s" + +level5_lzsa: +.incbin "level5.lzsa" diff --git a/compression/comparison/zx02_optim.s b/compression/comparison/zx02_optim.s new file mode 100644 index 00000000..5f6121f2 --- /dev/null +++ b/compression/comparison/zx02_optim.s @@ -0,0 +1,144 @@ +; De-compressor for ZX02 files +; ---------------------------- +; +; Decompress ZX02 data (6502 optimized format), optimized for speed and size +; 138 bytes code, 58.0 cycles/byte in test file. +; +; Compress with: +; zx02 input.bin output.zx0 +; +; (c) 2022 DMSC +; Code under MIT license, see LICENSE file. + + +ZP=$80 + +offset = ZP+0 +ZX0_src = ZP+2 +ZX0_dst = ZP+4 +bitr = ZP+6 +pntr = ZP+7 + + ; Initial values for offset, source, destination and bitr +zx0_ini_block: + .byte $00, $00, comp_data, out_addr, $80 + +;-------------------------------------------------- +; Decompress ZX0 data (6502 optimized format) + +full_decomp: + ; Get initialization block + ldy #7 + +copy_init: lda zx0_ini_block-1, y + sta offset-1, y + dey + bne copy_init + +; Decode literal: Ccopy next N bytes from compressed file +; Elias(length) byte[1] byte[2] ... byte[N] +decode_literal: + jsr get_elias + +cop0: lda (ZX0_src), y + inc ZX0_src + bne plus1 + inc ZX0_src+1 +plus1: sta (ZX0_dst),y + inc ZX0_dst + bne plus2 + inc ZX0_dst+1 +plus2: dex + bne cop0 + + asl bitr + bcs dzx0s_new_offset + +; Copy from last offset (repeat N bytes from last offset) +; Elias(length) + jsr get_elias +dzx0s_copy: + lda ZX0_dst + sbc offset ; C=0 from get_elias + sta pntr + lda ZX0_dst+1 + sbc offset+1 + sta pntr+1 + +cop1: + lda (pntr), y + inc pntr + bne plus3 + inc pntr+1 +plus3: sta (ZX0_dst),y + inc ZX0_dst + bne plus4 + inc ZX0_dst+1 +plus4: dex + bne cop1 + + 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 + jsr get_elias + beq exit ; Read a 0, signals the end + ; Decrease and divide by 2 + dex + txa + lsr ; @ + sta offset+1 + + ; Get low part of offset, a literal 7 bits + lda (ZX0_src), y + inc ZX0_src + bne plus5 + inc ZX0_src+1 +plus5: + ; Divide by 2 + ror ; @ + sta offset + + ; And get the copy length. + ; Start elias reading with the bit already in carry: + ldx #1 + jsr elias_skip1 + + inx + bcc dzx0s_copy + +; Read an elias-gamma interlaced code. +; ------------------------------------ +get_elias: + ; Initialize return value to #1 + ldx #1 + bne elias_start + +elias_get: ; Read next data bit to result + asl bitr + rol ; @ + tax + +elias_start: + ; Get one bit + asl bitr + bne elias_skip1 + + ; Read new bit from stream + lda (ZX0_src), y + inc ZX0_src + bne plus6 + inc ZX0_src+1 +plus6: ;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 diff --git a/compression/comparison/zx02_test.s b/compression/comparison/zx02_test.s new file mode 100644 index 00000000..38b0f0c4 --- /dev/null +++ b/compression/comparison/zx02_test.s @@ -0,0 +1,32 @@ +.include "zp.inc" +.include "hardware.inc" + +lzsa_test: + + bit SET_GR + bit PAGE0 + bit HIRES + bit FULLGR + +; lda #level5_lzsa +; sta getsrc_smc+2 ; LZSA_SRC_HI +; +; lda #$20 + + jsr full_decomp + +; jsr decompress_lzsa2_fast + +end: + jmp end + + +out_addr = $2000 + +.include "zx02_optim.s" + +comp_data: +level5_zx02: +.incbin "level5.zx02"