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
|
||||
|
||||
/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
|
||||
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
|
||||
|
||||
TARGET ?= zx_cartrige
|
||||
TARGET ?= zx_cartridge
|
||||
ICARUS = iverilog
|
||||
|
||||
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 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 PROJECT_CREATION_TIME_DATE "14:32:59 FEBRUARY 06, 2026"
|
||||
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 MAX_CORE_JUNCTION_TEMP 85
|
||||
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_cartrige.v
|
||||
set_global_assignment -name VERILOG_FILE src/zx_cartridge.v
|
||||
set_global_assignment -name CDF_FILE output_files/Chain1.cdf
|
||||
set_location_assignment PIN_18 -to A7
|
||||
set_location_assignment PIN_19 -to A13
|
||||
set_location_assignment PIN_20 -to A14
|
||||
set_location_assignment PIN_21 -to A15
|
||||
set_location_assignment PIN_31 -to CR_ROM_A[5]
|
||||
set_location_assignment PIN_29 -to CR_ROM_A[4]
|
||||
set_location_assignment PIN_28 -to CR_ROM_A[3]
|
||||
set_location_assignment PIN_9 -to CR_ROM_A[2]
|
||||
set_location_assignment PIN_11 -to CR_ROM_A[1]
|
||||
set_location_assignment PIN_12 -to CR_ROM_A[0]
|
||||
set_location_assignment PIN_34 -to CR_ROM_oe_n
|
||||
set_location_assignment PIN_27 -to ZX_ROM_blk
|
||||
set_location_assignment PIN_24 -to iorq_n
|
||||
set_location_assignment PIN_25 -to mreq_n
|
||||
set_location_assignment PIN_26 -to rd_n
|
||||
set_location_assignment PIN_8 -to CR_ROM_CS[3]
|
||||
set_location_assignment PIN_6 -to CR_ROM_CS[2]
|
||||
set_location_assignment PIN_5 -to CR_ROM_CS[1]
|
||||
set_location_assignment PIN_4 -to CR_ROM_CS[0]
|
||||
set_location_assignment PIN_1 -to reset_n
|
||||
set_location_assignment PIN_2 -to rd_n
|
||||
set_location_assignment PIN_4 -to wr_n
|
||||
set_location_assignment PIN_6 -to A14
|
||||
set_location_assignment PIN_8 -to A15
|
||||
set_location_assignment PIN_9 -to A13
|
||||
set_location_assignment PIN_11 -to CR_ROM_CS[0]
|
||||
set_location_assignment PIN_12 -to CR_ROM_CS[1]
|
||||
set_location_assignment PIN_14 -to CR_ROM_CS[2]
|
||||
set_location_assignment PIN_16 -to CR_ROM_A[5]
|
||||
set_location_assignment PIN_18 -to CR_ROM_A[4]
|
||||
set_location_assignment PIN_19 -to CR_ROM_A[3]
|
||||
set_location_assignment PIN_20 -to CR_ROM_A[1]
|
||||
set_location_assignment PIN_21 -to CR_ROM_A[2]
|
||||
set_location_assignment PIN_24 -to A7
|
||||
set_location_assignment PIN_25 -to CR_ROM_A[0]
|
||||
set_location_assignment PIN_26 -to CR_ROM_oe_n
|
||||
set_location_assignment PIN_27 -to CR_ROM_CS[2]
|
||||
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
|
||||
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]
|
||||
Name=Sources
|
||||
ParameterCount=0
|
||||
@ -973,99 +989,99 @@ Name=Fabrication Outputs
|
||||
Description=
|
||||
TargetPrinter=Microsoft Print to PDF
|
||||
PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
|
||||
OutputType1=CompositeDrill
|
||||
OutputName1=Composite Drill Drawing
|
||||
OutputType1=NC Drill
|
||||
OutputName1=NC Drill Files
|
||||
OutputDocumentPath1=
|
||||
OutputVariantName1=
|
||||
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
|
||||
OutputType2=Drill
|
||||
OutputName2=Drill Drawing/Guides
|
||||
Configuration1_Name1=ForceUpdateSettings
|
||||
Configuration1_Item1=False
|
||||
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=
|
||||
OutputVariantName2=
|
||||
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
|
||||
Configuration2_Name1=ForceUpdateSettings
|
||||
Configuration2_Item1=False
|
||||
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
|
||||
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
|
||||
OutputType3=IPC2581
|
||||
OutputName3=IPC-2581 Files
|
||||
OutputDocumentPath3=
|
||||
OutputVariantName3=
|
||||
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=Final
|
||||
OutputName4=Final Artwork Prints
|
||||
OutputType4=Test Points
|
||||
OutputName4=Test Point Report
|
||||
OutputDocumentPath4=
|
||||
OutputVariantName4=[No Variations]
|
||||
OutputVariantName4=
|
||||
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=Gerber X2
|
||||
OutputName5=Gerber X2 Files
|
||||
OutputType5=Plane
|
||||
OutputName5=Power-Plane Prints
|
||||
OutputDocumentPath5=
|
||||
OutputVariantName5=
|
||||
OutputDefault5=0
|
||||
Configuration5_Name1=ForceUpdateSettings
|
||||
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
|
||||
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
|
||||
OutputType6=ODB
|
||||
OutputName6=ODB++ Files
|
||||
OutputDocumentPath6=
|
||||
OutputVariantName6=[No Variations]
|
||||
OutputDefault6=0
|
||||
OutputType7=Mask
|
||||
OutputName7=Solder/Paste Mask Prints
|
||||
OutputType7=Drill
|
||||
OutputName7=Drill Drawing/Guides
|
||||
OutputDocumentPath7=
|
||||
OutputVariantName7=
|
||||
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
|
||||
OutputType8=NC Drill
|
||||
OutputName8=NC Drill Files
|
||||
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
|
||||
Configuration7_Name1=ForceUpdateSettings
|
||||
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=
|
||||
OutputVariantName8=
|
||||
OutputDefault8=0
|
||||
Configuration8_Name1=ForceUpdateSettings
|
||||
Configuration8_Item1=False
|
||||
Configuration8_Name2=OutputConfigurationParameter1
|
||||
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
|
||||
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
|
||||
OutputType9=Board Stack Report
|
||||
OutputName9=Report Board Stack
|
||||
OutputDocumentPath9=
|
||||
OutputVariantName9=
|
||||
OutputDefault9=0
|
||||
OutputType10=Plane
|
||||
OutputName10=Power-Plane Prints
|
||||
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
|
||||
OutputType10=Gerber X2
|
||||
OutputName10=Gerber X2 Files
|
||||
OutputDocumentPath10=
|
||||
OutputVariantName10=
|
||||
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
|
||||
OutputType11=Test Points
|
||||
OutputName11=Test Point Report
|
||||
Configuration10_Name1=ForceUpdateSettings
|
||||
Configuration10_Item1=False
|
||||
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=
|
||||
OutputVariantName11=
|
||||
OutputVariantName11=[No Variations]
|
||||
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
|
||||
OutputName12=Gerber Files
|
||||
OutputDocumentPath12=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user