mirror of
https://github.com/MikhaelKaa/zx_cartridge.git
synced 2026-03-16 14:37:57 +03:00
del content
This commit is contained in:
parent
21e0db2a10
commit
c6c2fe5875
Binary file not shown.
Binary file not shown.
@ -1,24 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# скрипт извлечения ресурсов из TAP файла с помощью tzxlist
|
|
||||||
|
|
||||||
TAP_FILE="batty.tap"
|
|
||||||
|
|
||||||
echo "Извлекаем блоки из $TAP_FILE..."
|
|
||||||
|
|
||||||
tzxlist -d 2 "$TAP_FILE"
|
|
||||||
tzxlist -d 3 "$TAP_FILE"
|
|
||||||
|
|
||||||
# Переименовываем в понятные имена
|
|
||||||
mv 00000002.dat screen.scr # картинка
|
|
||||||
mv 00000003.dat main.bin # код игры
|
|
||||||
rm *.dsc
|
|
||||||
rm *.hdr
|
|
||||||
|
|
||||||
rm *.zx0
|
|
||||||
./../tools/zx0/build/zx0 screen.scr
|
|
||||||
./../tools/zx0/build/zx0 main.bin
|
|
||||||
|
|
||||||
# main.bin -> 0x6800 (26624) точка входа в main
|
|
||||||
|
|
||||||
ls -la
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
SJASMPLUS=sjasmplus
|
|
||||||
|
|
||||||
build: clean
|
|
||||||
mkdir -p build
|
|
||||||
@${SJASMPLUS} ./ram_part.asm --syntax=F
|
|
||||||
@${SJASMPLUS} ./main.asm --syntax=F
|
|
||||||
# @${SJASMPLUS} ./test.asm --syntax=F
|
|
||||||
|
|
||||||
clean:
|
|
||||||
# @rm -f -r ./assets/*.zx0
|
|
||||||
@rm -f -r build
|
|
||||||
|
|
||||||
|
|
||||||
# ./tools/zx0/build/zx0 ./assets/Spec.scr
|
|
||||||
# ./tools/zx0/build/zx0 ./assets/Arsen.scr
|
|
||||||
# ./tools/zx0/build/zx0 ./assets/Jura.scr
|
|
||||||
# tape2wav ./build/GBX2601.tap ./build/GBX2601.wav
|
|
||||||
# run:
|
|
||||||
# fuse --machine 128 -g 3x --tape ./build/GBX2601.tap
|
|
||||||
# assets:
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
; 19 Feb 2026
|
|
||||||
; Михаил Каа
|
|
||||||
|
|
||||||
DEVICE ZXSPECTRUM48
|
|
||||||
ORG 0
|
|
||||||
|
|
||||||
start:
|
|
||||||
di
|
|
||||||
ld sp, 0xffff
|
|
||||||
jp main
|
|
||||||
|
|
||||||
ORG 100
|
|
||||||
main:
|
|
||||||
ld bc, 0x7ffd
|
|
||||||
ld a, 0b00010000
|
|
||||||
ld (0x5B5C), a
|
|
||||||
out (c), a
|
|
||||||
|
|
||||||
ld bc, 0xdf7f
|
|
||||||
ld a, 0b00000001
|
|
||||||
out (c), a
|
|
||||||
|
|
||||||
ld hl, 16384
|
|
||||||
ld de, 16385
|
|
||||||
ld bc, 49151
|
|
||||||
ld (hl), 0
|
|
||||||
ldir
|
|
||||||
|
|
||||||
ld hl, batty_scr
|
|
||||||
ld de, 0x4000
|
|
||||||
call dzx0_standard
|
|
||||||
|
|
||||||
ld hl, batty_bin
|
|
||||||
ld de, 0x6800
|
|
||||||
call dzx0_standard
|
|
||||||
|
|
||||||
ld hl, bvars
|
|
||||||
ld de, 0x5c00
|
|
||||||
ld bc, bvars_end - bvars
|
|
||||||
ldir
|
|
||||||
|
|
||||||
ld hl, ram_part
|
|
||||||
ld de, 0x6700
|
|
||||||
ld bc, ram_part_end - ram_part
|
|
||||||
ldir
|
|
||||||
|
|
||||||
; ld bc, 65535
|
|
||||||
; call delay
|
|
||||||
; ld bc, 65535
|
|
||||||
; call delay
|
|
||||||
; ld bc, 65535
|
|
||||||
; call delay
|
|
||||||
; ld bc, 65535
|
|
||||||
; call delay
|
|
||||||
|
|
||||||
jp 0x6700
|
|
||||||
|
|
||||||
jp main
|
|
||||||
|
|
||||||
; Процедура задержки
|
|
||||||
; bc - время
|
|
||||||
; delay:
|
|
||||||
; dec bc
|
|
||||||
; ld a, b
|
|
||||||
; or c
|
|
||||||
; jr nz, delay
|
|
||||||
; ret
|
|
||||||
|
|
||||||
INCLUDE "./tools/zx0/z80/dzx0_standard.asm"
|
|
||||||
|
|
||||||
batty_scr:
|
|
||||||
INCBIN "./Batty/screen.scr.zx0"
|
|
||||||
|
|
||||||
batty_bin:
|
|
||||||
INCBIN "./Batty/main.bin.zx0"
|
|
||||||
|
|
||||||
ram_part:
|
|
||||||
INCBIN "./build/ram_part_6700.bin"
|
|
||||||
ram_part_end
|
|
||||||
|
|
||||||
bvars:
|
|
||||||
INCBIN "./Batty/b48v.bin"
|
|
||||||
bvars_end
|
|
||||||
|
|
||||||
end:
|
|
||||||
; Выводим размер бинарника.
|
|
||||||
display "Cartridge BIOS code size: ", /d, end - start
|
|
||||||
display "Cartridge BIOS code start: ", /d, start
|
|
||||||
display "Cartridge BIOS code end: ", /d, end
|
|
||||||
|
|
||||||
SAVEBIN "build/bios_0000.bin", start, 16384
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
; 19 Feb 2026
|
|
||||||
; Михаил Каа
|
|
||||||
|
|
||||||
DEVICE ZXSPECTRUM48
|
|
||||||
ORG 0x6700
|
|
||||||
|
|
||||||
start:
|
|
||||||
ld bc, 0xbf7f
|
|
||||||
ld a, 0b10000000
|
|
||||||
out (c), a
|
|
||||||
|
|
||||||
ld sp, 0x6000
|
|
||||||
|
|
||||||
ld iy, 23610
|
|
||||||
|
|
||||||
wait_any_key:
|
|
||||||
ld bc, 0x00fe
|
|
||||||
in a, (c)
|
|
||||||
and 0x1f
|
|
||||||
cp 0x1f
|
|
||||||
jr z, wait_any_key
|
|
||||||
|
|
||||||
jp 0x6800
|
|
||||||
|
|
||||||
end:
|
|
||||||
; Выводим размер бинарника.
|
|
||||||
display "ram_part code size: ", /d, end - start
|
|
||||||
display "ram_part code start: ", /d, start
|
|
||||||
display "ram_part code end: ", /d, end
|
|
||||||
|
|
||||||
SAVEBIN "./build/ram_part_6700.bin", start, end - start
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
; Простейший тест переключения банков для карика
|
|
||||||
; Банк 0 всегда в 0000-1FFF, переключаемый в 2000-3FFF
|
|
||||||
; Цикл: переключить банк, прочитать байт из 2000h, повторить
|
|
||||||
|
|
||||||
DEVICE ZXSPECTRUM48
|
|
||||||
|
|
||||||
ORG 0
|
|
||||||
start:
|
|
||||||
di
|
|
||||||
jp main
|
|
||||||
|
|
||||||
; векторы RST (просто возврат)
|
|
||||||
ORG 0x08
|
|
||||||
ret
|
|
||||||
ORG 0x10
|
|
||||||
ret
|
|
||||||
ORG 0x18
|
|
||||||
ret
|
|
||||||
ORG 0x20
|
|
||||||
ret
|
|
||||||
ORG 0x28
|
|
||||||
ret
|
|
||||||
ORG 0x30
|
|
||||||
ret
|
|
||||||
ORG 0x38
|
|
||||||
ret
|
|
||||||
|
|
||||||
ORG 0x100
|
|
||||||
main:
|
|
||||||
ld bc, 0xdf7f ; порт выбора банка
|
|
||||||
ld d, 0 ; начальный банк
|
|
||||||
loop:
|
|
||||||
ld a, d
|
|
||||||
out (c), a ; переключить банк
|
|
||||||
nop ; небольшая пауза
|
|
||||||
nop
|
|
||||||
ld hl, 0x2000
|
|
||||||
ld a, (hl) ; прочитать первый байт банка
|
|
||||||
inc d ; следующий банк
|
|
||||||
jr loop
|
|
||||||
|
|
||||||
end:
|
|
||||||
; Выводим размер бинарника.
|
|
||||||
display "test code size: ", /d, end - start
|
|
||||||
display "test code start: ", /d, start
|
|
||||||
display "test code end: ", /d, end
|
|
||||||
|
|
||||||
SAVEBIN "build/test_0000.bin", start, 16384
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
BSD 3-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2021, Einar Saukas
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# We only allow compilation on linux!
|
|
||||||
ifneq ($(shell uname), Linux)
|
|
||||||
$(error OS must be WSL or Linux!)
|
|
||||||
endif
|
|
||||||
|
|
||||||
CC = gcc
|
|
||||||
#CFLAGS = -ox -ob -ol+ -onatx -oh -zp8 -g0 -Ofast -oe -ot -Wall -xc -s -finline-functions -floop-optimize -fno-stack-check -march=i386 -mtune=i686
|
|
||||||
CFLAGS = -Wall
|
|
||||||
RM = rm
|
|
||||||
|
|
||||||
all: build_dir zx0 dzx0
|
|
||||||
|
|
||||||
build_dir:
|
|
||||||
mkdir -p build
|
|
||||||
|
|
||||||
zx0:
|
|
||||||
$(CC) $(CFLAGS) -o build/zx0 src/zx0.c src/optimize.c src/compress.c src/memory.c
|
|
||||||
|
|
||||||
dzx0:
|
|
||||||
$(CC) $(CFLAGS) -o build/dzx0 src/dzx0.c
|
|
||||||
|
|
||||||
clean:
|
|
||||||
# Remove everything except source files
|
|
||||||
rm -r -f build
|
|
||||||
@ -1,476 +0,0 @@
|
|||||||
# ZX0
|
|
||||||
|
|
||||||
**ZX0** is an optimal data compressor for a custom
|
|
||||||
[LZ77/LZSS](https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Storer%E2%80%93Szymanski)
|
|
||||||
based compression format, that provides a tradeoff between high compression
|
|
||||||
ratio, and extremely simple fast decompression. Therefore it's especially
|
|
||||||
appropriate for low-end platforms, including 8-bit computers like the ZX
|
|
||||||
Spectrum.
|
|
||||||
|
|
||||||
A comparison with other compressors (courtesy of **introspec/spke**) can be seen
|
|
||||||
[here](https://www.cpcwiki.eu/forum/programming/new-cruncher-zx0/msg197727/#msg197727).
|
|
||||||
|
|
||||||
|
|
||||||
_**WARNING**: The ZX0 file format was changed in version 2. This new format allows
|
|
||||||
decompressors to be slightly smaller and run slightly faster. If you need to compress
|
|
||||||
a file to the old "classic" file format from version 1, then execute ZX0 compressor
|
|
||||||
using parameter "-c"._
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To compress a file, use the command-line compressor as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
zx0 Cobra.scr
|
|
||||||
```
|
|
||||||
|
|
||||||
This will generate a compressed file called "Cobra.scr.zx0".
|
|
||||||
|
|
||||||
Afterwards you can choose a decompressor routine in assembly Z80, according to
|
|
||||||
your requirements for speed and size:
|
|
||||||
|
|
||||||
* "Standard" routine: 68 bytes only
|
|
||||||
* "Turbo" routine: 126 bytes, about 21% faster
|
|
||||||
* "Fast" routine: 187 bytes, about 25% faster
|
|
||||||
* "Mega" routine: 673 bytes, about 28% faster
|
|
||||||
|
|
||||||
Finally compile the chosen decompressor routine and load the compressed file
|
|
||||||
somewhere in memory. To decompress data, just call the routine specifying the
|
|
||||||
source address of compressed data in HL and the target address in DE.
|
|
||||||
|
|
||||||
For instance, if you compile the decompressor routine to address 65000, load
|
|
||||||
"Cobra.scr.zx0" at address 51200, and you want to decompress it directly to the
|
|
||||||
screen, then execute the following code:
|
|
||||||
|
|
||||||
```
|
|
||||||
LD HL, 51200 ; source address (put "Cobra.scr.zx0" there)
|
|
||||||
LD DE, 16384 ; target address (screen memory in this case)
|
|
||||||
CALL 65000 ; decompress routine compiled at this address
|
|
||||||
```
|
|
||||||
|
|
||||||
It's also possible to decompress data into a memory area that partially overlaps
|
|
||||||
the compressed data itself (only if you won't need to decompress it again later,
|
|
||||||
obviously). In this case, the last address of compressed data must be at least
|
|
||||||
"delta" bytes higher than the last address of decompressed data. The exact value
|
|
||||||
of "delta" for each case is reported by **ZX0** during compression. See image
|
|
||||||
below:
|
|
||||||
|
|
||||||
```
|
|
||||||
|------------------| compressed data
|
|
||||||
|---------------------------------| decompressed data
|
|
||||||
start >> <--->
|
|
||||||
delta
|
|
||||||
```
|
|
||||||
|
|
||||||
For convenience, there's also a command-line decompressor that works as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
dzx0 Cobra.scr.zx0
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
The **ZX0** optimal compressor algorithm is fairly complex, thus compressing
|
|
||||||
typical files can take a few seconds. During development, you can speed up this
|
|
||||||
process simply using **ZX0** in "quick" mode. This will produce a non-optimal
|
|
||||||
larger compressed file but execute almost instantly:
|
|
||||||
|
|
||||||
```
|
|
||||||
zx0 -q Cobra.scr
|
|
||||||
```
|
|
||||||
|
|
||||||
This way, you can repeatedly modify your files, then quickly compress and test
|
|
||||||
them. Later, when you finish changing these files, you can compress them again
|
|
||||||
without "quick" mode for maximum compression. Notice that using "quick" mode
|
|
||||||
will only affect the size of the compressed file, not its format. Therefore
|
|
||||||
all decompressor routines will continue to work exactly the same way.
|
|
||||||
|
|
||||||
Fortunately all complexity lies on the compression process only. The **ZX0**
|
|
||||||
compression format itself is very simple and efficient, providing a high
|
|
||||||
compression ratio that can be decompressed quickly and easily. The provided
|
|
||||||
**ZX0** decompressor routines in assembly Z80 are small and fast, they only use
|
|
||||||
main registers (BC, DE, HL, AF), consume very little stack space, and do not
|
|
||||||
require additional decompression buffer.
|
|
||||||
|
|
||||||
The provided **ZX0** decompressor in C writes the output file while reading the
|
|
||||||
compressed file, without keeping it in memory. Therefore it always use the same
|
|
||||||
amount of memory, regardless of file size. Thus even large compressed files can
|
|
||||||
be decompressed in very small computers with limited memory, even if it took
|
|
||||||
considerable time and memory to compress it originally. It means decompressing
|
|
||||||
within asymptotically optimal space and time O(n) only, using storage space O(n)
|
|
||||||
for input and output files, and only memory space O(w) for processing.
|
|
||||||
|
|
||||||
|
|
||||||
## File Format
|
|
||||||
|
|
||||||
The **ZX0** compressed format is very simple. There are only 3 types of blocks:
|
|
||||||
|
|
||||||
* Literal (copy next N bytes from compressed file)
|
|
||||||
```
|
|
||||||
0 Elias(length) byte[1] byte[2] ... byte[N]
|
|
||||||
```
|
|
||||||
|
|
||||||
* Copy from last offset (repeat N bytes from last offset)
|
|
||||||
```
|
|
||||||
0 Elias(length)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Copy from new offset (repeat N bytes from new offset)
|
|
||||||
```
|
|
||||||
1 Elias(MSB(offset)+1) LSB(offset) Elias(length-1)
|
|
||||||
```
|
|
||||||
|
|
||||||
**ZX0** needs only 1 bit to distinguish between these blocks, because literal
|
|
||||||
blocks cannot be consecutive, and reusing last offset can only happen after a
|
|
||||||
literal block. The first block is always a literal, so the first bit is omitted.
|
|
||||||
|
|
||||||
The offset MSB and all lengths are stored using interlaced
|
|
||||||
[Elias Gamma Coding](https://en.wikipedia.org/wiki/Elias_gamma_coding). When
|
|
||||||
offset MSB equals 256 it means EOF. The offset LSB is stored using 7 bits
|
|
||||||
instead of 8, because it produces better results in most practical cases.
|
|
||||||
|
|
||||||
|
|
||||||
## Advanced Features
|
|
||||||
|
|
||||||
The **ZX0** compressor contains a few extra "hidden" features, that are slightly
|
|
||||||
harder to use properly, and not supported by the **ZX0** decompressor in C. Please
|
|
||||||
read carefully these instructions before attempting to use any of them!
|
|
||||||
|
|
||||||
|
|
||||||
#### _COMPRESSING BACKWARDS_
|
|
||||||
|
|
||||||
When using **ZX0** for "in-place" decompression (decompressing data to overlap the
|
|
||||||
same memory area storing the compressed data), you must always leave a small
|
|
||||||
margin of "delta" bytes of compressed data at the end. However it won't work to
|
|
||||||
decompress some large data that will occupy all the upper memory until the last
|
|
||||||
memory address, since there won't be even a couple bytes left at the end.
|
|
||||||
|
|
||||||
A possible workaround is to compress and decompress data backwards, starting at
|
|
||||||
the last memory address. Therefore you will only need to leave a small margin of
|
|
||||||
"delta" bytes of compressed data at the beginning instead. Technically, it will
|
|
||||||
require that lowest address of compressed data should be at least "delta" bytes
|
|
||||||
lower than lowest address of decompressed data. See image below:
|
|
||||||
|
|
||||||
compressed data |------------------|
|
|
||||||
decompressed data |---------------------------------|
|
|
||||||
<---> << start
|
|
||||||
delta
|
|
||||||
|
|
||||||
To compress a file backwards, use the command-line compressor as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
zx0 -b Cobra.scr
|
|
||||||
```
|
|
||||||
|
|
||||||
To decompress it later, you must call one of the supplied "backwards" variants
|
|
||||||
of the Assembly decompressor, specifying last source address of compressed data
|
|
||||||
in HL and last target address in DE.
|
|
||||||
|
|
||||||
For instance, if you compile a "backwards" Assembly decompressor routine to
|
|
||||||
address 64000, load backwards compressed file "Cobra.scr.zx0" (with size 2202
|
|
||||||
bytes) to address 51200, and want to decompress it directly to the ZX Spectrum
|
|
||||||
screen (with 6912 bytes), then execute the following code:
|
|
||||||
|
|
||||||
```
|
|
||||||
LD HL, 51200+2202-1 ; source (last address of "Cobra.scr.zx0")
|
|
||||||
LD DE, 16384+6912-1 ; target (last address of screen memory)
|
|
||||||
CALL 64000 ; backwards decompress routine
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that compressing backwards may sometimes produce slightly smaller
|
|
||||||
compressed files in certain cases, slightly larger compressed files in others.
|
|
||||||
Overall it shouldn't make much difference either way.
|
|
||||||
|
|
||||||
|
|
||||||
#### _COMPRESSING WITH PREFIX_
|
|
||||||
|
|
||||||
The LZ77/LZSS compression is achieved by "abbreviating repetitions", such that
|
|
||||||
certain sequences of bytes are replaced with much shorter references to previous
|
|
||||||
occurrences of these same sequences. For this reason, it's harder to get very
|
|
||||||
good compression ratio on very short files, or in the initial parts of larger
|
|
||||||
files, due to lack of choices for previous sequences that could be referenced.
|
|
||||||
|
|
||||||
A possible improvement is to compress data while also taking into account what
|
|
||||||
else will be already stored in memory during decompression later. Thus the
|
|
||||||
compressed data may even contain shorter references to repetitions stored in
|
|
||||||
some previous "prefix" memory area, instead of just repetitions within the
|
|
||||||
decompressed area itself.
|
|
||||||
|
|
||||||
An input file may contain both some prefix data to be referenced only, and the
|
|
||||||
actual data to be compressed. An optional parameter can specify how many bytes
|
|
||||||
must be skipped before compression. See below:
|
|
||||||
|
|
||||||
```
|
|
||||||
compressed data
|
|
||||||
|-------------------|
|
|
||||||
prefix decompressed data
|
|
||||||
|--------------|---------------------------------|
|
|
||||||
start >>
|
|
||||||
<--------------> <--->
|
|
||||||
skip delta
|
|
||||||
```
|
|
||||||
|
|
||||||
As usual, if you want to decompress data into a memory area that partially
|
|
||||||
overlaps the compressed data itself, the last address of compressed data must be
|
|
||||||
at least "delta" bytes higher than the last address of decompressed data.
|
|
||||||
|
|
||||||
For instance, if you want the first 6144 bytes of a certain file to be skipped
|
|
||||||
(not compressed but possibly referenced), then use the command-line compressor
|
|
||||||
as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
zx0 +6144 Cobra.cbr
|
|
||||||
```
|
|
||||||
|
|
||||||
In practice, suppose an action game uses a few generic sprites that are common
|
|
||||||
for all levels (such as player graphics), and other sprites are specific for
|
|
||||||
each level (such as enemies). All generic sprites must stay always accessible at
|
|
||||||
a certain memory area, but any level specific data can be only decompressed as
|
|
||||||
needed, to the memory area immediately following it. In this case, the generic
|
|
||||||
sprites area could be used as prefix when compressing and decompressing each
|
|
||||||
level, in an attempt to improve compression. For instance, suppose generic
|
|
||||||
graphics are loaded from file "generic.gfx" to address 56000, occupying 2500
|
|
||||||
bytes, and level specific graphics will be decompressed immediately afterwards,
|
|
||||||
to address 58500. To compress each level using "generic.gfx" as a 2500 bytes
|
|
||||||
prefix, use the command-line compressor as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
copy /b generic.gfx+level_1.gfx prefixed_level_1.gfx
|
|
||||||
zx0 +2500 prefixed_level_1.gfx
|
|
||||||
|
|
||||||
copy /b generic.gfx+level_2.gfx prefixed_level_2.gfx
|
|
||||||
zx0 +2500 prefixed_level_2.gfx
|
|
||||||
|
|
||||||
copy /b generic.gfx+level_3.gfx prefixed_level_3.gfx
|
|
||||||
zx0 +2500 prefixed_level_3.gfx
|
|
||||||
```
|
|
||||||
|
|
||||||
To decompress it later, you simply need to use one of the normal variants of the
|
|
||||||
Assembly decompressor, as usual. In this case, if you loaded compressed file
|
|
||||||
"prefixed_level_1.gfx.zx0" to address 48000 for instance, decompressing it will
|
|
||||||
require the following code:
|
|
||||||
|
|
||||||
```
|
|
||||||
LD HL, 48000 ; source address (put "prefixed_level_1.gfx.zx0" there)
|
|
||||||
LD DE, 58500 ; target address (level specific memory area in this case)
|
|
||||||
CALL 65000 ; decompress routine compiled at this address
|
|
||||||
```
|
|
||||||
|
|
||||||
However decompression will only work properly if exactly the same prefix data is
|
|
||||||
present in the memory area immediately preceding the decompression address.
|
|
||||||
Therefore you must be extremely careful to ensure the prefix area does not store
|
|
||||||
variables, self-modifying code, or anything else that may change prefix content
|
|
||||||
between compression and decompression. Also don't forget to recompress your
|
|
||||||
files whenever you modify a prefix!
|
|
||||||
|
|
||||||
In certain cases, compressing with a prefix may considerably help compression.
|
|
||||||
In others, it may not even make any difference. It mostly depends on how much
|
|
||||||
similarity exists between data to be compressed and its provided prefix.
|
|
||||||
|
|
||||||
|
|
||||||
#### _COMPRESSING BACKWARDS WITH SUFFIX_
|
|
||||||
|
|
||||||
Both features above can be used together. A file can be compressed backwards,
|
|
||||||
with an optional parameter to specify how many bytes should be skipped (not
|
|
||||||
compressed but possibly referenced) from the end of the input file instead. See
|
|
||||||
below:
|
|
||||||
|
|
||||||
```
|
|
||||||
compressed data
|
|
||||||
|-------------------|
|
|
||||||
decompressed data suffix
|
|
||||||
|---------------------------------|--------------|
|
|
||||||
<< start
|
|
||||||
<---> <-------------->
|
|
||||||
delta skip
|
|
||||||
```
|
|
||||||
|
|
||||||
As usual, if you want to decompress data into a memory area that partially
|
|
||||||
overlaps the compressed data itself, lowest address of compressed data must be
|
|
||||||
at least "delta" bytes lower than lowest address of decompressed data.
|
|
||||||
|
|
||||||
For instance, if you want to skip the last 768 bytes of a certain input file and
|
|
||||||
compress everything else (possibly referencing this "suffix" of 768 bytes), then
|
|
||||||
use the command-line compressor as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
zx0 -b +768 Cobra.cbr
|
|
||||||
```
|
|
||||||
|
|
||||||
In previous example, suppose the action game now stores level-specific sprites
|
|
||||||
in the memory area from address 33000 to 33511 (512 bytes), just before generic
|
|
||||||
sprites that are stored from address 33512 to 34535 (1024 bytes). In this case,
|
|
||||||
these generic sprites could be used as suffix when compressing and decompressing
|
|
||||||
level-specific data as needed, in an attempt to improve compression. To compress
|
|
||||||
each level using "generic.gfx" as a 1024 bytes suffix, use the command-line
|
|
||||||
compressor as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
copy /b level_1.gfx+generic.gfx level_1_suffixed.gfx
|
|
||||||
zx0 -b +1024 level_1_suffixed.gfx
|
|
||||||
|
|
||||||
copy /b level_2.gfx+generic.gfx level_2_suffixed.gfx
|
|
||||||
zx0 -b +1024 level_2_suffixed.gfx
|
|
||||||
|
|
||||||
copy /b level_3.gfx+generic.gfx level_3_suffixed.gfx
|
|
||||||
zx0 -b +1024 level_3_suffixed.gfx
|
|
||||||
```
|
|
||||||
|
|
||||||
To decompress it later, use the backwards variant of the Assembly decompressor.
|
|
||||||
In this case, if you compile a "backwards" decompressor routine to address
|
|
||||||
64000, and load compressed file "level_1_suffixed.gfx.zx0" (with 217 bytes) to
|
|
||||||
address 39000 for instance, decompressing it will require the following code:
|
|
||||||
|
|
||||||
```
|
|
||||||
LD HL, 39000+217-1 ; source (last address of "level_1_suffixed.gfx.zx0")
|
|
||||||
LD DE, 33000+512-1 ; target (last address of level-specific data)
|
|
||||||
CALL 64000 ; backwards decompress routine
|
|
||||||
```
|
|
||||||
|
|
||||||
Analogously, decompression will only work properly if exactly the same suffix
|
|
||||||
data is present in the memory area immediately following the decompression area.
|
|
||||||
Therefore you must be extremely careful to ensure the suffix area does not store
|
|
||||||
variables, self-modifying code, or anything else that may change suffix content
|
|
||||||
between compression and decompression. Also don't forget to recompress your
|
|
||||||
files whenever you modify a suffix!
|
|
||||||
|
|
||||||
Also if you are using "in-place" decompression, you must leave a small margin of
|
|
||||||
"delta" bytes of compressed data just before the decompression area.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The **ZX0** data compression format and algorithm was designed and implemented
|
|
||||||
by **Einar Saukas**. Special thanks to **introspec/spke** for several
|
|
||||||
suggestions and improvements, and together with **uniabis** for providing the
|
|
||||||
"Fast" decompressor. Also special thanks to **Urusergi** for additional ideas
|
|
||||||
and improvements.
|
|
||||||
|
|
||||||
The optimal C compressor is available under the "BSD-3" license. In practice,
|
|
||||||
this is relevant only if you want to modify its source code and/or incorporate
|
|
||||||
the compressor within your own products. Otherwise, if you just execute it to
|
|
||||||
compress files, you can simply ignore these conditions.
|
|
||||||
|
|
||||||
The decompressors can be used freely within your own programs (either for the
|
|
||||||
ZX Spectrum or any other platform), even for commercial releases. The only
|
|
||||||
condition is that you must indicate somehow in your documentation that you have
|
|
||||||
used **ZX0**.
|
|
||||||
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
**ZX0** implemented in other programming languages:
|
|
||||||
|
|
||||||
* [ZX0-Java](https://github.com/einar-saukas/ZX0-Java) - Faster
|
|
||||||
multi-thread data compressor for **ZX0** in [Java](https://www.java.com/).
|
|
||||||
|
|
||||||
* [ZX0-Kotlin](https://github.com/einar-saukas/ZX0-Kotlin) - Faster
|
|
||||||
multi-thread data compressor for **ZX0** in [Kotlin](https://kotlinlang.org/).
|
|
||||||
|
|
||||||
* [Salvador](https://github.com/emmanuel-marty/salvador) - A non-optimal but
|
|
||||||
much faster data compressor for **ZX0** in C.
|
|
||||||
|
|
||||||
**ZX0** ported to other platforms:
|
|
||||||
|
|
||||||
* [DEC PDP11](https://github.com/ivagorRetrocomp/DeZX) _("classic" file format v1)_
|
|
||||||
|
|
||||||
* [Hitachi 6309](https://github.com/dougmasten/zx0-6x09) _("classic" file format v1)_
|
|
||||||
|
|
||||||
* [Intel 8080](https://github.com/ivagorRetrocomp/DeZX) _("classic" file format v1)_
|
|
||||||
|
|
||||||
* [Intel 8088/x86](https://github.com/emmanuel-marty/unzx0_x86) _(all formats)_
|
|
||||||
|
|
||||||
* [MOS 6502](https://github.com/bboxy/bitfire/tree/master/packer/zx0/6502) _(all formats)_
|
|
||||||
|
|
||||||
* [MOS 6502](https://xxl.atari.pl/zx0-decompressor/) (stream) - _(all formats)_
|
|
||||||
|
|
||||||
* [Motorola 6809](https://github.com/dougmasten/zx0-6x09) _("classic" file format v1)_
|
|
||||||
|
|
||||||
* [Motorola 68000](https://github.com/emmanuel-marty/unzx0_68000) _(all formats)_
|
|
||||||
|
|
||||||
Tools supporting **ZX0**:
|
|
||||||
|
|
||||||
* [z88dk](http://www.z88dk.org/) - The main C compiler for Z80 machines, that
|
|
||||||
provides built-in support for **ZX0**, **ZX1**, **ZX2**, and **ZX7**.
|
|
||||||
|
|
||||||
* [ZX Basic](https://zxbasic.readthedocs.io/) - The main BASIC compiler for
|
|
||||||
Z80 machines, that provides built-in support for **ZX0**.
|
|
||||||
|
|
||||||
* [Mad-Pascal](https://github.com/tebe6502/Mad-Pascal) - The 32-bit Turbo
|
|
||||||
Pascal compiler for Atari XE/XL, that provides built-in support for **ZX0**.
|
|
||||||
|
|
||||||
* [RASM Assembler](https://github.com/EdouardBERGE/rasm/) - A very fast Z80
|
|
||||||
assembler, that provides built-in support for **ZX0** and **ZX7**.
|
|
||||||
|
|
||||||
* [MSXlib](https://github.com/theNestruo/msx-msxlib) - A set of libraries to
|
|
||||||
create MSX videogame cartridges, that provides built-in support
|
|
||||||
for **ZX0**, **ZX1**, and **ZX7**.
|
|
||||||
|
|
||||||
* [coco-dev](https://github.com/jamieleecho/coco-dev) - A Docker development
|
|
||||||
environment to create Tandy Color Computer applications, that provides
|
|
||||||
built-in support for **ZX0**.
|
|
||||||
|
|
||||||
* [Gfx2Next](https://github.com/headkaze/Gfx2Next) - A graphics conversion
|
|
||||||
utility for ZX Spectrum Next development, that provides built-in support
|
|
||||||
for **ZX0**.
|
|
||||||
|
|
||||||
* [ConvImgCpc](https://github.com/DemoniakLudo/ConvImgCpc) - An image
|
|
||||||
conversion utility for Amstrad CPC development, that provides built-in support
|
|
||||||
for **ZX0** and **ZX1**.
|
|
||||||
|
|
||||||
* [Vortex2_Player_SJASM](https://github.com/andydansby/Vortex2_Player_SJASM_ver2_compress) -
|
|
||||||
A packaging utility to compile Vortex 2 music for a ZX Spectrum, that compresses
|
|
||||||
songs using **ZX0**.
|
|
||||||
|
|
||||||
Projects using **ZX0**:
|
|
||||||
|
|
||||||
* [Bitfire](https://github.com/bboxy/bitfire) - A disk image loader/generator
|
|
||||||
for Commodore 64, that stores all compressed data using a modified version
|
|
||||||
of **ZX0**.
|
|
||||||
|
|
||||||
* [Defender CoCo 3](http://www.lcurtisboyle.com/nitros9/defender.html) - A
|
|
||||||
conversion of the official Williams Defender game from the arcades for the
|
|
||||||
Tandy Color Computer 3 that stores all compressed data using **ZX0** to fit
|
|
||||||
on two 160K floppy disks.
|
|
||||||
|
|
||||||
* [NSID_Emu](https://spectrumcomputing.co.uk/forums/viewtopic.php?f=8&t=2786) -
|
|
||||||
A SID Player for ZX Spectrum that stores all compressed data using **ZX0**.
|
|
||||||
|
|
||||||
* [ZX Interface 2 Cartridges](http://www.fruitcake.plus.com/Sinclair/Interface2/Cartridges/Interface2_RC_New_3rdParty_GameConversions.htm) -
|
|
||||||
Several ZX Interface 2 conversions were created using either **ZX0** or **ZX7**
|
|
||||||
so a full game could fit into a small 16K cartridge.
|
|
||||||
|
|
||||||
* [Joust CoCo 3](http://www.lcurtisboyle.com/nitros9/joust.html) - A port of
|
|
||||||
arcade game Joust for the Tandy Color Computer 3, that stores all compressed
|
|
||||||
data using **ZX0** to fit on a single 160K floppy disk.
|
|
||||||
|
|
||||||
* [Sonic GX](http://norecess.cpcscene.net/) - A remake of video game Sonic the
|
|
||||||
Hedgehog for the GX-4000, that stores all compressed data using **ZX0**.
|
|
||||||
|
|
||||||
* [Rit and Tam](http://www.indieretronews.com/2021/02/rit-and-tam-arcade-classic-rodland-is.html) -
|
|
||||||
A remake of platform game Rodland for the Amstrad, that stores all compressed
|
|
||||||
data using **ZX0**.
|
|
||||||
|
|
||||||
* [others](https://spectrumcomputing.co.uk/entry/36245/ZX-Spectrum/ZX0) -
|
|
||||||
A list of Sinclair-related programs using **ZX0** is available at **Spectrum Computing**.
|
|
||||||
|
|
||||||
Related projects (by the same author):
|
|
||||||
|
|
||||||
* [RCS](https://github.com/einar-saukas/RCS) - Use **ZX0** and **RCS** together
|
|
||||||
to improve compression of ZX Spectrum screens.
|
|
||||||
|
|
||||||
* [ZX0](https://github.com/einar-saukas/ZX0) - The official **ZX0** repository.
|
|
||||||
|
|
||||||
* [ZX1](https://github.com/einar-saukas/ZX1) - A simpler but faster version
|
|
||||||
of **ZX0**, that sacrifices about 1.5% compression to run about 15% faster.
|
|
||||||
|
|
||||||
* [ZX2](https://github.com/einar-saukas/ZX2) - A minimalist version of **ZX1**,
|
|
||||||
intended for compressing very small files.
|
|
||||||
|
|
||||||
* [ZX5](https://github.com/einar-saukas/ZX5) - An experimental, more complex
|
|
||||||
compressor based on **ZX0**.
|
|
||||||
|
|
||||||
* [ZX7](https://spectrumcomputing.co.uk/entry/27996/ZX-Spectrum/ZX7) - A widely
|
|
||||||
popular predecessor compressor (now superseded by **ZX0**).
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* (c) Copyright 2021 by Einar Saukas. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * The name of its author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "zx0.h"
|
|
||||||
|
|
||||||
unsigned char* output_data;
|
|
||||||
int output_index;
|
|
||||||
int input_index;
|
|
||||||
int bit_index;
|
|
||||||
int bit_mask;
|
|
||||||
int diff;
|
|
||||||
int backtrack;
|
|
||||||
|
|
||||||
void read_bytes(int n, int *delta) {
|
|
||||||
input_index += n;
|
|
||||||
diff += n;
|
|
||||||
if (*delta < diff)
|
|
||||||
*delta = diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_byte(int value) {
|
|
||||||
output_data[output_index++] = value;
|
|
||||||
diff--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_bit(int value) {
|
|
||||||
if (backtrack) {
|
|
||||||
if (value)
|
|
||||||
output_data[output_index-1] |= 1;
|
|
||||||
backtrack = FALSE;
|
|
||||||
} else {
|
|
||||||
if (!bit_mask) {
|
|
||||||
bit_mask = 128;
|
|
||||||
bit_index = output_index;
|
|
||||||
write_byte(0);
|
|
||||||
}
|
|
||||||
if (value)
|
|
||||||
output_data[bit_index] |= bit_mask;
|
|
||||||
bit_mask >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_interlaced_elias_gamma(int value, int backwards_mode, int invert_mode) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 2; i <= value; i <<= 1)
|
|
||||||
;
|
|
||||||
i >>= 1;
|
|
||||||
while (i >>= 1) {
|
|
||||||
write_bit(backwards_mode);
|
|
||||||
write_bit(invert_mode ? !(value & i) : (value & i));
|
|
||||||
}
|
|
||||||
write_bit(!backwards_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *compress(BLOCK *optimal, unsigned char *input_data, int input_size, int skip, int backwards_mode, int invert_mode, int *output_size, int *delta) {
|
|
||||||
BLOCK *prev;
|
|
||||||
BLOCK *next;
|
|
||||||
int last_offset = INITIAL_OFFSET;
|
|
||||||
int length;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* calculate and allocate output buffer */
|
|
||||||
*output_size = (optimal->bits+25)/8;
|
|
||||||
output_data = (unsigned char *)malloc(*output_size);
|
|
||||||
if (!output_data) {
|
|
||||||
fprintf(stderr, "Error: Insufficient memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* un-reverse optimal sequence */
|
|
||||||
prev = NULL;
|
|
||||||
while (optimal) {
|
|
||||||
next = optimal->chain;
|
|
||||||
optimal->chain = prev;
|
|
||||||
prev = optimal;
|
|
||||||
optimal = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize data */
|
|
||||||
diff = *output_size-input_size+skip;
|
|
||||||
*delta = 0;
|
|
||||||
input_index = skip;
|
|
||||||
output_index = 0;
|
|
||||||
bit_mask = 0;
|
|
||||||
backtrack = TRUE;
|
|
||||||
|
|
||||||
/* generate output */
|
|
||||||
for (optimal = prev->chain; optimal; prev=optimal, optimal = optimal->chain) {
|
|
||||||
length = optimal->index-prev->index;
|
|
||||||
|
|
||||||
if (!optimal->offset) {
|
|
||||||
/* copy literals indicator */
|
|
||||||
write_bit(0);
|
|
||||||
|
|
||||||
/* copy literals length */
|
|
||||||
write_interlaced_elias_gamma(length, backwards_mode, FALSE);
|
|
||||||
|
|
||||||
/* copy literals values */
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
write_byte(input_data[input_index]);
|
|
||||||
read_bytes(1, delta);
|
|
||||||
}
|
|
||||||
} else if (optimal->offset == last_offset) {
|
|
||||||
/* copy from last offset indicator */
|
|
||||||
write_bit(0);
|
|
||||||
|
|
||||||
/* copy from last offset length */
|
|
||||||
write_interlaced_elias_gamma(length, backwards_mode, FALSE);
|
|
||||||
read_bytes(length, delta);
|
|
||||||
} else {
|
|
||||||
/* copy from new offset indicator */
|
|
||||||
write_bit(1);
|
|
||||||
|
|
||||||
/* copy from new offset MSB */
|
|
||||||
write_interlaced_elias_gamma((optimal->offset-1)/128+1, backwards_mode, invert_mode);
|
|
||||||
|
|
||||||
/* copy from new offset LSB */
|
|
||||||
if (backwards_mode)
|
|
||||||
write_byte(((optimal->offset-1)%128)<<1);
|
|
||||||
else
|
|
||||||
write_byte((127-(optimal->offset-1)%128)<<1);
|
|
||||||
|
|
||||||
/* copy from new offset length */
|
|
||||||
backtrack = TRUE;
|
|
||||||
write_interlaced_elias_gamma(length-1, backwards_mode, FALSE);
|
|
||||||
read_bytes(length, delta);
|
|
||||||
|
|
||||||
last_offset = optimal->offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* end marker */
|
|
||||||
write_bit(1);
|
|
||||||
write_interlaced_elias_gamma(256, backwards_mode, invert_mode);
|
|
||||||
|
|
||||||
/* done! */
|
|
||||||
return output_data;
|
|
||||||
}
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* ZX0 decompressor - by Einar Saukas
|
|
||||||
* https://github.com/einar-saukas/ZX0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define BUFFER_SIZE 65536 /* must be > MAX_OFFSET */
|
|
||||||
#define INITIAL_OFFSET 1
|
|
||||||
|
|
||||||
#define FALSE 0
|
|
||||||
#define TRUE 1
|
|
||||||
|
|
||||||
FILE *ifp;
|
|
||||||
FILE *ofp;
|
|
||||||
char *input_name;
|
|
||||||
char *output_name;
|
|
||||||
unsigned char *input_data;
|
|
||||||
unsigned char *output_data;
|
|
||||||
size_t input_index;
|
|
||||||
size_t output_index;
|
|
||||||
size_t input_size;
|
|
||||||
size_t output_size;
|
|
||||||
size_t partial_counter;
|
|
||||||
int bit_mask;
|
|
||||||
int bit_value;
|
|
||||||
int backtrack;
|
|
||||||
int last_byte;
|
|
||||||
|
|
||||||
int read_byte() {
|
|
||||||
if (input_index == partial_counter) {
|
|
||||||
input_index = 0;
|
|
||||||
partial_counter = fread(input_data, sizeof(char), BUFFER_SIZE, ifp);
|
|
||||||
input_size += partial_counter;
|
|
||||||
if (partial_counter == 0) {
|
|
||||||
fprintf(stderr, (input_size ? "Error: Truncated input file %s\n" : "Error: Empty input file %s\n"), input_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
last_byte = input_data[input_index++];
|
|
||||||
return last_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_bit() {
|
|
||||||
if (backtrack) {
|
|
||||||
backtrack = FALSE;
|
|
||||||
return last_byte & 1;
|
|
||||||
}
|
|
||||||
bit_mask >>= 1;
|
|
||||||
if (bit_mask == 0) {
|
|
||||||
bit_mask = 128;
|
|
||||||
bit_value = read_byte();
|
|
||||||
}
|
|
||||||
return bit_value & bit_mask ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_interlaced_elias_gamma(int inverted) {
|
|
||||||
int value = 1;
|
|
||||||
while (!read_bit()) {
|
|
||||||
value = value << 1 | read_bit() ^ inverted;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void save_output() {
|
|
||||||
if (output_index != 0) {
|
|
||||||
if (fwrite(output_data, sizeof(char), output_index, ofp) != output_index) {
|
|
||||||
fprintf(stderr, "Error: Cannot write output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
output_size += output_index;
|
|
||||||
output_index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_byte(int value) {
|
|
||||||
output_data[output_index++] = value;
|
|
||||||
if (output_index == BUFFER_SIZE) {
|
|
||||||
save_output();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_bytes(int offset, int length) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (offset > output_size+output_index) {
|
|
||||||
fprintf(stderr, "Error: Invalid data in input file %s\n", input_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
while (length-- > 0) {
|
|
||||||
i = output_index-offset;
|
|
||||||
write_byte(output_data[i >= 0 ? i : BUFFER_SIZE+i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void decompress(int classic_mode) {
|
|
||||||
int last_offset = INITIAL_OFFSET;
|
|
||||||
int length;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
input_data = (unsigned char *)malloc(BUFFER_SIZE);
|
|
||||||
output_data = (unsigned char *)malloc(BUFFER_SIZE);
|
|
||||||
if (!input_data || !output_data) {
|
|
||||||
fprintf(stderr, "Error: Insufficient memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
input_size = 0;
|
|
||||||
input_index = 0;
|
|
||||||
partial_counter = 0;
|
|
||||||
output_index = 0;
|
|
||||||
output_size = 0;
|
|
||||||
bit_mask = 0;
|
|
||||||
backtrack = FALSE;
|
|
||||||
|
|
||||||
COPY_LITERALS:
|
|
||||||
length = read_interlaced_elias_gamma(FALSE);
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
write_byte(read_byte());
|
|
||||||
if (read_bit())
|
|
||||||
goto COPY_FROM_NEW_OFFSET;
|
|
||||||
|
|
||||||
/*COPY_FROM_LAST_OFFSET:*/
|
|
||||||
length = read_interlaced_elias_gamma(FALSE);
|
|
||||||
write_bytes(last_offset, length);
|
|
||||||
if (!read_bit())
|
|
||||||
goto COPY_LITERALS;
|
|
||||||
|
|
||||||
COPY_FROM_NEW_OFFSET:
|
|
||||||
last_offset = read_interlaced_elias_gamma(!classic_mode);
|
|
||||||
if (last_offset == 256) {
|
|
||||||
save_output();
|
|
||||||
if (input_index != partial_counter) {
|
|
||||||
fprintf(stderr, "Error: Input file %s too long\n", input_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
last_offset = last_offset*128-(read_byte()>>1);
|
|
||||||
backtrack = TRUE;
|
|
||||||
length = read_interlaced_elias_gamma(FALSE)+1;
|
|
||||||
write_bytes(last_offset, length);
|
|
||||||
if (read_bit())
|
|
||||||
goto COPY_FROM_NEW_OFFSET;
|
|
||||||
else
|
|
||||||
goto COPY_LITERALS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
int forced_mode = FALSE;
|
|
||||||
int classic_mode = FALSE;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
printf("DZX0 v2.2: Data decompressor by Einar Saukas\n");
|
|
||||||
|
|
||||||
/* process hidden optional parameters */
|
|
||||||
for (i = 1; i < argc && *argv[i] == '-'; i++) {
|
|
||||||
if (!strcmp(argv[i], "-f")) {
|
|
||||||
forced_mode = TRUE;
|
|
||||||
} else if (!strcmp(argv[i], "-c")) {
|
|
||||||
classic_mode = TRUE;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error: Invalid parameter %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* determine output filename */
|
|
||||||
if (argc == i+1) {
|
|
||||||
input_name = argv[i];
|
|
||||||
input_size = strlen(input_name);
|
|
||||||
if (input_size > 4 && !strcmp(input_name+input_size-4, ".zx0")) {
|
|
||||||
input_size = strlen(input_name);
|
|
||||||
output_name = (char *)malloc(input_size);
|
|
||||||
strcpy(output_name, input_name);
|
|
||||||
output_name[input_size-4] = '\0';
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error: Cannot infer output filename\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else if (argc == i+2) {
|
|
||||||
input_name = argv[i];
|
|
||||||
output_name = argv[i+1];
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Usage: %s [-f] [-c] input.zx0 [output]\n"
|
|
||||||
" -f Force overwrite of output file\n"
|
|
||||||
" -c Classic file format (v1.*)\n", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* open input file */
|
|
||||||
ifp = fopen(input_name, "rb");
|
|
||||||
if (!ifp) {
|
|
||||||
fprintf(stderr, "Error: Cannot access input file %s\n", input_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check output file */
|
|
||||||
if (!forced_mode && fopen(output_name, "rb") != NULL) {
|
|
||||||
fprintf(stderr, "Error: Already existing output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create output file */
|
|
||||||
ofp = fopen(output_name, "wb");
|
|
||||||
if (!ofp) {
|
|
||||||
fprintf(stderr, "Error: Cannot create output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* generate output file */
|
|
||||||
decompress(classic_mode);
|
|
||||||
|
|
||||||
/* close input file */
|
|
||||||
fclose(ifp);
|
|
||||||
|
|
||||||
/* close output file */
|
|
||||||
fclose(ofp);
|
|
||||||
|
|
||||||
/* done! */
|
|
||||||
printf("File decompressed from %lu to %lu bytes!\n", (unsigned long)input_size, (unsigned long)output_size);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
/*
|
|
||||||
* (c) Copyright 2021 by Einar Saukas. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * The name of its author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "zx0.h"
|
|
||||||
|
|
||||||
#define QTY_BLOCKS 10000
|
|
||||||
|
|
||||||
BLOCK *ghost_root = NULL;
|
|
||||||
BLOCK *dead_array = NULL;
|
|
||||||
int dead_array_size = 0;
|
|
||||||
|
|
||||||
BLOCK *allocate(int bits, int index, int offset, BLOCK *chain) {
|
|
||||||
BLOCK *ptr;
|
|
||||||
|
|
||||||
if (ghost_root) {
|
|
||||||
ptr = ghost_root;
|
|
||||||
ghost_root = ptr->ghost_chain;
|
|
||||||
if (ptr->chain && !--ptr->chain->references) {
|
|
||||||
ptr->chain->ghost_chain = ghost_root;
|
|
||||||
ghost_root = ptr->chain;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!dead_array_size) {
|
|
||||||
dead_array = (BLOCK *)malloc(QTY_BLOCKS*sizeof(BLOCK));
|
|
||||||
if (!dead_array) {
|
|
||||||
fprintf(stderr, "Error: Insufficient memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
dead_array_size = QTY_BLOCKS;
|
|
||||||
}
|
|
||||||
ptr = &dead_array[--dead_array_size];
|
|
||||||
}
|
|
||||||
ptr->bits = bits;
|
|
||||||
ptr->index = index;
|
|
||||||
ptr->offset = offset;
|
|
||||||
if (chain)
|
|
||||||
chain->references++;
|
|
||||||
ptr->chain = chain;
|
|
||||||
ptr->references = 0;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void assign(BLOCK **ptr, BLOCK *chain) {
|
|
||||||
chain->references++;
|
|
||||||
if (*ptr && !--(*ptr)->references) {
|
|
||||||
(*ptr)->ghost_chain = ghost_root;
|
|
||||||
ghost_root = *ptr;
|
|
||||||
}
|
|
||||||
*ptr = chain;
|
|
||||||
}
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* (c) Copyright 2021 by Einar Saukas. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * The name of its author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "zx0.h"
|
|
||||||
|
|
||||||
#define MAX_SCALE 50
|
|
||||||
|
|
||||||
int offset_ceiling(int index, int offset_limit) {
|
|
||||||
return index > offset_limit ? offset_limit : index < INITIAL_OFFSET ? INITIAL_OFFSET : index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int elias_gamma_bits(int value) {
|
|
||||||
int bits = 1;
|
|
||||||
while (value >>= 1)
|
|
||||||
bits += 2;
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLOCK* optimize(unsigned char *input_data, int input_size, int skip, int offset_limit) {
|
|
||||||
BLOCK **last_literal;
|
|
||||||
BLOCK **last_match;
|
|
||||||
BLOCK **optimal;
|
|
||||||
int* match_length;
|
|
||||||
int* best_length;
|
|
||||||
int best_length_size;
|
|
||||||
int bits;
|
|
||||||
int index;
|
|
||||||
int offset;
|
|
||||||
int length;
|
|
||||||
int bits2;
|
|
||||||
int dots = 2;
|
|
||||||
int max_offset = offset_ceiling(input_size-1, offset_limit);
|
|
||||||
|
|
||||||
/* allocate all main data structures at once */
|
|
||||||
last_literal = (BLOCK **)calloc(max_offset+1, sizeof(BLOCK *));
|
|
||||||
last_match = (BLOCK **)calloc(max_offset+1, sizeof(BLOCK *));
|
|
||||||
optimal = (BLOCK **)calloc(input_size, sizeof(BLOCK *));
|
|
||||||
match_length = (int *)calloc(max_offset+1, sizeof(int));
|
|
||||||
best_length = (int *)malloc(input_size*sizeof(int));
|
|
||||||
if (!last_literal || !last_match || !optimal || !match_length || !best_length) {
|
|
||||||
fprintf(stderr, "Error: Insufficient memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (input_size > 2)
|
|
||||||
best_length[2] = 2;
|
|
||||||
|
|
||||||
/* start with fake block */
|
|
||||||
assign(&last_match[INITIAL_OFFSET], allocate(-1, skip-1, INITIAL_OFFSET, NULL));
|
|
||||||
|
|
||||||
printf("[");
|
|
||||||
|
|
||||||
/* process remaining bytes */
|
|
||||||
for (index = skip; index < input_size; index++) {
|
|
||||||
best_length_size = 2;
|
|
||||||
max_offset = offset_ceiling(index, offset_limit);
|
|
||||||
for (offset = 1; offset <= max_offset; offset++) {
|
|
||||||
if (index != skip && index >= offset && input_data[index] == input_data[index-offset]) {
|
|
||||||
/* copy from last offset */
|
|
||||||
if (last_literal[offset]) {
|
|
||||||
length = index-last_literal[offset]->index;
|
|
||||||
bits = last_literal[offset]->bits + 1 + elias_gamma_bits(length);
|
|
||||||
assign(&last_match[offset], allocate(bits, index, offset, last_literal[offset]));
|
|
||||||
if (!optimal[index] || optimal[index]->bits > bits)
|
|
||||||
assign(&optimal[index], last_match[offset]);
|
|
||||||
}
|
|
||||||
/* copy from new offset */
|
|
||||||
if (++match_length[offset] > 1) {
|
|
||||||
if (best_length_size < match_length[offset]) {
|
|
||||||
bits = optimal[index-best_length[best_length_size]]->bits + elias_gamma_bits(best_length[best_length_size]-1);
|
|
||||||
do {
|
|
||||||
best_length_size++;
|
|
||||||
bits2 = optimal[index-best_length_size]->bits + elias_gamma_bits(best_length_size-1);
|
|
||||||
if (bits2 <= bits) {
|
|
||||||
best_length[best_length_size] = best_length_size;
|
|
||||||
bits = bits2;
|
|
||||||
} else {
|
|
||||||
best_length[best_length_size] = best_length[best_length_size-1];
|
|
||||||
}
|
|
||||||
} while(best_length_size < match_length[offset]);
|
|
||||||
}
|
|
||||||
length = best_length[match_length[offset]];
|
|
||||||
bits = optimal[index-length]->bits + 8 + elias_gamma_bits((offset-1)/128+1) + elias_gamma_bits(length-1);
|
|
||||||
if (!last_match[offset] || last_match[offset]->index != index || last_match[offset]->bits > bits) {
|
|
||||||
assign(&last_match[offset], allocate(bits, index, offset, optimal[index-length]));
|
|
||||||
if (!optimal[index] || optimal[index]->bits > bits)
|
|
||||||
assign(&optimal[index], last_match[offset]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* copy literals */
|
|
||||||
match_length[offset] = 0;
|
|
||||||
if (last_match[offset]) {
|
|
||||||
length = index-last_match[offset]->index;
|
|
||||||
bits = last_match[offset]->bits + 1 + elias_gamma_bits(length) + length*8;
|
|
||||||
assign(&last_literal[offset], allocate(bits, index, 0, last_match[offset]));
|
|
||||||
if (!optimal[index] || optimal[index]->bits > bits)
|
|
||||||
assign(&optimal[index], last_literal[offset]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* indicate progress */
|
|
||||||
if (index*MAX_SCALE/input_size > dots) {
|
|
||||||
printf(".");
|
|
||||||
fflush(stdout);
|
|
||||||
dots++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("]\n");
|
|
||||||
|
|
||||||
return optimal[input_size-1];
|
|
||||||
}
|
|
||||||
@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
* (c) Copyright 2021 by Einar Saukas. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * The name of its author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "zx0.h"
|
|
||||||
|
|
||||||
#define MAX_OFFSET_ZX0 32640
|
|
||||||
#define MAX_OFFSET_ZX7 2176
|
|
||||||
|
|
||||||
void reverse(unsigned char *first, unsigned char *last) {
|
|
||||||
unsigned char c;
|
|
||||||
|
|
||||||
while (first < last) {
|
|
||||||
c = *first;
|
|
||||||
*first++ = *last;
|
|
||||||
*last-- = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
int skip = 0;
|
|
||||||
int forced_mode = FALSE;
|
|
||||||
int quick_mode = FALSE;
|
|
||||||
int backwards_mode = FALSE;
|
|
||||||
int classic_mode = FALSE;
|
|
||||||
char *output_name;
|
|
||||||
unsigned char *input_data;
|
|
||||||
unsigned char *output_data;
|
|
||||||
FILE *ifp;
|
|
||||||
FILE *ofp;
|
|
||||||
int input_size;
|
|
||||||
int output_size;
|
|
||||||
int partial_counter;
|
|
||||||
int total_counter;
|
|
||||||
int delta;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
printf("ZX0 v2.2: Optimal data compressor by Einar Saukas\n");
|
|
||||||
|
|
||||||
/* process optional parameters */
|
|
||||||
for (i = 1; i < argc && (*argv[i] == '-' || *argv[i] == '+'); i++) {
|
|
||||||
if (!strcmp(argv[i], "-f")) {
|
|
||||||
forced_mode = TRUE;
|
|
||||||
} else if (!strcmp(argv[i], "-c")) {
|
|
||||||
classic_mode = TRUE;
|
|
||||||
} else if (!strcmp(argv[i], "-b")) {
|
|
||||||
backwards_mode = TRUE;
|
|
||||||
} else if (!strcmp(argv[i], "-q")) {
|
|
||||||
quick_mode = TRUE;
|
|
||||||
} else if ((skip = atoi(argv[i])) <= 0) {
|
|
||||||
fprintf(stderr, "Error: Invalid parameter %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* determine output filename */
|
|
||||||
if (argc == i+1) {
|
|
||||||
output_name = (char *)malloc(strlen(argv[i])+5);
|
|
||||||
strcpy(output_name, argv[i]);
|
|
||||||
strcat(output_name, ".zx0");
|
|
||||||
} else if (argc == i+2) {
|
|
||||||
output_name = argv[i+1];
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Usage: %s [-f] [-c] [-b] [-q] input [output.zx0]\n"
|
|
||||||
" -f Force overwrite of output file\n"
|
|
||||||
" -c Classic file format (v1.*)\n"
|
|
||||||
" -b Compress backwards\n"
|
|
||||||
" -q Quick non-optimal compression\n", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* open input file */
|
|
||||||
ifp = fopen(argv[i], "rb");
|
|
||||||
if (!ifp) {
|
|
||||||
fprintf(stderr, "Error: Cannot access input file %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* determine input size */
|
|
||||||
fseek(ifp, 0L, SEEK_END);
|
|
||||||
input_size = ftell(ifp);
|
|
||||||
fseek(ifp, 0L, SEEK_SET);
|
|
||||||
if (!input_size) {
|
|
||||||
fprintf(stderr, "Error: Empty input file %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* validate skip against input size */
|
|
||||||
if (skip >= input_size) {
|
|
||||||
fprintf(stderr, "Error: Skipping entire input file %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate input buffer */
|
|
||||||
input_data = (unsigned char *)malloc(input_size);
|
|
||||||
if (!input_data) {
|
|
||||||
fprintf(stderr, "Error: Insufficient memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read input file */
|
|
||||||
total_counter = 0;
|
|
||||||
do {
|
|
||||||
partial_counter = fread(input_data+total_counter, sizeof(char), input_size-total_counter, ifp);
|
|
||||||
total_counter += partial_counter;
|
|
||||||
} while (partial_counter > 0);
|
|
||||||
|
|
||||||
if (total_counter != input_size) {
|
|
||||||
fprintf(stderr, "Error: Cannot read input file %s\n", argv[i]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close input file */
|
|
||||||
fclose(ifp);
|
|
||||||
|
|
||||||
/* check output file */
|
|
||||||
if (!forced_mode && fopen(output_name, "rb") != NULL) {
|
|
||||||
fprintf(stderr, "Error: Already existing output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* create output file */
|
|
||||||
ofp = fopen(output_name, "wb");
|
|
||||||
if (!ofp) {
|
|
||||||
fprintf(stderr, "Error: Cannot create output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* conditionally reverse input file */
|
|
||||||
if (backwards_mode)
|
|
||||||
reverse(input_data, input_data+input_size-1);
|
|
||||||
|
|
||||||
/* generate output file */
|
|
||||||
output_data = compress(optimize(input_data, input_size, skip, quick_mode ? MAX_OFFSET_ZX7 : MAX_OFFSET_ZX0), input_data, input_size, skip, backwards_mode, !classic_mode && !backwards_mode, &output_size, &delta);
|
|
||||||
|
|
||||||
/* conditionally reverse output file */
|
|
||||||
if (backwards_mode)
|
|
||||||
reverse(output_data, output_data+output_size-1);
|
|
||||||
|
|
||||||
/* write output file */
|
|
||||||
if (fwrite(output_data, sizeof(char), output_size, ofp) != output_size) {
|
|
||||||
fprintf(stderr, "Error: Cannot write output file %s\n", output_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* close output file */
|
|
||||||
fclose(ofp);
|
|
||||||
|
|
||||||
/* done! */
|
|
||||||
printf("File%s compressed%s from %d to %d bytes! (delta %d)\n", (skip ? " partially" : ""), (backwards_mode ? " backwards" : ""), input_size-skip, output_size, delta);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* (c) Copyright 2021 by Einar Saukas. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * The name of its author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define INITIAL_OFFSET 1
|
|
||||||
|
|
||||||
#define FALSE 0
|
|
||||||
#define TRUE 1
|
|
||||||
|
|
||||||
typedef struct block_t {
|
|
||||||
struct block_t *chain;
|
|
||||||
struct block_t *ghost_chain;
|
|
||||||
int bits;
|
|
||||||
int index;
|
|
||||||
int offset;
|
|
||||||
int references;
|
|
||||||
} BLOCK;
|
|
||||||
|
|
||||||
BLOCK *allocate(int bits, int index, int offset, BLOCK *chain);
|
|
||||||
|
|
||||||
void assign(BLOCK **ptr, BLOCK *chain);
|
|
||||||
|
|
||||||
BLOCK *optimize(unsigned char *input_data, int input_size, int skip, int offset_limit);
|
|
||||||
|
|
||||||
unsigned char *compress(BLOCK *optimal, unsigned char *input_data, int input_size, int skip, int backwards_mode, int invert_mode, int *output_size, int *delta);
|
|
||||||
@ -1,250 +0,0 @@
|
|||||||
;
|
|
||||||
; Speed-optimized ZX0 decompressor by spke (191 bytes) - OLD FILE FORMAT v1
|
|
||||||
;
|
|
||||||
; ver.00 by spke (27/01-23/03/2021, 191 bytes)
|
|
||||||
; ver.01 by spke (24/03/2021, 193(+2) bytes - fixed a bug in the initialization)
|
|
||||||
; ver.01patch2 by uniabis (25/03/2021, 191(-2) bytes - fixed a bug with elias over 8bits)
|
|
||||||
; ver.01patch5 by uniabis (29/03/2021, 191 bytes - a bit faster)
|
|
||||||
;
|
|
||||||
; Original ZX0 decompressors were written by Einar Saukas
|
|
||||||
;
|
|
||||||
; This decompressor was written on the basis of "Standard" decompressor by
|
|
||||||
; Einar Saukas and optimized for speed by spke. This decompressor is
|
|
||||||
; about 5% faster than the "Turbo" decompressor, which is 128 bytes long.
|
|
||||||
; It has about the same speed as the 412 bytes version of the "Mega" decompressor.
|
|
||||||
;
|
|
||||||
; The decompressor uses AF, AF', BC, DE, HL and IX and relies upon self-modified code.
|
|
||||||
;
|
|
||||||
; The decompression is done in the standard way:
|
|
||||||
;
|
|
||||||
; ld hl,FirstByteOfCompressedData
|
|
||||||
; ld de,FirstByteOfMemoryForDecompressedData
|
|
||||||
; call DecompressZX0
|
|
||||||
;
|
|
||||||
; Of course, ZX0 compression algorithms are (c) 2021 Einar Saukas,
|
|
||||||
; see https://github.com/einar-saukas/ZX0 for more information
|
|
||||||
;
|
|
||||||
; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com
|
|
||||||
;
|
|
||||||
; 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 source distribution.
|
|
||||||
|
|
||||||
DecompressZX0:
|
|
||||||
scf
|
|
||||||
ex af, af'
|
|
||||||
ld ix, CopyMatch1
|
|
||||||
ld bc, $ffff
|
|
||||||
ld (PrevOffset+1), bc ; default offset is -1
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
jr RunOfLiterals ; BC is assumed to contains 0 most of the time
|
|
||||||
|
|
||||||
; 7-bit offsets allow additional optimizations, based on the facts that C==0 and AF' has C ON!
|
|
||||||
ShorterOffsets:
|
|
||||||
ex af, af'
|
|
||||||
sbc a, a
|
|
||||||
ld (PrevOffset+2), a ; the top byte of the offset is always $FF
|
|
||||||
ld a, (hl)
|
|
||||||
inc hl
|
|
||||||
rra
|
|
||||||
ld (PrevOffset+1), a ; note that AF' always has flag C ON
|
|
||||||
jr nc, LongerMatch
|
|
||||||
|
|
||||||
CopyMatch2: ; the case of matches with len=2
|
|
||||||
ex af, af'
|
|
||||||
ld c, 2
|
|
||||||
|
|
||||||
; the faster match copying code
|
|
||||||
CopyMatch1:
|
|
||||||
push hl ; preserve source
|
|
||||||
|
|
||||||
PrevOffset:
|
|
||||||
ld hl, $ffff ; restore offset (default offset is -1)
|
|
||||||
add hl, de ; HL = dest - offset
|
|
||||||
ldir
|
|
||||||
pop hl ; restore source
|
|
||||||
|
|
||||||
; after a match you can have either
|
|
||||||
; 0 + <elias length> = run of literals, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
AfterMatch1:
|
|
||||||
add a, a
|
|
||||||
jr nc, RunOfLiterals
|
|
||||||
|
|
||||||
UsualMatch: ; this is the case of usual match+offset
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerOffets
|
|
||||||
jr nz, ShorterOffsets ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, ShorterOffsets
|
|
||||||
|
|
||||||
LongerOffets:
|
|
||||||
inc c
|
|
||||||
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
call z, ReloadReadGamma
|
|
||||||
|
|
||||||
ProcessOffset:
|
|
||||||
ex af, af'
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; end-of-data marker (only checked for longer offsets)
|
|
||||||
rra
|
|
||||||
ld (PrevOffset+2),a
|
|
||||||
ld a, (hl)
|
|
||||||
inc hl
|
|
||||||
rra
|
|
||||||
ld (PrevOffset+1), a
|
|
||||||
|
|
||||||
; lowest bit is the first bit of the gamma code for length
|
|
||||||
jr c, CopyMatch2
|
|
||||||
|
|
||||||
; this wastes 1 t-state for longer matches far away,
|
|
||||||
; but saves 4 t-states for longer nearby (seems to pay off in testing)
|
|
||||||
ld c, b
|
|
||||||
LongerMatch:
|
|
||||||
inc c
|
|
||||||
; doing SCF here ensures that AF' has flag C ON and costs
|
|
||||||
; cheaper than doing SCF in the ShortestOffsets branch
|
|
||||||
scf
|
|
||||||
ex af, af'
|
|
||||||
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
call z,ReloadReadGamma
|
|
||||||
|
|
||||||
CopyMatch3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (PrevOffset+1) ; restore offset
|
|
||||||
add hl, de ; HL = dest - offset
|
|
||||||
|
|
||||||
; because BC>=3-1, we can do 2 x LDI safely
|
|
||||||
ldi
|
|
||||||
ldir
|
|
||||||
inc c
|
|
||||||
ldi
|
|
||||||
pop hl ; restore source
|
|
||||||
|
|
||||||
; after a match you can have either
|
|
||||||
; 0 + <elias length> = run of literals, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
AfterMatch3:
|
|
||||||
add a, a
|
|
||||||
jr c, UsualMatch
|
|
||||||
|
|
||||||
RunOfLiterals:
|
|
||||||
inc c
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerRun
|
|
||||||
jr nz, CopyLiteral ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, CopyLiteral
|
|
||||||
|
|
||||||
LongerRun:
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
jr nz, CopyLiterals
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
call nc, ReadGammaAligned
|
|
||||||
|
|
||||||
CopyLiterals:
|
|
||||||
ldi
|
|
||||||
|
|
||||||
CopyLiteral:
|
|
||||||
ldir
|
|
||||||
|
|
||||||
; after a literal run you can have either
|
|
||||||
; 0 + <elias length> = match using a repeated offset, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
add a, a
|
|
||||||
jr c, UsualMatch
|
|
||||||
|
|
||||||
RepMatch:
|
|
||||||
inc c
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerRepMatch
|
|
||||||
jr nz, CopyMatch1 ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, CopyMatch1
|
|
||||||
|
|
||||||
LongerRepMatch:
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
jp nz, CopyMatch1
|
|
||||||
|
|
||||||
; this is a crafty equivalent of CALL ReloadReadGamma : JP CopyMatch1
|
|
||||||
push ix
|
|
||||||
|
|
||||||
; the subroutine for reading the remainder of the partly read Elias gamma code.
|
|
||||||
; it has two entry points: ReloadReadGamma first refills the bit reservoir in A,
|
|
||||||
; while ReadGammaAligned assumes that the bit reservoir has just been refilled.
|
|
||||||
ReloadReadGamma:
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
ret c
|
|
||||||
ReadGammaAligned:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
|
|
||||||
ReadingLongGamma: ; this loop does not need unrolling, as it does not get much use anyway
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jr nz, ReadingLongGamma
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr ReadingLongGamma
|
|
||||||
@ -1,464 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas
|
|
||||||
; "Mega" version (681 bytes, 28% faster) - OLD FILE FORMAT v1
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_mega:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
ld (dzx0m_last_offset+1), bc
|
|
||||||
inc bc
|
|
||||||
jr dzx0m_literals0
|
|
||||||
|
|
||||||
dzx0m_new_offset6:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
dzx0m_elias_offset1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset7
|
|
||||||
dzx0m_new_offset7:
|
|
||||||
ex af, af' ; adjust for negative offset
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length7 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length3
|
|
||||||
dzx0m_elias_length3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length1
|
|
||||||
dzx0m_length1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset0
|
|
||||||
dzx0m_literals0:
|
|
||||||
inc c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
dzx0m_elias_literals3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals1
|
|
||||||
dzx0m_literals1:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset0
|
|
||||||
inc c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
dzx0m_elias_reuse3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse1
|
|
||||||
dzx0m_reuse1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals0
|
|
||||||
|
|
||||||
dzx0m_new_offset0:
|
|
||||||
inc c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
dzx0m_elias_offset3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset1
|
|
||||||
dzx0m_new_offset1:
|
|
||||||
ex af, af' ; adjust for negative offset
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length1 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length5
|
|
||||||
dzx0m_elias_length5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length3
|
|
||||||
dzx0m_length3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset2
|
|
||||||
dzx0m_literals2:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
dzx0m_elias_literals5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals3
|
|
||||||
dzx0m_literals3:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset2
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
dzx0m_elias_reuse5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse3
|
|
||||||
dzx0m_reuse3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals2
|
|
||||||
|
|
||||||
dzx0m_new_offset2:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
dzx0m_elias_offset5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset3
|
|
||||||
dzx0m_new_offset3:
|
|
||||||
ex af, af' ; adjust for negative offset
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length3 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length7
|
|
||||||
dzx0m_elias_length7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length5
|
|
||||||
dzx0m_length5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset4
|
|
||||||
dzx0m_literals4:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
dzx0m_elias_literals7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals5
|
|
||||||
dzx0m_literals5:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset4
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
dzx0m_elias_reuse7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse5
|
|
||||||
dzx0m_reuse5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals4
|
|
||||||
|
|
||||||
dzx0m_new_offset4:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
dzx0m_elias_offset7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset5
|
|
||||||
dzx0m_new_offset5:
|
|
||||||
ex af, af' ; adjust for negative offset
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length5 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length1
|
|
||||||
dzx0m_elias_length1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length7
|
|
||||||
dzx0m_length7:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jp c, dzx0m_new_offset6
|
|
||||||
dzx0m_literals6:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
dzx0m_elias_literals1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals7
|
|
||||||
dzx0m_literals7:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jp c, dzx0m_new_offset6
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
dzx0m_elias_reuse1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse7
|
|
||||||
dzx0m_reuse7:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0m_last_offset:
|
|
||||||
ld hl, 0
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals6
|
|
||||||
|
|
||||||
jp dzx0m_new_offset6
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas
|
|
||||||
; "Standard" version (69 bytes only) - OLD FILE FORMAT v1
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_standard:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
push bc
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
dzx0s_literals:
|
|
||||||
call dzx0s_elias ; obtain length
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0s_new_offset
|
|
||||||
call dzx0s_elias ; obtain length
|
|
||||||
dzx0s_copy:
|
|
||||||
ex (sp), hl ; preserve source, restore offset
|
|
||||||
push hl ; preserve offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore offset
|
|
||||||
ex (sp), hl ; preserve offset, restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0s_literals
|
|
||||||
dzx0s_new_offset:
|
|
||||||
call dzx0s_elias ; obtain offset MSB
|
|
||||||
ex af, af'
|
|
||||||
pop af ; discard last offset
|
|
||||||
xor a ; adjust for negative offset
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
push bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call nc, dzx0s_elias_backtrack
|
|
||||||
inc bc
|
|
||||||
jr dzx0s_copy
|
|
||||||
dzx0s_elias:
|
|
||||||
inc c ; interlaced Elias gamma coding
|
|
||||||
dzx0s_elias_loop:
|
|
||||||
add a, a
|
|
||||||
jr nz, dzx0s_elias_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0s_elias_skip:
|
|
||||||
ret c
|
|
||||||
dzx0s_elias_backtrack:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
jr dzx0s_elias_loop
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas & introspec
|
|
||||||
; "Turbo" version (128 bytes, 21% faster) - OLD FILE FORMAT v1
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_turbo:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
ld (dzx0t_last_offset+1), bc
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
jr dzx0t_literals
|
|
||||||
dzx0t_new_offset:
|
|
||||||
inc c ; obtain offset MSB
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_new_offset_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_new_offset_skip:
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
ex af, af' ; adjust for negative offset
|
|
||||||
xor a
|
|
||||||
sub c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, a
|
|
||||||
ex af, af'
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0t_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
inc bc
|
|
||||||
dzx0t_copy:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0t_last_offset:
|
|
||||||
ld hl, 0 ; restore offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0t_new_offset
|
|
||||||
dzx0t_literals:
|
|
||||||
inc c ; obtain length
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_literals_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_literals_skip:
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0t_new_offset
|
|
||||||
inc c ; obtain length
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_last_offset_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_last_offset_skip:
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
jp dzx0t_copy
|
|
||||||
dzx0t_elias:
|
|
||||||
add a, a ; interlaced Elias gamma coding
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, dzx0t_elias
|
|
||||||
ret nz
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
dzx0t_elias_loop:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jr nc, dzx0t_elias_loop
|
|
||||||
ret nz
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
jr nc, dzx0t_elias_loop
|
|
||||||
ret
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,235 +0,0 @@
|
|||||||
;
|
|
||||||
; Speed-optimized ZX0 decompressor by spke (187 bytes)
|
|
||||||
;
|
|
||||||
; ver.00 by spke (27/01-23/03/2021, 191 bytes)
|
|
||||||
; ver.01 by spke (24/03/2021, 193(+2) bytes - fixed a bug in the initialization)
|
|
||||||
; ver.01patch2 by uniabis (25/03/2021, 191(-2) bytes - fixed a bug with elias over 8bits)
|
|
||||||
; ver.01patch9 by uniabis (10/09/2021, 187(-4) bytes - support for new v2 format)
|
|
||||||
;
|
|
||||||
; Original ZX0 decompressors were written by Einar Saukas
|
|
||||||
;
|
|
||||||
; This decompressor was written on the basis of "Standard" decompressor by
|
|
||||||
; Einar Saukas and optimized for speed by spke. This decompressor is
|
|
||||||
; about 5% faster than the "Turbo" decompressor, which is 128 bytes long.
|
|
||||||
; It has about the same speed as the 412 bytes version of the "Mega" decompressor.
|
|
||||||
;
|
|
||||||
; The decompressor uses AF, BC, DE, HL and IX and relies upon self-modified code.
|
|
||||||
;
|
|
||||||
; The decompression is done in the standard way:
|
|
||||||
;
|
|
||||||
; ld hl,FirstByteOfCompressedData
|
|
||||||
; ld de,FirstByteOfMemoryForDecompressedData
|
|
||||||
; call DecompressZX0
|
|
||||||
;
|
|
||||||
; Of course, ZX0 compression algorithms are (c) 2021 Einar Saukas,
|
|
||||||
; see https://github.com/einar-saukas/ZX0 for more information
|
|
||||||
;
|
|
||||||
; Drop me an email if you have any comments/ideas/suggestions: zxintrospec@gmail.com
|
|
||||||
;
|
|
||||||
; 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 source distribution.
|
|
||||||
|
|
||||||
DecompressZX0:
|
|
||||||
|
|
||||||
ld ix, CopyMatch1
|
|
||||||
ld bc, $ffff
|
|
||||||
ld (PrevOffset+1), bc ; default offset is -1
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
jr RunOfLiterals ; BC is assumed to contains 0 most of the time
|
|
||||||
|
|
||||||
ShorterOffsets:
|
|
||||||
ld b, $ff ; the top byte of the offset is always $FF
|
|
||||||
ld c, (hl)
|
|
||||||
inc hl
|
|
||||||
rr c
|
|
||||||
ld (PrevOffset+1), bc
|
|
||||||
jr nc, LongerMatch
|
|
||||||
|
|
||||||
CopyMatch2: ; the case of matches with len=2
|
|
||||||
ld bc, 2
|
|
||||||
|
|
||||||
; the faster match copying code
|
|
||||||
CopyMatch1:
|
|
||||||
push hl ; preserve source
|
|
||||||
|
|
||||||
PrevOffset:
|
|
||||||
ld hl, $ffff ; restore offset (default offset is -1)
|
|
||||||
add hl, de ; HL = dest - offset
|
|
||||||
ldir
|
|
||||||
pop hl ; restore source
|
|
||||||
|
|
||||||
; after a match you can have either
|
|
||||||
; 0 + <elias length> = run of literals, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
AfterMatch1:
|
|
||||||
add a, a
|
|
||||||
jr nc, RunOfLiterals
|
|
||||||
|
|
||||||
UsualMatch: ; this is the case of usual match+offset
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerOffets
|
|
||||||
jr nz, ShorterOffsets ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, ShorterOffsets
|
|
||||||
|
|
||||||
LongerOffets:
|
|
||||||
ld c, $fe
|
|
||||||
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
call z, ReloadReadGamma
|
|
||||||
|
|
||||||
ProcessOffset:
|
|
||||||
|
|
||||||
inc c
|
|
||||||
ret z ; end-of-data marker (only checked for longer offsets)
|
|
||||||
rr c
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl)
|
|
||||||
inc hl
|
|
||||||
rr c
|
|
||||||
ld (PrevOffset+1), bc
|
|
||||||
|
|
||||||
; lowest bit is the first bit of the gamma code for length
|
|
||||||
jr c, CopyMatch2
|
|
||||||
|
|
||||||
LongerMatch:
|
|
||||||
ld bc, 1
|
|
||||||
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
call z,ReloadReadGamma
|
|
||||||
|
|
||||||
CopyMatch3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (PrevOffset+1) ; restore offset
|
|
||||||
add hl, de ; HL = dest - offset
|
|
||||||
|
|
||||||
; because BC>=3-1, we can do 2 x LDI safely
|
|
||||||
ldi
|
|
||||||
ldir
|
|
||||||
inc c
|
|
||||||
ldi
|
|
||||||
pop hl ; restore source
|
|
||||||
|
|
||||||
; after a match you can have either
|
|
||||||
; 0 + <elias length> = run of literals, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
AfterMatch3:
|
|
||||||
add a, a
|
|
||||||
jr c, UsualMatch
|
|
||||||
|
|
||||||
RunOfLiterals:
|
|
||||||
inc c
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerRun
|
|
||||||
jr nz, CopyLiteral ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, CopyLiteral
|
|
||||||
|
|
||||||
LongerRun:
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
jr nz, CopyLiterals
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
call nc, ReadGammaAligned
|
|
||||||
|
|
||||||
CopyLiterals:
|
|
||||||
ldi
|
|
||||||
|
|
||||||
CopyLiteral:
|
|
||||||
ldir
|
|
||||||
|
|
||||||
; after a literal run you can have either
|
|
||||||
; 0 + <elias length> = match using a repeated offset, or
|
|
||||||
; 1 + <elias offset msb> + [7-bits of offset lsb + 1-bit of length] + <elias length> = another match
|
|
||||||
add a, a
|
|
||||||
jr c, UsualMatch
|
|
||||||
|
|
||||||
RepMatch:
|
|
||||||
inc c
|
|
||||||
add a, a
|
|
||||||
jr nc, LongerRepMatch
|
|
||||||
jr nz, CopyMatch1 ; NZ after NC == "confirmed C"
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
jr c, CopyMatch1
|
|
||||||
|
|
||||||
LongerRepMatch:
|
|
||||||
add a, a ; inline read gamma
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, $-4
|
|
||||||
|
|
||||||
jp nz, CopyMatch1
|
|
||||||
|
|
||||||
; this is a crafty equivalent of CALL ReloadReadGamma : JP CopyMatch1
|
|
||||||
push ix
|
|
||||||
|
|
||||||
; the subroutine for reading the remainder of the partly read Elias gamma code.
|
|
||||||
; it has two entry points: ReloadReadGamma first refills the bit reservoir in A,
|
|
||||||
; while ReadGammaAligned assumes that the bit reservoir has just been refilled.
|
|
||||||
ReloadReadGamma:
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
|
|
||||||
ret c
|
|
||||||
ReadGammaAligned:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ReadingLongGamma: ; this loop does not need unrolling, as it does not get much use anyway
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jr nz, ReadingLongGamma
|
|
||||||
|
|
||||||
ld a, (hl) ; reload bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
jr ReadingLongGamma
|
|
||||||
@ -1,452 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas
|
|
||||||
; "Mega" version (673 bytes, 28% faster)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_mega:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
ld (dzx0m_last_offset+1), bc
|
|
||||||
inc bc
|
|
||||||
jr dzx0m_literals0
|
|
||||||
|
|
||||||
dzx0m_new_offset6:
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
dzx0m_elias_offset1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset7
|
|
||||||
dzx0m_new_offset7:
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length7 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length3
|
|
||||||
dzx0m_elias_length3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length1
|
|
||||||
dzx0m_length1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset0
|
|
||||||
dzx0m_literals0:
|
|
||||||
inc c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
dzx0m_elias_literals3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals1
|
|
||||||
dzx0m_literals1:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset0
|
|
||||||
inc c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
dzx0m_elias_reuse3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse1
|
|
||||||
dzx0m_reuse1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals0
|
|
||||||
|
|
||||||
dzx0m_new_offset0:
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
dzx0m_elias_offset3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset1
|
|
||||||
dzx0m_new_offset1:
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length1 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length5
|
|
||||||
dzx0m_elias_length5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length3
|
|
||||||
dzx0m_length3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset2
|
|
||||||
dzx0m_literals2:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
dzx0m_elias_literals5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals3
|
|
||||||
dzx0m_literals3:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset2
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
dzx0m_elias_reuse5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse3
|
|
||||||
dzx0m_reuse3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals2
|
|
||||||
|
|
||||||
dzx0m_new_offset2:
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset5
|
|
||||||
dzx0m_elias_offset5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset3
|
|
||||||
dzx0m_new_offset3:
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length3 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length7
|
|
||||||
dzx0m_elias_length7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length5
|
|
||||||
dzx0m_length5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0m_new_offset4
|
|
||||||
dzx0m_literals4:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals7
|
|
||||||
dzx0m_elias_literals7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals5
|
|
||||||
dzx0m_literals5:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0m_new_offset4
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse7
|
|
||||||
dzx0m_elias_reuse7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse5
|
|
||||||
dzx0m_reuse5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals4
|
|
||||||
|
|
||||||
dzx0m_new_offset4:
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp c, dzx0m_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_new_offset7
|
|
||||||
dzx0m_elias_offset7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_offset5
|
|
||||||
dzx0m_new_offset5:
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0m_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp c, dzx0m_length5 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_length1
|
|
||||||
dzx0m_elias_length1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_length7
|
|
||||||
dzx0m_length7:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0m_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldi ; copy one more from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jp c, dzx0m_new_offset6
|
|
||||||
dzx0m_literals6:
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_literals1
|
|
||||||
dzx0m_elias_literals1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_literals7
|
|
||||||
dzx0m_literals7:
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jp c, dzx0m_new_offset6
|
|
||||||
inc c
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp c, dzx0m_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0m_reuse1
|
|
||||||
dzx0m_elias_reuse1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0m_elias_reuse7
|
|
||||||
dzx0m_reuse7:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0m_last_offset:
|
|
||||||
ld hl, 0
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0m_literals6
|
|
||||||
|
|
||||||
jp dzx0m_new_offset6
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,459 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas & introspec
|
|
||||||
; "Mega" version (676 bytes, 28% faster) - BACKWARDS VARIANT
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: last source address (compressed data)
|
|
||||||
; DE: last destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_mega_back:
|
|
||||||
ld bc, 1 ; preserve default offset 1
|
|
||||||
ld (dzx0mb_last_offset+1), bc
|
|
||||||
jr dzx0mb_literals0
|
|
||||||
|
|
||||||
dzx0mb_new_offset6:
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp nc, dzx0mb_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset1
|
|
||||||
dzx0mb_elias_offset1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_offset7
|
|
||||||
dzx0mb_new_offset7:
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
ld (dzx0mb_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp nc, dzx0mb_length7 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length3
|
|
||||||
dzx0mb_elias_length3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_length1
|
|
||||||
dzx0mb_length1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldd ; copy one more from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0mb_new_offset0
|
|
||||||
dzx0mb_literals0:
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals3
|
|
||||||
dzx0mb_elias_literals3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_literals1
|
|
||||||
dzx0mb_literals1:
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0mb_new_offset0
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse3
|
|
||||||
dzx0mb_elias_reuse3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_reuse1
|
|
||||||
dzx0mb_reuse1:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0mb_literals0
|
|
||||||
|
|
||||||
dzx0mb_new_offset0:
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp nc, dzx0mb_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset3
|
|
||||||
dzx0mb_elias_offset3:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_offset1
|
|
||||||
dzx0mb_new_offset1:
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
ld (dzx0mb_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp nc, dzx0mb_length1 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length5
|
|
||||||
dzx0mb_elias_length5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_length3
|
|
||||||
dzx0mb_length3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldd ; copy one more from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0mb_new_offset2
|
|
||||||
dzx0mb_literals2:
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals5
|
|
||||||
dzx0mb_elias_literals5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_literals3
|
|
||||||
dzx0mb_literals3:
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0mb_new_offset2
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse5
|
|
||||||
dzx0mb_elias_reuse5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_reuse3
|
|
||||||
dzx0mb_reuse3:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0mb_literals2
|
|
||||||
|
|
||||||
dzx0mb_new_offset2:
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp nc, dzx0mb_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset7
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset5
|
|
||||||
dzx0mb_elias_offset5:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_offset3
|
|
||||||
dzx0mb_new_offset3:
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
ld (dzx0mb_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp nc, dzx0mb_length3 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length7
|
|
||||||
dzx0mb_elias_length7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_length5
|
|
||||||
dzx0mb_length5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldd ; copy one more from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0mb_new_offset4
|
|
||||||
dzx0mb_literals4:
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals7
|
|
||||||
dzx0mb_elias_literals7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_literals5
|
|
||||||
dzx0mb_literals5:
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0mb_new_offset4
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse7
|
|
||||||
dzx0mb_elias_reuse7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_reuse5
|
|
||||||
dzx0mb_reuse5:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0mb_literals4
|
|
||||||
|
|
||||||
dzx0mb_new_offset4:
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
jp nc, dzx0mb_new_offset3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset1
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_new_offset7
|
|
||||||
dzx0mb_elias_offset7:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_offset5
|
|
||||||
dzx0mb_new_offset5:
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
ld (dzx0mb_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1
|
|
||||||
jp nc, dzx0mb_length5 ; obtain length
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_length1
|
|
||||||
dzx0mb_elias_length1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_length7
|
|
||||||
dzx0mb_length7:
|
|
||||||
push hl ; preserve source
|
|
||||||
ld hl, (dzx0mb_last_offset+1)
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
ldd ; copy one more from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jp c, dzx0mb_new_offset6
|
|
||||||
dzx0mb_literals6:
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_literals5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_literals1
|
|
||||||
dzx0mb_elias_literals1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_literals7
|
|
||||||
dzx0mb_literals7:
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jp c, dzx0mb_new_offset6
|
|
||||||
add a, a ; obtain length
|
|
||||||
jp nc, dzx0mb_reuse5
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse3
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jp nc, dzx0mb_reuse1
|
|
||||||
dzx0mb_elias_reuse1:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
add a, a
|
|
||||||
jp c, dzx0mb_elias_reuse7
|
|
||||||
dzx0mb_reuse7:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0mb_last_offset:
|
|
||||||
ld hl, 0
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0mb_literals6
|
|
||||||
|
|
||||||
jp dzx0mb_new_offset6
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas & Urusergi
|
|
||||||
; "Standard" version (68 bytes only)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_standard:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
push bc
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
dzx0s_literals:
|
|
||||||
call dzx0s_elias ; obtain length
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0s_new_offset
|
|
||||||
call dzx0s_elias ; obtain length
|
|
||||||
dzx0s_copy:
|
|
||||||
ex (sp), hl ; preserve source, restore offset
|
|
||||||
push hl ; preserve offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore offset
|
|
||||||
ex (sp), hl ; preserve offset, restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0s_literals
|
|
||||||
dzx0s_new_offset:
|
|
||||||
pop bc ; discard last offset
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
call dzx0s_elias_loop ; obtain offset MSB
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
push bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call nc, dzx0s_elias_backtrack
|
|
||||||
inc bc
|
|
||||||
jr dzx0s_copy
|
|
||||||
dzx0s_elias:
|
|
||||||
inc c ; interlaced Elias gamma coding
|
|
||||||
dzx0s_elias_loop:
|
|
||||||
add a, a
|
|
||||||
jr nz, dzx0s_elias_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0s_elias_skip:
|
|
||||||
ret c
|
|
||||||
dzx0s_elias_backtrack:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
jr dzx0s_elias_loop
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas
|
|
||||||
; "Standard" version (69 bytes only) - BACKWARDS VARIANT
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: last source address (compressed data)
|
|
||||||
; DE: last destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_standard_back:
|
|
||||||
ld bc, 1 ; preserve default offset 1
|
|
||||||
push bc
|
|
||||||
ld a, $80
|
|
||||||
dzx0sb_literals:
|
|
||||||
call dzx0sb_elias ; obtain length
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0sb_new_offset
|
|
||||||
call dzx0sb_elias ; obtain length
|
|
||||||
dzx0sb_copy:
|
|
||||||
ex (sp), hl ; preserve source, restore offset
|
|
||||||
push hl ; preserve offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore offset
|
|
||||||
ex (sp), hl ; preserve offset, restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr nc, dzx0sb_literals
|
|
||||||
dzx0sb_new_offset:
|
|
||||||
inc sp ; discard last offset
|
|
||||||
inc sp
|
|
||||||
call dzx0sb_elias ; obtain offset MSB
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
push bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call c, dzx0sb_elias_backtrack
|
|
||||||
inc bc
|
|
||||||
jr dzx0sb_copy
|
|
||||||
dzx0sb_elias_backtrack:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
dzx0sb_elias:
|
|
||||||
add a, a ; inverted interlaced Elias gamma coding
|
|
||||||
jr nz, dzx0sb_elias_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
rla
|
|
||||||
dzx0sb_elias_skip:
|
|
||||||
jr c, dzx0sb_elias_backtrack
|
|
||||||
ret
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas & introspec
|
|
||||||
; "Turbo" version (126 bytes, 21% faster)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: source address (compressed data)
|
|
||||||
; DE: destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_turbo:
|
|
||||||
ld bc, $ffff ; preserve default offset 1
|
|
||||||
ld (dzx0t_last_offset+1), bc
|
|
||||||
inc bc
|
|
||||||
ld a, $80
|
|
||||||
jr dzx0t_literals
|
|
||||||
dzx0t_new_offset:
|
|
||||||
ld c, $fe ; prepare negative offset
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_new_offset_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_new_offset_skip:
|
|
||||||
call nc, dzx0t_elias ; obtain offset MSB
|
|
||||||
inc c
|
|
||||||
ret z ; check end marker
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
inc hl
|
|
||||||
rr b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
ld (dzx0t_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
inc bc
|
|
||||||
dzx0t_copy:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0t_last_offset:
|
|
||||||
ld hl, 0 ; restore offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
ldir ; copy from offset
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0t_new_offset
|
|
||||||
dzx0t_literals:
|
|
||||||
inc c ; obtain length
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_literals_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_literals_skip:
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
ldir ; copy literals
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0t_new_offset
|
|
||||||
inc c ; obtain length
|
|
||||||
add a, a
|
|
||||||
jp nz, dzx0t_last_offset_skip
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
dzx0t_last_offset_skip:
|
|
||||||
call nc, dzx0t_elias
|
|
||||||
jp dzx0t_copy
|
|
||||||
dzx0t_elias:
|
|
||||||
add a, a ; interlaced Elias gamma coding
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
jr nc, dzx0t_elias
|
|
||||||
ret nz
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret c
|
|
||||||
dzx0t_elias_loop:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jr nc, dzx0t_elias_loop
|
|
||||||
ret nz
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
inc hl
|
|
||||||
rla
|
|
||||||
jr nc, dzx0t_elias_loop
|
|
||||||
ret
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
; -----------------------------------------------------------------------------
|
|
||||||
; ZX0 decoder by Einar Saukas & introspec
|
|
||||||
; "Turbo" version (126 bytes, 21% faster) - BACKWARDS VARIANT
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
; Parameters:
|
|
||||||
; HL: last source address (compressed data)
|
|
||||||
; DE: last destination address (decompressing)
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
dzx0_turbo_back:
|
|
||||||
ld bc, 1 ; preserve default offset 1
|
|
||||||
ld (dzx0tb_last_offset+1), bc
|
|
||||||
ld a, $80
|
|
||||||
jr dzx0tb_literals
|
|
||||||
dzx0tb_new_offset:
|
|
||||||
add a, a ; obtain offset MSB
|
|
||||||
call c, dzx0tb_elias
|
|
||||||
dec b
|
|
||||||
ret z ; check end marker
|
|
||||||
dec c ; adjust for positive offset
|
|
||||||
ld b, c
|
|
||||||
ld c, (hl) ; obtain offset LSB
|
|
||||||
dec hl
|
|
||||||
srl b ; last offset bit becomes first length bit
|
|
||||||
rr c
|
|
||||||
inc bc
|
|
||||||
ld (dzx0tb_last_offset+1), bc ; preserve new offset
|
|
||||||
ld bc, 1 ; obtain length
|
|
||||||
call c, dzx0tb_elias_loop
|
|
||||||
inc bc
|
|
||||||
dzx0tb_copy:
|
|
||||||
push hl ; preserve source
|
|
||||||
dzx0tb_last_offset:
|
|
||||||
ld hl, 0 ; restore offset
|
|
||||||
add hl, de ; calculate destination - offset
|
|
||||||
lddr ; copy from offset
|
|
||||||
inc c
|
|
||||||
pop hl ; restore source
|
|
||||||
add a, a ; copy from literals or new offset?
|
|
||||||
jr c, dzx0tb_new_offset
|
|
||||||
dzx0tb_literals:
|
|
||||||
add a, a ; obtain length
|
|
||||||
call c, dzx0tb_elias
|
|
||||||
lddr ; copy literals
|
|
||||||
inc c
|
|
||||||
add a, a ; copy from last offset or new offset?
|
|
||||||
jr c, dzx0tb_new_offset
|
|
||||||
add a, a ; obtain length
|
|
||||||
call c, dzx0tb_elias
|
|
||||||
jp dzx0tb_copy
|
|
||||||
dzx0tb_elias_loop:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
dzx0tb_elias:
|
|
||||||
jp nz, dzx0tb_elias_loop ; inverted interlaced Elias gamma coding
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
rla
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
dzx0tb_elias_reload:
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
ld a, (hl) ; load another group of 8 bits
|
|
||||||
dec hl
|
|
||||||
rla
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
ret nc
|
|
||||||
add a, a
|
|
||||||
rl c
|
|
||||||
rl b
|
|
||||||
add a, a
|
|
||||||
jr c, dzx0tb_elias_reload
|
|
||||||
ret
|
|
||||||
; -----------------------------------------------------------------------------
|
|
||||||
Loading…
Reference in New Issue
Block a user