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