mirror of
https://github.com/MikhaelKaa/zx_cartridge.git
synced 2026-03-16 14:37:57 +03:00
Merge branch 'main' of https://github.com/MikhaelKaa/zx_cartridge
This commit is contained in:
commit
21e0db2a10
12
.gitignore
vendored
12
.gitignore
vendored
@ -12,5 +12,15 @@
|
|||||||
/HW/History
|
/HW/History
|
||||||
|
|
||||||
/FW/zx_cartrige_description.txt
|
/FW/zx_cartrige_description.txt
|
||||||
|
/Content/build
|
||||||
|
/Content/tools/zx0/build
|
||||||
|
/Content/Batty/batty.sna
|
||||||
|
/Content/Batty/loader.asm
|
||||||
|
/Content/Batty/loader0.bin
|
||||||
|
/Content/Batty/loader1.bin
|
||||||
|
/Content/Batty/main.asm
|
||||||
|
/Content/Batty/main.bin
|
||||||
|
/Content/Batty/main.bin.zx0
|
||||||
|
/Content/Batty/screen.scr
|
||||||
|
/Content/Batty/screen.scr.zx0
|
||||||
/HW/Project Outputs for zx_cartridge
|
/HW/Project Outputs for zx_cartridge
|
||||||
Content/tools/zx0/build/*
|
|
||||||
|
|||||||
BIN
Content/Batty/b48v.bin
Normal file
BIN
Content/Batty/b48v.bin
Normal file
Binary file not shown.
BIN
Content/Batty/batty.tap
Normal file
BIN
Content/Batty/batty.tap
Normal file
Binary file not shown.
24
Content/Batty/debuild.sh
Normal file
24
Content/Batty/debuild.sh
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/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
|
||||||
20
Content/Makefile
Normal file
20
Content/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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:
|
||||||
91
Content/main.asm
Normal file
91
Content/main.asm
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
; 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
|
||||||
31
Content/ram_part.asm
Normal file
31
Content/ram_part.asm
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
; 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
|
||||||
48
Content/test.asm
Normal file
48
Content/test.asm
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
; Простейший тест переключения банков для карика
|
||||||
|
; Банк 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
|
||||||
29
Content/tools/zx0/LICENSE
Normal file
29
Content/tools/zx0/LICENSE
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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.
|
||||||
24
Content/tools/zx0/Makefile
Normal file
24
Content/tools/zx0/Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 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
|
||||||
476
Content/tools/zx0/README.md
Normal file
476
Content/tools/zx0/README.md
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
# 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**).
|
||||||
164
Content/tools/zx0/src/compress.c
Normal file
164
Content/tools/zx0/src/compress.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* (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;
|
||||||
|
}
|
||||||
226
Content/tools/zx0/src/dzx0.c
Normal file
226
Content/tools/zx0/src/dzx0.c
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
75
Content/tools/zx0/src/memory.c
Normal file
75
Content/tools/zx0/src/memory.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* (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;
|
||||||
|
}
|
||||||
138
Content/tools/zx0/src/optimize.c
Normal file
138
Content/tools/zx0/src/optimize.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* (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];
|
||||||
|
}
|
||||||
177
Content/tools/zx0/src/zx0.c
Normal file
177
Content/tools/zx0/src/zx0.c
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* (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;
|
||||||
|
}
|
||||||
46
Content/tools/zx0/src/zx0.h
Normal file
46
Content/tools/zx0/src/zx0.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* (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);
|
||||||
250
Content/tools/zx0/z80/OLD_V1/dzx0_fast_CLASSIC.asm
Normal file
250
Content/tools/zx0/z80/OLD_V1/dzx0_fast_CLASSIC.asm
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
;
|
||||||
|
; 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
|
||||||
464
Content/tools/zx0/z80/OLD_V1/dzx0_mega_CLASSIC.asm
Normal file
464
Content/tools/zx0/z80/OLD_V1/dzx0_mega_CLASSIC.asm
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
63
Content/tools/zx0/z80/OLD_V1/dzx0_standard_CLASSIC.asm
Normal file
63
Content/tools/zx0/z80/OLD_V1/dzx0_standard_CLASSIC.asm
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
103
Content/tools/zx0/z80/OLD_V1/dzx0_turbo_CLASSIC.asm
Normal file
103
Content/tools/zx0/z80/OLD_V1/dzx0_turbo_CLASSIC.asm
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
235
Content/tools/zx0/z80/dzx0_fast.asm
Normal file
235
Content/tools/zx0/z80/dzx0_fast.asm
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
;
|
||||||
|
; 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
|
||||||
452
Content/tools/zx0/z80/dzx0_mega.asm
Normal file
452
Content/tools/zx0/z80/dzx0_mega.asm
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
459
Content/tools/zx0/z80/dzx0_mega_back.asm
Normal file
459
Content/tools/zx0/z80/dzx0_mega_back.asm
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
61
Content/tools/zx0/z80/dzx0_standard.asm
Normal file
61
Content/tools/zx0/z80/dzx0_standard.asm
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
62
Content/tools/zx0/z80/dzx0_standard_back.asm
Normal file
62
Content/tools/zx0/z80/dzx0_standard_back.asm
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
100
Content/tools/zx0/z80/dzx0_turbo.asm
Normal file
100
Content/tools/zx0/z80/dzx0_turbo.asm
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
99
Content/tools/zx0/z80/dzx0_turbo_back.asm
Normal file
99
Content/tools/zx0/z80/dzx0_turbo_back.asm
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
; 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
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
BIN
Docs/sch_22022026.pdf
Normal file
BIN
Docs/sch_22022026.pdf
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
# Для тестирования модуля неодходимо чтобы тест назывался %имя_модуля%_tb
|
# Для тестирования модуля неодходимо чтобы тест назывался %имя_модуля%_tb
|
||||||
|
|
||||||
TARGET ?= zx_cartrige
|
TARGET ?= zx_cartridge
|
||||||
ICARUS = iverilog
|
ICARUS = iverilog
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|||||||
142
FW/src/zx_cartridge.v
Normal file
142
FW/src/zx_cartridge.v
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
// Модуль картриджа для ZX Spectrum
|
||||||
|
// 19.02.2026 Mikhael Kaa
|
||||||
|
//
|
||||||
|
// Аппаратная часть:
|
||||||
|
// - 4 микросхем AM29F040 (по 512 КБ) -> всего 2 МБ = 256 страницы по 8 КБ.
|
||||||
|
// - Адресные линии CPU A0..A12 подключены напрямую ко всем микросхемам ПЗУ.
|
||||||
|
// - Старшие линии адреса A13..A18 формируются внутри этого модуля.
|
||||||
|
//
|
||||||
|
// Окно памяти 0x0000..0x3FFF (16 КБ) разделено на две 8‑килобайтные половины:
|
||||||
|
// - Нижняя половина (A13 = 0, 0x0000..0x1FFF) : всегда отображается на страницу 0 (BIOS картриджа).
|
||||||
|
// - Верхняя половина (A13 = 1, 0x2000..0x3FFF) : отображается на выбираемую страницу.
|
||||||
|
//
|
||||||
|
// Выбор страницы:
|
||||||
|
// 8‑битный номер страницы формируется как reg_bank[7:0].
|
||||||
|
// - биты [7:6] : выбор одной из четырех микросхем (0‑3).
|
||||||
|
// - биты [5:0] : выбор 8‑килобайтной страницы внутри выбранной микросхемы (0‑63).
|
||||||
|
// Таким образом, можно адресовать любую из 256 страниц.
|
||||||
|
//
|
||||||
|
// Регистр управления reg_ctl (8 бит):
|
||||||
|
// reg_ctl[7] : при установке в 1 отключает ПЗУ картриджа (все выходы пассивны).
|
||||||
|
// Остальные биты зарезервированы.
|
||||||
|
//
|
||||||
|
// Порты ввода‑вывода (запись/чтение, активный уровень низкий):
|
||||||
|
// bank : запись reg_bank (биты 7..0) – происходит, когда
|
||||||
|
// A15=1, A14=1, A13=0, A7=0, IORQ=0, WR=0. Порт 0xdf7f.
|
||||||
|
// control : запись регистра управления reg_ctl – происходит, когда
|
||||||
|
// A15=1, A14=0, A13=1, A7=0, IORQ=0, WR=0. Порт 0xbf7f.
|
||||||
|
// Чтение любого из этих портов возвращает значение соответствующего регистра.
|
||||||
|
//
|
||||||
|
// Выходы:
|
||||||
|
// ZX_ROM_blk – активный высокий уровень; блокирует внутреннее ПЗУ ZX Spectrum.
|
||||||
|
// CR_ROM_oe_n – выход разрешения выходов для всех микросхем ПЗУ (активный низкий).
|
||||||
|
// CR_ROM_A[5:0] – линии адреса A13..A18 для микросхем ПЗУ.
|
||||||
|
// Для нижнего окна (A13=0) на эту шину выставляется 0.
|
||||||
|
// Для верхнего окна (A13=1) на ней передаётся 6‑битное смещение страницы.
|
||||||
|
// CR_ROM_CS[3:0] – выбор микросхем (активный низкий). Одна линия становится низкой
|
||||||
|
// только при обращении к картриджу (cpu_use_rom, MREQ и RD активны,
|
||||||
|
// картридж не отключён) и совпадении выбранной микросхемы.
|
||||||
|
// В нижнем окне всегда выбирается микросхема 0.
|
||||||
|
//
|
||||||
|
// Сброс: почти все регистры асинхронно очищаются низким уровнем reset_n.
|
||||||
|
|
||||||
|
module zx_cartridge (
|
||||||
|
// Сброс
|
||||||
|
input reset_n,
|
||||||
|
// Управляющие сигналы CPU
|
||||||
|
input iorq_n,
|
||||||
|
input rd_n,
|
||||||
|
input wr_n,
|
||||||
|
input mreq_n,
|
||||||
|
// Часть адресной шины CPU
|
||||||
|
input A7,
|
||||||
|
input A13,
|
||||||
|
input A14,
|
||||||
|
input A15,
|
||||||
|
inout [7:0] D,
|
||||||
|
|
||||||
|
// Сигнал блокировки внутреннего ПЗУ ZX Spectrum
|
||||||
|
output ZX_ROM_blk,
|
||||||
|
// Выход разрешения для ПЗУ картриджа (активный низкий)
|
||||||
|
output CR_ROM_oe_n,
|
||||||
|
// Старшие биты адреса для ПЗУ (A13..A18)
|
||||||
|
output [5:0] CR_ROM_A,
|
||||||
|
// Выбор кристаллов для четырех ПЗУ 29040 (активный низкий)
|
||||||
|
output [3:0] CR_ROM_CS
|
||||||
|
);
|
||||||
|
|
||||||
|
// 8‑битный банковый регистр (хранит биты 7..0 номера страницы)
|
||||||
|
reg [7:0] reg_bank = 8'b0;
|
||||||
|
// 8‑битный регистр управления:
|
||||||
|
// reg_ctl[6:0] - доступны софтам после сброса
|
||||||
|
// reg_ctl[7] – отключение картриджа (1 = отключён)
|
||||||
|
reg [7:0] reg_ctl = 8'b0;
|
||||||
|
|
||||||
|
// В спектруме декодирование порта 7ffd идет по А1, А15 == 0.
|
||||||
|
// Декодирование портов ввода‑вывода картриджа
|
||||||
|
wire bank = iorq_n | A7 | A13 | ~A14 | ~A15; // A15=1, A14=1, A13=0, A7=0
|
||||||
|
wire control = iorq_n | A7 | ~A13 | A14 | ~A15; // A15=1, A14=0, A13=1, A7=0
|
||||||
|
|
||||||
|
// CPU обращается к области ПЗУ 0x0000..0x3FFF (A15=0, A14=0)
|
||||||
|
wire cpu_use_rom = ~(A14 | A15);
|
||||||
|
|
||||||
|
wire is_enable = reg_ctl[7];
|
||||||
|
|
||||||
|
|
||||||
|
wire write_bank = ~bank & ~wr_n;
|
||||||
|
always @(posedge write_bank or negedge reset_n) begin
|
||||||
|
if (!reset_n)
|
||||||
|
reg_bank <= 8'b0;
|
||||||
|
else
|
||||||
|
reg_bank <= D;
|
||||||
|
end
|
||||||
|
|
||||||
|
wire write_control = ~control & ~wr_n;
|
||||||
|
always @(posedge write_control or negedge reset_n) begin
|
||||||
|
if (!reset_n)
|
||||||
|
reg_ctl[7] <= 1'b0; // только бит отключения сбрасывается
|
||||||
|
else
|
||||||
|
reg_ctl <= D;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Чтение регистров обратно в CPU
|
||||||
|
assign D = (~bank & ~rd_n) ? reg_bank[7:0] :
|
||||||
|
(~control & ~rd_n) ? reg_ctl : 8'bz;
|
||||||
|
|
||||||
|
|
||||||
|
// Разделение на выбор микросхемы (2 бита) и смещение страницы (6 бит)
|
||||||
|
wire [1:0] chip_sel = reg_bank[7:6]; // какая из 4 микросхем (0..3)
|
||||||
|
wire [5:0] page_offs = reg_bank[5:0]; // смещение внутри микросхемы (0..63)
|
||||||
|
|
||||||
|
// Условие доступа к картриджу:
|
||||||
|
// CPU читает область ПЗУ, MREQ и RD активны, картридж не отключён
|
||||||
|
wire rom_access = cpu_use_rom & ~mreq_n & ~rd_n & ~is_enable;
|
||||||
|
|
||||||
|
// Сигнал разрешения выходов и блокировки ПЗУ
|
||||||
|
assign CR_ROM_oe_n = ~rom_access;
|
||||||
|
assign ZX_ROM_blk = rom_access;
|
||||||
|
|
||||||
|
// CR_ROM_A зависит от окна:
|
||||||
|
// нижнее окно (A13=0) : принудительный адрес 0 (страница 0)
|
||||||
|
// верхнее окно (A13=1) : используется смещение из регистра
|
||||||
|
assign CR_ROM_A = (A13 == 1'b0) ? 6'b0 : page_offs;
|
||||||
|
|
||||||
|
// Формирование сигналов выбора микросхем:
|
||||||
|
// Для нижнего окна всегда включается микросхема 0.
|
||||||
|
// Для верхнего окна включается микросхема, выбранная chip_sel.
|
||||||
|
// CS активен низким уровнем и активен только при rom_access = истина.
|
||||||
|
// CS активен (0) только при rom_access = 1 и выполнении условий:
|
||||||
|
// - для микросхемы 0: либо нижнее окно (A13=0), либо верхнее окно с chip_sel = 0
|
||||||
|
// - для микросхем 1..3: только верхнее окно (A13=1) и chip_sel равен номеру микросхемы
|
||||||
|
assign CR_ROM_CS[0] = ~( rom_access &
|
||||||
|
( (A13 == 1'b0) || // нижнее окно всегда выбирает чип 0
|
||||||
|
( (A13 == 1'b1) && (chip_sel == 2'd0) ) ) );
|
||||||
|
assign CR_ROM_CS[1] = ~( rom_access &
|
||||||
|
( (A13 == 1'b1) && (chip_sel == 2'd1) ) );
|
||||||
|
assign CR_ROM_CS[2] = ~( rom_access &
|
||||||
|
( (A13 == 1'b1) && (chip_sel == 2'd2) ) );
|
||||||
|
assign CR_ROM_CS[3] = ~( rom_access &
|
||||||
|
( (A13 == 1'b1) && (chip_sel == 2'd3) ) );
|
||||||
|
|
||||||
|
endmodule
|
||||||
263
FW/src/zx_cartridge_tb.v
Normal file
263
FW/src/zx_cartridge_tb.v
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module tb_zx_cartridge();
|
||||||
|
// Управляющие сигналы
|
||||||
|
reg reset_n;
|
||||||
|
reg iorq_n;
|
||||||
|
reg rd_n;
|
||||||
|
reg wr_n;
|
||||||
|
reg mreq_n;
|
||||||
|
|
||||||
|
// Полная адресная шина (16 бит)
|
||||||
|
reg [15:0] address;
|
||||||
|
|
||||||
|
// Подключение отдельных бит к DUT
|
||||||
|
wire A7 = address[7];
|
||||||
|
wire A13 = address[13];
|
||||||
|
wire A14 = address[14];
|
||||||
|
wire A15 = address[15];
|
||||||
|
|
||||||
|
// Шина данных (8 бит) – двунаправленная
|
||||||
|
wire [7:0] D;
|
||||||
|
reg [7:0] D_drive; // данные для записи от тестбенча
|
||||||
|
wire [7:0] D_sample; // данные, читаемые из DUT
|
||||||
|
assign D = (wr_n == 0) ? D_drive : 8'bz;
|
||||||
|
assign D_sample = D;
|
||||||
|
|
||||||
|
// Выходы DUT
|
||||||
|
wire ZX_ROM_blk;
|
||||||
|
wire CR_ROM_oe_n;
|
||||||
|
wire [5:0] CR_ROM_A;
|
||||||
|
wire [3:0] CR_ROM_CS; // теперь 4 бита
|
||||||
|
|
||||||
|
// Вспомогательная переменная для чтения портов
|
||||||
|
reg [7:0] dummy;
|
||||||
|
|
||||||
|
// Тестируемый модуль (новая версия)
|
||||||
|
zx_cartridge uut (
|
||||||
|
.reset_n(reset_n),
|
||||||
|
.iorq_n(iorq_n),
|
||||||
|
.rd_n(rd_n),
|
||||||
|
.wr_n(wr_n),
|
||||||
|
.mreq_n(mreq_n),
|
||||||
|
.A7(A7),
|
||||||
|
.A13(A13),
|
||||||
|
.A14(A14),
|
||||||
|
.A15(A15),
|
||||||
|
.D(D),
|
||||||
|
.ZX_ROM_blk(ZX_ROM_blk),
|
||||||
|
.CR_ROM_oe_n(CR_ROM_oe_n),
|
||||||
|
.CR_ROM_A(CR_ROM_A),
|
||||||
|
.CR_ROM_CS(CR_ROM_CS)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Задачи для моделирования циклов Z80
|
||||||
|
|
||||||
|
// Запись в порт ввода-вывода
|
||||||
|
task write_port(input [15:0] addr, input [7:0] data);
|
||||||
|
begin
|
||||||
|
address = addr;
|
||||||
|
D_drive = data;
|
||||||
|
#10;
|
||||||
|
iorq_n = 0;
|
||||||
|
wr_n = 0;
|
||||||
|
#20;
|
||||||
|
iorq_n = 1;
|
||||||
|
wr_n = 1;
|
||||||
|
#10;
|
||||||
|
D_drive = 8'bz;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// Чтение из порта ввода-вывода (возвращает прочитанные данные)
|
||||||
|
task read_port(input [15:0] addr, output [7:0] data);
|
||||||
|
begin
|
||||||
|
address = addr;
|
||||||
|
#10;
|
||||||
|
iorq_n = 0;
|
||||||
|
rd_n = 0;
|
||||||
|
#20;
|
||||||
|
data = D_sample;
|
||||||
|
iorq_n = 1;
|
||||||
|
rd_n = 1;
|
||||||
|
#10;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// Чтение из памяти с проверкой CR_ROM_A и CR_ROM_CS (новые сигналы)
|
||||||
|
task read_mem_check(input [15:0] addr, input [5:0] exp_A, input [3:0] exp_CS);
|
||||||
|
begin
|
||||||
|
address = addr;
|
||||||
|
#10;
|
||||||
|
mreq_n = 0;
|
||||||
|
rd_n = 0;
|
||||||
|
#10; // ждём стабилизации
|
||||||
|
check_equal(exp_A, CR_ROM_A, "CR_ROM_A during read");
|
||||||
|
check_equal(exp_CS, CR_ROM_CS, "CR_ROM_CS during read");
|
||||||
|
#10;
|
||||||
|
mreq_n = 1;
|
||||||
|
rd_n = 1;
|
||||||
|
#10;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// Чтение из памяти с проверкой CR_ROM_oe_n и ZX_ROM_blk
|
||||||
|
task read_mem_check_oe(input [15:0] addr, input exp_oe, input exp_blk);
|
||||||
|
begin
|
||||||
|
address = addr;
|
||||||
|
#10;
|
||||||
|
mreq_n = 0;
|
||||||
|
rd_n = 0;
|
||||||
|
#10;
|
||||||
|
check_equal(exp_oe, CR_ROM_oe_n, "CR_ROM_oe_n during read");
|
||||||
|
check_equal(exp_blk, ZX_ROM_blk, "ZX_ROM_blk during read");
|
||||||
|
#10;
|
||||||
|
mreq_n = 1;
|
||||||
|
rd_n = 1;
|
||||||
|
#10;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// Простое чтение из памяти (без проверки, для установки адреса)
|
||||||
|
task read_mem(input [15:0] addr);
|
||||||
|
begin
|
||||||
|
address = addr;
|
||||||
|
#10;
|
||||||
|
mreq_n = 0;
|
||||||
|
rd_n = 0;
|
||||||
|
#20;
|
||||||
|
mreq_n = 1;
|
||||||
|
rd_n = 1;
|
||||||
|
#10;
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// Проверка равенства (поддерживает 4‑битные и 6‑битные аргументы)
|
||||||
|
task check_equal(input [31:0] expected, input [31:0] actual, input [80*8:0] msg);
|
||||||
|
if (expected !== actual) begin
|
||||||
|
$display("ERROR: %s. Expected %h, got %h", msg, expected, actual);
|
||||||
|
end else begin
|
||||||
|
$display("OK: %s", msg);
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$dumpfile("zx_cartridge.vcd");
|
||||||
|
$dumpvars(0, tb_zx_cartridge);
|
||||||
|
|
||||||
|
// Исходное состояние: сброс активен, все сигналы неактивны
|
||||||
|
reset_n = 0;
|
||||||
|
iorq_n = 1;
|
||||||
|
rd_n = 1;
|
||||||
|
wr_n = 1;
|
||||||
|
mreq_n = 1;
|
||||||
|
address = 16'h0000;
|
||||||
|
D_drive = 8'bz;
|
||||||
|
#100;
|
||||||
|
reset_n = 1;
|
||||||
|
#10;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test 1: Запись и чтение регистров через порты
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
$display("=== Test 1: Write and read registers via I/O ports ===");
|
||||||
|
|
||||||
|
// Запись в bank: адрес 0xC000 (A15=1, A14=1, A13=0, A7=0)
|
||||||
|
write_port(16'hC000, 8'hA5); // запись reg_bank = 0xA5
|
||||||
|
read_port(16'hC000, dummy);
|
||||||
|
check_equal(8'hA5, dummy, "Read bank returns written value");
|
||||||
|
|
||||||
|
// Запись в control: адрес 0xA000 (A15=1, A14=0, A13=1, A7=0)
|
||||||
|
write_port(16'hA000, 8'h80); // запись reg_ctl с битом 7 = 1 (отключение)
|
||||||
|
read_port(16'hA000, dummy);
|
||||||
|
check_equal(8'h80, dummy, "Read control returns written value");
|
||||||
|
|
||||||
|
// Сбрасываем бит disable (reg_ctl[7]=0) для дальнейших тестов
|
||||||
|
write_port(16'hA000, 8'h00);
|
||||||
|
read_port(16'hA000, dummy);
|
||||||
|
check_equal(8'h00, dummy, "Control = 0 after disable cleared");
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test 2: Формирование страницы и выбор микросхемы
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
$display("=== Test 2: Page and chip select formation ===");
|
||||||
|
|
||||||
|
// Записываем bank = 0xA5 -> chip_sel = 2'b10 = 2, page_offs = 6'b100101 = 37
|
||||||
|
write_port(16'hC000, 8'hA5);
|
||||||
|
|
||||||
|
// Верхнее окно (A13=1): адрес 0x2000
|
||||||
|
// Ожидаем CR_ROM_A = 37, активный CS2 (бит 2 = 0) -> 4'b1011 (младший бит = CS0)
|
||||||
|
read_mem_check(16'h2000, 6'd37, 4'b1011); // CS2 активен (0), остальные 1
|
||||||
|
|
||||||
|
// Нижнее окно (A13=0): адрес 0x1000
|
||||||
|
// Ожидаем CR_ROM_A = 0, активный CS0 -> 4'b1110
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test 3: Проверка всех вариантов chip_sel
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
$display("=== Test 3: Chip select generation for all chip_sel values ===");
|
||||||
|
|
||||||
|
// chip_sel = 0
|
||||||
|
write_port(16'hC000, 8'h00); // 0b00000000
|
||||||
|
read_mem_check(16'h2000, 6'd0, 4'b1110); // CS0 активен (0) -> 1110
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно тоже CS0
|
||||||
|
|
||||||
|
// chip_sel = 1
|
||||||
|
write_port(16'hC000, 8'h40); // 0b01000000 -> chip_sel=1, offs=0
|
||||||
|
read_mem_check(16'h2000, 6'd0, 4'b1101); // CS1 активен -> 1101
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно CS0
|
||||||
|
|
||||||
|
// chip_sel = 2
|
||||||
|
write_port(16'hC000, 8'h80); // 0b10000000 -> chip_sel=2, offs=0
|
||||||
|
read_mem_check(16'h2000, 6'd0, 4'b1011); // CS2 активен -> 1011
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||||||
|
|
||||||
|
// chip_sel = 3
|
||||||
|
write_port(16'hC000, 8'hC0); // 0b11000000 -> chip_sel=3, offs=0
|
||||||
|
read_mem_check(16'h2000, 6'd0, 4'b0111); // CS3 активен -> 0111
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test 4: Сигнал rom_access и CR_ROM_oe_n / ZX_ROM_blk
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
$display("=== Test 4: rom_access control ===");
|
||||||
|
|
||||||
|
// Включим картридж (reg_ctl[7]=0) – уже 0
|
||||||
|
// Чтение из области ROM (адрес 0x1000)
|
||||||
|
read_mem_check_oe(16'h1000, 1'b0, 1'b1); // CR_ROM_oe_n = 0, ZX_ROM_blk = 1
|
||||||
|
|
||||||
|
// Чтение из области не ROM (адрес 0x4000, A15=0, A14=1)
|
||||||
|
read_mem_check_oe(16'h4000, 1'b1, 1'b0); // оба неактивны
|
||||||
|
|
||||||
|
// Отключим картридж (установим бит 7)
|
||||||
|
write_port(16'hA000, 8'h80);
|
||||||
|
read_mem_check_oe(16'h1000, 1'b1, 1'b0); // неактивны, т.к. картридж отключён
|
||||||
|
|
||||||
|
// Снова включим
|
||||||
|
write_port(16'hA000, 8'h00);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Test 5: Сброс
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
$display("=== Test 5: Reset ===");
|
||||||
|
reset_n = 0;
|
||||||
|
#20;
|
||||||
|
reset_n = 1;
|
||||||
|
#10;
|
||||||
|
|
||||||
|
// Проверим, что регистры сброшены в 0
|
||||||
|
read_port(16'hC000, dummy);
|
||||||
|
check_equal(8'h00, dummy, "bank reads 0 after reset");
|
||||||
|
read_port(16'hA000, dummy);
|
||||||
|
check_equal(8'h00, dummy, "control reads 0 after reset");
|
||||||
|
|
||||||
|
// Проверим поведение после сброса: нижнее окно CS0, страница 0
|
||||||
|
read_mem_check(16'h1000, 6'd0, 4'b1110); // нижнее окно: CS0 активен
|
||||||
|
read_mem_check(16'h2000, 6'd0, 4'b1110); // верхнее окно тоже должно быть CS0 (т.к. bank=0)
|
||||||
|
|
||||||
|
$display("=== All tests completed ===");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@ -1,62 +0,0 @@
|
|||||||
`timescale 1ns / 1ps
|
|
||||||
// ZX SPECTRUM cartrige module
|
|
||||||
// 17.02.2026 Mikhael Kaa
|
|
||||||
// CPU adr bus A0...A12 connect directly to CR_ROM chip
|
|
||||||
module zx_cartrige #(
|
|
||||||
// default example parameter
|
|
||||||
parameter SELF_LOCK_VAL = 15
|
|
||||||
)(
|
|
||||||
// Reset
|
|
||||||
input reset_n,
|
|
||||||
// CPU ctrl signals
|
|
||||||
input iorq_n,
|
|
||||||
input rd_n,
|
|
||||||
input mreq_n,
|
|
||||||
// Part of CPU adr bus
|
|
||||||
input A7,
|
|
||||||
input A13,
|
|
||||||
input A14,
|
|
||||||
input A15,
|
|
||||||
|
|
||||||
// ZX ROM block
|
|
||||||
output ZX_ROM_blk,
|
|
||||||
// Cartrige ROM enable
|
|
||||||
output CR_ROM_oe_n,
|
|
||||||
// Up part cartrige ROM adr bus (A13...A18)
|
|
||||||
output [5:0] CR_ROM_A,
|
|
||||||
output [3:0] CR_ROM_CS
|
|
||||||
|
|
||||||
);
|
|
||||||
// CR_ROM 8kb bank counter
|
|
||||||
reg [5:0] CR_ROM_bank_cnt = 6'b0;
|
|
||||||
// Self lock register, disable all logic and CR_ROM
|
|
||||||
reg self_lock = 1'b0;
|
|
||||||
// rd or wr port 0x7f increment CR_ROM bank
|
|
||||||
wire rom_page_up = iorq_n | A7 | self_lock;
|
|
||||||
// CPU work with 0000...1fff adr
|
|
||||||
wire lower_rom = ({A13, A14, A15} == 3'b000) ? 1'b1 : 1'b0;
|
|
||||||
|
|
||||||
always @(negedge rom_page_up or negedge reset_n) begin
|
|
||||||
if(!reset_n) begin
|
|
||||||
CR_ROM_bank_cnt <= 6'b0;
|
|
||||||
self_lock <= 1'b0;
|
|
||||||
end else begin
|
|
||||||
// increment bank counter
|
|
||||||
CR_ROM_bank_cnt <= CR_ROM_bank_cnt + 1'b1;
|
|
||||||
// check self lock
|
|
||||||
if(CR_ROM_bank_cnt == SELF_LOCK_VAL) begin
|
|
||||||
self_lock <= 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assign CR_ROM_oe_n = ~lower_rom | rd_n | mreq_n | self_lock ;
|
|
||||||
assign ZX_ROM_blk = ~CR_ROM_oe_n;
|
|
||||||
assign CR_ROM_CS[0] = CR_ROM_oe_n;
|
|
||||||
assign CR_ROM_CS[1] = 1'b1;
|
|
||||||
assign CR_ROM_CS[2] = 1'b1;
|
|
||||||
assign CR_ROM_CS[3] = 1'b1;
|
|
||||||
|
|
||||||
assign CR_ROM_A = CR_ROM_bank_cnt;
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@ -1,196 +0,0 @@
|
|||||||
`timescale 1ns / 1ps
|
|
||||||
|
|
||||||
module tb_zx_cartrige();
|
|
||||||
// Управляющие сигналы
|
|
||||||
reg reset_n;
|
|
||||||
reg iorq_n;
|
|
||||||
reg rd_n;
|
|
||||||
reg mreq_n;
|
|
||||||
|
|
||||||
// Полная адресная шина (16 бит)
|
|
||||||
reg [15:0] address;
|
|
||||||
|
|
||||||
// Подключение отдельных бит к DUT
|
|
||||||
wire A7 = address[7];
|
|
||||||
wire A13 = address[13];
|
|
||||||
wire A14 = address[14];
|
|
||||||
wire A15 = address[15];
|
|
||||||
|
|
||||||
// Выходы DUT
|
|
||||||
wire ZX_ROM_blk;
|
|
||||||
wire CR_ROM_oe_n;
|
|
||||||
wire [5:0] CR_ROM_A;
|
|
||||||
|
|
||||||
// Тестируемый модуль (с уменьшенным параметром для быстрой проверки)
|
|
||||||
zx_cartrige #(
|
|
||||||
.SELF_LOCK_VAL(3)
|
|
||||||
) uut (
|
|
||||||
.reset_n(reset_n),
|
|
||||||
.iorq_n(iorq_n),
|
|
||||||
.rd_n(rd_n),
|
|
||||||
.mreq_n(mreq_n),
|
|
||||||
.A7(A7),
|
|
||||||
.A13(A13),
|
|
||||||
.A14(A14),
|
|
||||||
.A15(A15),
|
|
||||||
.ZX_ROM_blk(ZX_ROM_blk),
|
|
||||||
.CR_ROM_oe_n(CR_ROM_oe_n),
|
|
||||||
.CR_ROM_A(CR_ROM_A)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Задачи для моделирования циклов Z80
|
|
||||||
// Запись в порт (активируется iorq_n, для инкремента важен его спад)
|
|
||||||
task write_port(input [15:0] addr);
|
|
||||||
begin
|
|
||||||
address = addr;
|
|
||||||
#10;
|
|
||||||
iorq_n = 0; // начало цикла IN/OUT
|
|
||||||
#10;
|
|
||||||
iorq_n = 1; // завершение цикла – отрицательный фронт
|
|
||||||
#10;
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
// Чтение из памяти
|
|
||||||
task read_mem(input [15:0] addr);
|
|
||||||
begin
|
|
||||||
address = addr;
|
|
||||||
#10;
|
|
||||||
mreq_n = 0; // запрос памяти
|
|
||||||
rd_n = 0; // чтение
|
|
||||||
#20; // удерживаем для проверки
|
|
||||||
mreq_n = 1;
|
|
||||||
rd_n = 1;
|
|
||||||
#10;
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
// Проверка с выводом сообщения
|
|
||||||
task check_equal(input [31:0] expected, input [31:0] actual, input [80*8:0] msg);
|
|
||||||
if (expected !== actual) begin
|
|
||||||
$display("ERROR: %s. Expected %d, got %d", msg, expected, actual);
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
$dumpfile("zx_cartrige.vcd");
|
|
||||||
$dumpvars(0, tb_zx_cartrige);
|
|
||||||
|
|
||||||
// Исходное состояние: сброс активен, все сигналы неактивны
|
|
||||||
reset_n = 0;
|
|
||||||
iorq_n = 1;
|
|
||||||
rd_n = 1;
|
|
||||||
mreq_n = 1;
|
|
||||||
address = 16'h0000;
|
|
||||||
#100;
|
|
||||||
reset_n = 1;
|
|
||||||
#10;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 1: Инкремент происходит только при A7=0 и спаде iorq_n
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 1: Increment condition (A7=0 and iorq_n falling) ===");
|
|
||||||
check_equal(0, CR_ROM_A, "Initial CR_ROM_A");
|
|
||||||
|
|
||||||
// Попытка с A7=1 – не должен инкрементироваться
|
|
||||||
write_port(16'h0080); // A7=1 (адрес 0x80)
|
|
||||||
#10;
|
|
||||||
check_equal(0, CR_ROM_A, "After write to port 0x80 (A7=1)");
|
|
||||||
|
|
||||||
// Корректный инкремент с A7=0
|
|
||||||
write_port(16'h007F); // A7=0
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_A, "After first write to 0x7F");
|
|
||||||
|
|
||||||
write_port(16'h007F); // второй раз
|
|
||||||
#10;
|
|
||||||
check_equal(2, CR_ROM_A, "After second write to 0x7F");
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 2: Достижение SELF_LOCK_VAL (3) блокирует дальнейшие инкременты
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 2: Self-lock at value 3 ===");
|
|
||||||
write_port(16'h007F); // третий раз -> lock
|
|
||||||
#10;
|
|
||||||
check_equal(3, CR_ROM_A, "After third write (should lock)");
|
|
||||||
|
|
||||||
// Попытка инкремента после блокировки
|
|
||||||
write_port(16'h007F);
|
|
||||||
#10;
|
|
||||||
check_equal(3, CR_ROM_A, "Write after lock - no increment");
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 3: При self_lock=1 CR_ROM_oe_n не активируется даже в нижней ROM
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 3: CR_ROM_oe_n inactive while locked ===");
|
|
||||||
read_mem(16'h0100); // адрес в нижней области (0x100)
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n during locked read");
|
|
||||||
check_equal(0, ZX_ROM_blk, "ZX_ROM_blk during locked read");
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 4: Сброс обнуляет счётчик и снимает блокировку
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 4: Reset ===");
|
|
||||||
reset_n = 0;
|
|
||||||
#20;
|
|
||||||
reset_n = 1;
|
|
||||||
#10;
|
|
||||||
check_equal(0, CR_ROM_A, "After reset");
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n after reset");
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 5: Активация CR_ROM_oe_n при чтении нижних 8KB (self_lock=0)
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 5: CR_ROM_oe_n activation in lower ROM (0x0000-0x1FFF) ===");
|
|
||||||
|
|
||||||
// Чтение внутри нижней области
|
|
||||||
read_mem(16'h0100);
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x100");
|
|
||||||
check_equal(0, ZX_ROM_blk, "ZX_ROM_blk at 0x100");
|
|
||||||
|
|
||||||
read_mem(16'h1FFF); // граница нижней области
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x1FFF");
|
|
||||||
|
|
||||||
// Чтение вне нижней области
|
|
||||||
read_mem(16'h2000); // A13=1
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x2000 (outside)");
|
|
||||||
|
|
||||||
read_mem(16'h4001); // A14=1
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n at 0x4001 (outside)");
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Test 6: Проверка влияния mreq_n и rd_n
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
$display("=== Test 6: Control signals mreq_n and rd_n ===");
|
|
||||||
address = 16'h0100;
|
|
||||||
#10;
|
|
||||||
|
|
||||||
// mreq_n=0, rd_n=1 – чтение не активно
|
|
||||||
mreq_n = 0; rd_n = 1;
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n with rd_n=1");
|
|
||||||
|
|
||||||
// mreq_n=1, rd_n=0 – нет запроса памяти
|
|
||||||
mreq_n = 1; rd_n = 0;
|
|
||||||
#10;
|
|
||||||
check_equal(1, CR_ROM_oe_n, "CR_ROM_oe_n with mreq_n=1");
|
|
||||||
|
|
||||||
// Оба активны – должно включиться
|
|
||||||
mreq_n = 0; rd_n = 0;
|
|
||||||
#10;
|
|
||||||
check_equal(0, CR_ROM_oe_n, "CR_ROM_oe_n with both active");
|
|
||||||
|
|
||||||
// Возврат в исходное
|
|
||||||
mreq_n = 1; rd_n = 1;
|
|
||||||
#10;
|
|
||||||
|
|
||||||
$display("=== All tests completed ===");
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
set_global_assignment -name FAMILY MAX7000S
|
set_global_assignment -name FAMILY MAX7000S
|
||||||
set_global_assignment -name DEVICE "EPM7064SLC44-10"
|
set_global_assignment -name DEVICE "EPM7064SLC44-10"
|
||||||
set_global_assignment -name TOP_LEVEL_ENTITY zx_cartrige
|
set_global_assignment -name TOP_LEVEL_ENTITY zx_cartridge
|
||||||
set_global_assignment -name ORIGINAL_QUARTUS_VERSION "13.0 SP1"
|
set_global_assignment -name ORIGINAL_QUARTUS_VERSION "13.0 SP1"
|
||||||
set_global_assignment -name PROJECT_CREATION_TIME_DATE "14:32:59 FEBRUARY 06, 2026"
|
set_global_assignment -name PROJECT_CREATION_TIME_DATE "14:32:59 FEBRUARY 06, 2026"
|
||||||
set_global_assignment -name LAST_QUARTUS_VERSION "13.0 SP1"
|
set_global_assignment -name LAST_QUARTUS_VERSION "13.0 SP1"
|
||||||
@ -50,25 +50,34 @@ set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR "-1"
|
|||||||
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
|
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
|
||||||
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
|
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
|
||||||
set_global_assignment -name MAX7000_DEVICE_IO_STANDARD TTL
|
set_global_assignment -name MAX7000_DEVICE_IO_STANDARD TTL
|
||||||
set_location_assignment PIN_1 -to reset_n
|
set_global_assignment -name VERILOG_FILE src/zx_cartridge.v
|
||||||
set_global_assignment -name VERILOG_FILE src/zx_cartrige.v
|
|
||||||
set_global_assignment -name CDF_FILE output_files/Chain1.cdf
|
set_global_assignment -name CDF_FILE output_files/Chain1.cdf
|
||||||
set_location_assignment PIN_18 -to A7
|
set_location_assignment PIN_1 -to reset_n
|
||||||
set_location_assignment PIN_19 -to A13
|
set_location_assignment PIN_2 -to rd_n
|
||||||
set_location_assignment PIN_20 -to A14
|
set_location_assignment PIN_4 -to wr_n
|
||||||
set_location_assignment PIN_21 -to A15
|
set_location_assignment PIN_6 -to A14
|
||||||
set_location_assignment PIN_31 -to CR_ROM_A[5]
|
set_location_assignment PIN_8 -to A15
|
||||||
set_location_assignment PIN_29 -to CR_ROM_A[4]
|
set_location_assignment PIN_9 -to A13
|
||||||
set_location_assignment PIN_28 -to CR_ROM_A[3]
|
set_location_assignment PIN_11 -to CR_ROM_CS[0]
|
||||||
set_location_assignment PIN_9 -to CR_ROM_A[2]
|
set_location_assignment PIN_12 -to CR_ROM_CS[1]
|
||||||
set_location_assignment PIN_11 -to CR_ROM_A[1]
|
set_location_assignment PIN_14 -to CR_ROM_CS[2]
|
||||||
set_location_assignment PIN_12 -to CR_ROM_A[0]
|
set_location_assignment PIN_16 -to CR_ROM_A[5]
|
||||||
set_location_assignment PIN_34 -to CR_ROM_oe_n
|
set_location_assignment PIN_18 -to CR_ROM_A[4]
|
||||||
set_location_assignment PIN_27 -to ZX_ROM_blk
|
set_location_assignment PIN_19 -to CR_ROM_A[3]
|
||||||
set_location_assignment PIN_24 -to iorq_n
|
set_location_assignment PIN_20 -to CR_ROM_A[1]
|
||||||
set_location_assignment PIN_25 -to mreq_n
|
set_location_assignment PIN_21 -to CR_ROM_A[2]
|
||||||
set_location_assignment PIN_26 -to rd_n
|
set_location_assignment PIN_24 -to A7
|
||||||
set_location_assignment PIN_8 -to CR_ROM_CS[3]
|
set_location_assignment PIN_25 -to CR_ROM_A[0]
|
||||||
set_location_assignment PIN_6 -to CR_ROM_CS[2]
|
set_location_assignment PIN_26 -to CR_ROM_oe_n
|
||||||
set_location_assignment PIN_5 -to CR_ROM_CS[1]
|
set_location_assignment PIN_27 -to CR_ROM_CS[2]
|
||||||
set_location_assignment PIN_4 -to CR_ROM_CS[0]
|
set_location_assignment PIN_28 -to D[0]
|
||||||
|
set_location_assignment PIN_29 -to D[1]
|
||||||
|
set_location_assignment PIN_31 -to D[7]
|
||||||
|
set_location_assignment PIN_33 -to ZX_ROM_blk
|
||||||
|
set_location_assignment PIN_34 -to D[2]
|
||||||
|
set_location_assignment PIN_37 -to D[6]
|
||||||
|
set_location_assignment PIN_39 -to D[4]
|
||||||
|
set_location_assignment PIN_40 -to D[3]
|
||||||
|
set_location_assignment PIN_41 -to D[5]
|
||||||
|
set_location_assignment PIN_43 -to mreq_n
|
||||||
|
set_location_assignment PIN_44 -to iorq_n
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -717,6 +717,22 @@ DItemRevisionGUID=
|
|||||||
GenerateClassCluster=0
|
GenerateClassCluster=0
|
||||||
DocumentUniqueId=PLYWCAJP
|
DocumentUniqueId=PLYWCAJP
|
||||||
|
|
||||||
|
[GeneratedDocument1]
|
||||||
|
DocumentPath=Project Outputs for zx_cartridge\Design Rule Check - pcb.html
|
||||||
|
DItemRevisionGUID=
|
||||||
|
|
||||||
|
[GeneratedDocument2]
|
||||||
|
DocumentPath=Project Outputs for zx_cartridge\pcb.DRR
|
||||||
|
DItemRevisionGUID=
|
||||||
|
|
||||||
|
[GeneratedDocument3]
|
||||||
|
DocumentPath=Project Outputs for zx_cartridge\pcb.LDP
|
||||||
|
DItemRevisionGUID=
|
||||||
|
|
||||||
|
[GeneratedDocument4]
|
||||||
|
DocumentPath=Project Outputs for zx_cartridge\pcb.TXT
|
||||||
|
DItemRevisionGUID=
|
||||||
|
|
||||||
[Configuration1]
|
[Configuration1]
|
||||||
Name=Sources
|
Name=Sources
|
||||||
ParameterCount=0
|
ParameterCount=0
|
||||||
@ -973,99 +989,99 @@ Name=Fabrication Outputs
|
|||||||
Description=
|
Description=
|
||||||
TargetPrinter=Microsoft Print to PDF
|
TargetPrinter=Microsoft Print to PDF
|
||||||
PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
|
PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
|
||||||
OutputType1=CompositeDrill
|
OutputType1=NC Drill
|
||||||
OutputName1=Composite Drill Drawing
|
OutputName1=NC Drill Files
|
||||||
OutputDocumentPath1=
|
OutputDocumentPath1=
|
||||||
OutputVariantName1=
|
OutputVariantName1=
|
||||||
OutputDefault1=0
|
OutputDefault1=0
|
||||||
PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
Configuration1_Name1=ForceUpdateSettings
|
||||||
OutputType2=Drill
|
Configuration1_Item1=False
|
||||||
OutputName2=Drill Drawing/Guides
|
Configuration1_Name2=OutputConfigurationParameter1
|
||||||
|
Configuration1_Item2=BoardEdgeRoutToolDia=2000000|GenerateBoardEdgeRout=False|GenerateDrilledSlotsG85=False|GenerateEIADrillFile=False|GenerateSeparatePlatedNonPlatedFiles=False|NumberOfDecimals=5|NumberOfUnits=2|OptimizeChangeLocationCommands=True|OriginPosition=Relative|Record=DrillView|Units=Imperial|ZeroesMode=SuppressTrailingZeroes
|
||||||
|
OutputType2=Mask
|
||||||
|
OutputName2=Solder/Paste Mask Prints
|
||||||
OutputDocumentPath2=
|
OutputDocumentPath2=
|
||||||
OutputVariantName2=
|
OutputVariantName2=
|
||||||
OutputDefault2=0
|
OutputDefault2=0
|
||||||
PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=2.56|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
Configuration2_Name1=ForceUpdateSettings
|
OutputType3=IPC2581
|
||||||
Configuration2_Item1=False
|
OutputName3=IPC-2581 Files
|
||||||
Configuration2_Name2=OutputConfigurationParameter1
|
|
||||||
Configuration2_Item2=DesignatorDisplayMode=Physical|PrintArea=DesignExtent|PrintAreaLowerLeftCornerX=0|PrintAreaLowerLeftCornerY=0|PrintAreaUpperRightCornerX=0|PrintAreaUpperRightCornerY=0|Record=PcbPrintView
|
|
||||||
Configuration2_Name3=OutputConfigurationParameter2
|
|
||||||
Configuration2_Item3=IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=0|Mirror=False|Name=Drill Drawing For (Top Layer - Bottom Layer)|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False
|
|
||||||
Configuration2_Name4=OutputConfigurationParameter3
|
|
||||||
Configuration2_Item4=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=DrillDrawing|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name5=OutputConfigurationParameter4
|
|
||||||
Configuration2_Item5=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name6=OutputConfigurationParameter5
|
|
||||||
Configuration2_Item6=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name7=OutputConfigurationParameter6
|
|
||||||
Configuration2_Item7=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name8=OutputConfigurationParameter7
|
|
||||||
Configuration2_Item8=IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=1|Mirror=False|Name=Drill Guide For (Top Layer - Bottom Layer)|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False
|
|
||||||
Configuration2_Name9=OutputConfigurationParameter8
|
|
||||||
Configuration2_Item9=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=DrillGuide|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name10=OutputConfigurationParameter9
|
|
||||||
Configuration2_Item10=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name11=OutputConfigurationParameter10
|
|
||||||
Configuration2_Item11=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
|
||||||
Configuration2_Name12=OutputConfigurationParameter11
|
|
||||||
Configuration2_Item12=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
|
||||||
OutputType3=Board Stack Report
|
|
||||||
OutputName3=Report Board Stack
|
|
||||||
OutputDocumentPath3=
|
OutputDocumentPath3=
|
||||||
OutputVariantName3=
|
OutputVariantName3=
|
||||||
OutputDefault3=0
|
OutputDefault3=0
|
||||||
PageOptions3=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
OutputType4=Test Points
|
||||||
OutputType4=Final
|
OutputName4=Test Point Report
|
||||||
OutputName4=Final Artwork Prints
|
|
||||||
OutputDocumentPath4=
|
OutputDocumentPath4=
|
||||||
OutputVariantName4=[No Variations]
|
OutputVariantName4=
|
||||||
OutputDefault4=0
|
OutputDefault4=0
|
||||||
PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
OutputType5=Plane
|
||||||
OutputType5=Gerber X2
|
OutputName5=Power-Plane Prints
|
||||||
OutputName5=Gerber X2 Files
|
|
||||||
OutputDocumentPath5=
|
OutputDocumentPath5=
|
||||||
OutputVariantName5=
|
OutputVariantName5=
|
||||||
OutputDefault5=0
|
OutputDefault5=0
|
||||||
Configuration5_Name1=ForceUpdateSettings
|
PageOptions5=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
Configuration5_Item1=False
|
|
||||||
Configuration5_Name2=OutputConfigurationParameter1
|
|
||||||
Configuration5_Item2=FileComment= |FileSubject=Autodetect|GenerateDRCRulesFile=True|GerberUnit=Imperial|MinusApertureTolerance=50|NumberOfDecimals=6|OptimizeChangeLocationCommands=True|PlotBackDrillingPairs=False|PlotBlindViasPairs=False|PlotBoardProfile=True|PlotDrillDrawingPair0_Backdrill=False|PlotDrillDrawingPair0_DrillType=Regular|PlotDrillDrawingPair0_HighLayer=Bottom Layer|PlotDrillDrawingPair0_LowLayer=Top Layer|PlotDrillDrawingPairs=False|PlotDrillGuidePair0_Backdrill=False|PlotDrillGuidePair0_DrillType=Regular|PlotDrillGuidePair0_HighLayer=Bottom Layer|PlotDrillGuidePair0_LowLayer=Top Layer|PlotDrillGuidePairs=False|PlotMicroViasPairs=False|PlotNPTHPair0_Backdrill=False|PlotNPTHPair0_DrillType=Regular|PlotNPTHPair0_HighLayer=Top Layer|PlotNPTHPair0_LowLayer=Bottom Layer|PlotNPTHPairs=False|PlotPTHPair0_Backdrill=False|PlotPTHPair0_DrillType=Regular|PlotPTHPair0_HighLayer=Top Layer|PlotPTHPair0_LowLayer=Bottom Layer|PlotPTHPairs=False|PlotX2.Set=SerializeLayerHash.Version~2,ClassName~TLayerToBoolean,16777217~1,16973831~1,16973849~1,16973835~1,16973830~1,16842751~1,16973834~1,16973837~1,16973848~1|PlusApertureTolerance=50|Record=GerberX2View|Sorted=False
|
|
||||||
OutputType6=ODB
|
OutputType6=ODB
|
||||||
OutputName6=ODB++ Files
|
OutputName6=ODB++ Files
|
||||||
OutputDocumentPath6=
|
OutputDocumentPath6=
|
||||||
OutputVariantName6=[No Variations]
|
OutputVariantName6=[No Variations]
|
||||||
OutputDefault6=0
|
OutputDefault6=0
|
||||||
OutputType7=Mask
|
OutputType7=Drill
|
||||||
OutputName7=Solder/Paste Mask Prints
|
OutputName7=Drill Drawing/Guides
|
||||||
OutputDocumentPath7=
|
OutputDocumentPath7=
|
||||||
OutputVariantName7=
|
OutputVariantName7=
|
||||||
OutputDefault7=0
|
OutputDefault7=0
|
||||||
PageOptions7=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
PageOptions7=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.92|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
OutputType8=NC Drill
|
Configuration7_Name1=ForceUpdateSettings
|
||||||
OutputName8=NC Drill Files
|
Configuration7_Item1=False
|
||||||
|
Configuration7_Name2=OutputConfigurationParameter1
|
||||||
|
Configuration7_Item2=DesignatorDisplayMode=Physical|PrintArea=DesignExtent|PrintAreaLowerLeftCornerX=0|PrintAreaLowerLeftCornerY=0|PrintAreaUpperRightCornerX=0|PrintAreaUpperRightCornerY=0|Record=PcbPrintView
|
||||||
|
Configuration7_Name3=OutputConfigurationParameter2
|
||||||
|
Configuration7_Item3=IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=0|Mirror=False|Name=Drill Drawing For (Top Layer - Bottom Layer)|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False
|
||||||
|
Configuration7_Name4=OutputConfigurationParameter3
|
||||||
|
Configuration7_Item4=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=DrillDrawing|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name5=OutputConfigurationParameter4
|
||||||
|
Configuration7_Item5=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name6=OutputConfigurationParameter5
|
||||||
|
Configuration7_Item6=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name7=OutputConfigurationParameter6
|
||||||
|
Configuration7_Item7=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name8=OutputConfigurationParameter7
|
||||||
|
Configuration7_Item8=IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=1|Mirror=False|Name=Drill Guide For (Top Layer - Bottom Layer)|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False
|
||||||
|
Configuration7_Name9=OutputConfigurationParameter8
|
||||||
|
Configuration7_Item9=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=DrillGuide|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name10=OutputConfigurationParameter9
|
||||||
|
Configuration7_Item10=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name11=OutputConfigurationParameter10
|
||||||
|
Configuration7_Item11=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
||||||
|
Configuration7_Name12=OutputConfigurationParameter11
|
||||||
|
Configuration7_Item12=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=1|Record=PcbPrintLayer
|
||||||
|
OutputType8=CompositeDrill
|
||||||
|
OutputName8=Composite Drill Drawing
|
||||||
OutputDocumentPath8=
|
OutputDocumentPath8=
|
||||||
OutputVariantName8=
|
OutputVariantName8=
|
||||||
OutputDefault8=0
|
OutputDefault8=0
|
||||||
Configuration8_Name1=ForceUpdateSettings
|
PageOptions8=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
Configuration8_Item1=False
|
OutputType9=Board Stack Report
|
||||||
Configuration8_Name2=OutputConfigurationParameter1
|
OutputName9=Report Board Stack
|
||||||
Configuration8_Item2=BoardEdgeRoutToolDia=2000000|GenerateBoardEdgeRout=False|GenerateDrilledSlotsG85=False|GenerateEIADrillFile=False|GenerateSeparatePlatedNonPlatedFiles=False|NumberOfDecimals=5|NumberOfUnits=2|OptimizeChangeLocationCommands=True|OriginPosition=Relative|Record=DrillView|Units=Imperial|ZeroesMode=SuppressTrailingZeroes
|
|
||||||
OutputType9=IPC2581
|
|
||||||
OutputName9=IPC-2581 Files
|
|
||||||
OutputDocumentPath9=
|
OutputDocumentPath9=
|
||||||
OutputVariantName9=
|
OutputVariantName9=
|
||||||
OutputDefault9=0
|
OutputDefault9=0
|
||||||
OutputType10=Plane
|
PageOptions9=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
OutputName10=Power-Plane Prints
|
OutputType10=Gerber X2
|
||||||
|
OutputName10=Gerber X2 Files
|
||||||
OutputDocumentPath10=
|
OutputDocumentPath10=
|
||||||
OutputVariantName10=
|
OutputVariantName10=
|
||||||
OutputDefault10=0
|
OutputDefault10=0
|
||||||
PageOptions10=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
Configuration10_Name1=ForceUpdateSettings
|
||||||
OutputType11=Test Points
|
Configuration10_Item1=False
|
||||||
OutputName11=Test Point Report
|
Configuration10_Name2=OutputConfigurationParameter1
|
||||||
|
Configuration10_Item2=FileComment= |FileSubject=Autodetect|GenerateDRCRulesFile=True|GerberUnit=Imperial|MinusApertureTolerance=50|NumberOfDecimals=6|OptimizeChangeLocationCommands=True|PlotBackDrillingPairs=False|PlotBlindViasPairs=False|PlotBoardProfile=True|PlotDrillDrawingPair0_Backdrill=False|PlotDrillDrawingPair0_DrillType=Regular|PlotDrillDrawingPair0_HighLayer=Bottom Layer|PlotDrillDrawingPair0_LowLayer=Top Layer|PlotDrillDrawingPairs=False|PlotDrillGuidePair0_Backdrill=False|PlotDrillGuidePair0_DrillType=Regular|PlotDrillGuidePair0_HighLayer=Bottom Layer|PlotDrillGuidePair0_LowLayer=Top Layer|PlotDrillGuidePairs=False|PlotMicroViasPairs=False|PlotNPTHPair0_Backdrill=False|PlotNPTHPair0_DrillType=Regular|PlotNPTHPair0_HighLayer=Top Layer|PlotNPTHPair0_LowLayer=Bottom Layer|PlotNPTHPairs=False|PlotPTHPair0_Backdrill=False|PlotPTHPair0_DrillType=Regular|PlotPTHPair0_HighLayer=Top Layer|PlotPTHPair0_LowLayer=Bottom Layer|PlotPTHPairs=False|PlotX2.Set=SerializeLayerHash.Version~2,ClassName~TLayerToBoolean,16777217~1,16973831~1,16973849~1,16973835~1,16973830~1,16842751~1,16973834~1,16973837~1,16973848~1|PlusApertureTolerance=50|Record=GerberX2View|Sorted=False
|
||||||
|
OutputType11=Final
|
||||||
|
OutputName11=Final Artwork Prints
|
||||||
OutputDocumentPath11=
|
OutputDocumentPath11=
|
||||||
OutputVariantName11=
|
OutputVariantName11=[No Variations]
|
||||||
OutputDefault11=0
|
OutputDefault11=0
|
||||||
|
PageOptions11=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
|
||||||
OutputType12=Gerber
|
OutputType12=Gerber
|
||||||
OutputName12=Gerber Files
|
OutputName12=Gerber Files
|
||||||
OutputDocumentPath12=
|
OutputDocumentPath12=
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user