605 lines
24 KiB
Plaintext
605 lines
24 KiB
Plaintext
Krill's loader
|
|
|
|
November 2022
|
|
Krill@Plush.de
|
|
|
|
|
|
What it is
|
|
----------
|
|
This is an easy-to-use arbitrarily interruptible flexible general-purpose
|
|
file-based disk loader system with optional decompression and limited
|
|
save-with-replace capability, aiming to provide maximum loading speed while
|
|
retaining full standard Commodore DOS format compatibility and also having a
|
|
reasonably small memory footprint.
|
|
Being fully standard DOS format compatible, disks and disk images may be
|
|
written by numerous available third-party copy, transfer, and disk image
|
|
manipulation programs.
|
|
The selection of byte-stream based compression algorithms includes many popular
|
|
crunchers.
|
|
Natively supported drives include all variants of the classic Commodore drive
|
|
families of 1541, 1570/71, 1581, as well as CMD FD drives, and 1541U.
|
|
Using the KERNAL fallback option, devices like SD2IEC (slow), IDE64 (fast), and
|
|
netload are supported non-natively.
|
|
Both PAL and NTSC video systems are supported.
|
|
The drive's CPU may run custom code provided by the user.
|
|
|
|
|
|
What it isn't
|
|
-------------
|
|
Simply put, anything that's not a read-only disk driver (with limited saving
|
|
capabilities).
|
|
Although any of the following features and functionality may be built on top of
|
|
it, it is not:
|
|
- a DOS: it cannot save to newly-created files, format disks, provide random
|
|
access below file level, or otherwise organise data on a disk
|
|
- a framework of any kind: it's only active when you tell it to, it does not do
|
|
anything in the background by itself, and the machine is fully yours when the
|
|
loader is idle
|
|
- a memory expansion server: files are not cached in REU, GeoRAM, RAMLink,
|
|
RAMDrive, or similar devices
|
|
- loading in an interrupt handler or another mainline thread: this can easily
|
|
and flexibly be implemented by the user
|
|
|
|
|
|
Features
|
|
--------
|
|
- maximum raw loading speed of 7.7 kB/s (~20x) on 1541, typical speed is around
|
|
7 kB/s (~18x)
|
|
- resident size: $01f9 bytes for combined load+depack using ZX0 (fits in
|
|
$0200..$03f8), $01fb bytes with TSCrunch (fits in $0200..$03fa), $e0 bytes
|
|
load-raw only
|
|
- IRQ/NMI/DMA/sprites/badlines are allowed without restrictions
|
|
- 2bit+ATN protocol (72 cycles per byte), multiple drives on the bus are
|
|
allowed, but loading only from the primary drive
|
|
- supported crunchers: Bitnax, Byteboozer2, Doynax-LZ, Exomizer, Levelcrush,
|
|
LZSA2, NuCrunch, Pucrunch, Subsizer, tinycrunch, TSCrunch, ZX0
|
|
- depacking fully in-place using Bitnax, LZSA2, tinycrunch, TSCrunch or ZX0,
|
|
no clobbered bytes beyond unpacked data
|
|
- loading files in arbitrary order via directory with 0-terminated filename
|
|
strings
|
|
- loadnext functionality for sequential loading without specifying subsequent
|
|
filenames
|
|
- load address override for raw and crunched files
|
|
- PAL/NTSC support, NTSC is optional
|
|
- returns status codes for error checking and multiple disk handling
|
|
- optional loading and decompression to RAM at $d000..$dfff
|
|
- optional saving to pre-existing save-slot files
|
|
- optional memory decompression without loading
|
|
- optional loading via shadow directory for unrestricted dir-art
|
|
- custom drive code API facilitating co-processing on the drive's CPU
|
|
- full on-the-fly block GCR read+decode+checksumming
|
|
- native interleave at 100% CPU for loading uncompressed files is 3 revolutions
|
|
per track on tracks 18+, 4 on tracks 1-17, and 4 for all tracks for
|
|
compressed files
|
|
- implements Shrydar stepping for minimised seek time to an adjacent track
|
|
- free relocation of install routine, resident/transient portions and zeropage
|
|
variables
|
|
|
|
|
|
Folder structure overview
|
|
-------------------------
|
|
- loader this
|
|
- build output files
|
|
- docs documentation
|
|
- include include files
|
|
- src source code
|
|
- decompress decrunch routines
|
|
- drives drive-side code
|
|
- hal hardware abstraction layer implementations
|
|
- samples example projects
|
|
- benchmark benchmark using Bitfire/Sparkle corpus
|
|
- cc65 using the loader from a C program
|
|
- drivecode running custom code in the drive
|
|
- minexample minimum example
|
|
- resources graphics and other binaries
|
|
- save overwriting data in save-slot files
|
|
- standalone use loader from BASIC prompt
|
|
- test test application
|
|
- turndisk disk change handling
|
|
- tools utilities
|
|
- b2 Byteboozer2
|
|
- bitnax-07a8c67 Bitnax
|
|
- cc1541 use cc1541 for optimised disk images
|
|
- dali ZX0
|
|
- doynamite1.1 Doynax-LZ
|
|
- exomizer-3.1 Exomizer
|
|
- lzsa LZSA(2)
|
|
- nucrunch-1.0.1 NuCrunch
|
|
- pucrunch Pucrunch
|
|
- subsizer-0.7pre1 Subsizer
|
|
- tinycrunch_v1.2 tinycrunch
|
|
- tscrunch TSCrunch
|
|
- wcrush LevelCrush
|
|
- shared miscellaneous include files
|
|
|
|
|
|
Building the binaries
|
|
---------------------
|
|
Prebuilt binaries (install-c64.prg, loader-c64.prg and loadersymbols-c64.inc)
|
|
can be found in loader/build.
|
|
|
|
To build them, prerequisites are ca65 (see https://www.cc65.org,
|
|
https://cc65.github.io, or https://github.com/cc65/cc65), make, and perl.
|
|
In loader or loader/src, run
|
|
|
|
$ make prg
|
|
|
|
This will produce (and overwrite) install-c64.prg, loader-c64.prg, and
|
|
loadersymbols-c64.inc in loader/build.
|
|
|
|
To specify zeropage, install and resident addresses, run, e.g.,
|
|
|
|
$ make prg ZP=e8 INSTALL=1000 RESIDENT=ce00
|
|
|
|
which will produce binaries using zeropage addresses $e8 and up, with the
|
|
install routine located at $1000 and the resident portion at $ce00.
|
|
|
|
The two .prg files are regular C-64 program files with 2 bytes load address and
|
|
can be linked statically using .incbin or similar features of any 6502
|
|
assembler/linker.
|
|
The symbols file is a text file defining the call addresses and zeropage
|
|
variables in canonical 6502 assembler syntax and is intended to be included from
|
|
assembly source code.
|
|
|
|
|
|
Configuration
|
|
-------------
|
|
The main configuration file is loader/include/config.inc. The individual
|
|
settings of the options are evaluated at build time and directly control various
|
|
aspects of the loader's functionality and its memory footprint.
|
|
This include file can be overridden by setting the EXTCONFIGPATH environment
|
|
variable to a folder containing a file named loaderconfig.inc.
|
|
|
|
Note that NTSC support is optional, and disabled in the pre-built binaries. This
|
|
is because it slightly reduces PAL performance. However, resident portion
|
|
incarnations with and without NTSC support can be selected at run-time in order
|
|
to achieve maximum performance for both PAL and NTSC.
|
|
|
|
Refer to loader/samples for various example projects using different individual
|
|
external configuration files.
|
|
|
|
|
|
Basic operation
|
|
---------------
|
|
Before the loader can operate, its drive-side code portion(s) must be installed
|
|
in the drive(s). This is done by
|
|
|
|
jsr install; subsequent loader calls will only work on the active drive as
|
|
; denoted by FA = $ae
|
|
bcs error
|
|
; loader installed successfully
|
|
|
|
On error, the carry flag is set and an error code is returned in the accu. As
|
|
the install routine calls various KERNAL routines, the KERNAL ROM must be
|
|
enabled (e.g., $01 set to $36 or $37).
|
|
Note that the KERNAL routines also perform SEI and CLI, so it's advisable to set
|
|
up any custom interrupt handlers or enable sprites only after installing the
|
|
loader.
|
|
Also note that some variables in lowmem ($0200..$03ff) will be overwritten by
|
|
some KERNAL routines the install routine is calling. Thus, if the resident
|
|
portion resides in memory below $0400, make sure to copy it there only after
|
|
installing the loader.
|
|
Furthermore, in additon to FA = $ae, some more zeropage variables must have
|
|
valid values. See the bottom of loader/include/loader.inc for details. This list
|
|
may not be complete.
|
|
The drive-side portion remains resident in the drive. After successful
|
|
installation, the install routine is not needed any more and may be overwritten.
|
|
The KERNAL ROM may be disabled and zeropage variables clobbered.
|
|
|
|
With the installed loader, files can only be loaded from the active drive, which
|
|
cannot be changed, and all other drives will remain inert:
|
|
|
|
ldx #<filename
|
|
ldy #>filename
|
|
jsr loadraw; load without decompression
|
|
bcs error; accu contains error code when carry is set after loading
|
|
; unpacked file loaded successfully
|
|
|
|
ldx #<packed
|
|
ldy #>packed
|
|
jsr loadcompd; load with decompression
|
|
bcs error; accu contains error code when carry is set after loading
|
|
; packed file loaded successfully
|
|
|
|
[...]
|
|
|
|
filename:
|
|
.petscii "unpacked"
|
|
.byte 0
|
|
|
|
packed:
|
|
.petscii "packed"
|
|
.byte 0
|
|
|
|
Note that the filename strings are 0-terminated.
|
|
|
|
While loading using filenames with wildcards ("?" and "*") is not possible,
|
|
subsequent files following the previously-loaded file can be loaded via a
|
|
zero-length filename:
|
|
|
|
ldx #<nextfile
|
|
ldy #>nextfile
|
|
jsr loadraw; or loadcompd
|
|
bcs error
|
|
|
|
[...]
|
|
|
|
nextfile:
|
|
.byte 0
|
|
|
|
The first file to be loaded by the loader must be loaded via its actual
|
|
filename. Any subsequent files may then be loaded using the loadnext feature.
|
|
|
|
It is strongly recommended to always check the carry flag for errors and handle
|
|
them.
|
|
|
|
The loader code is completely inert when the loader is idle. Thus, the resident
|
|
code portion can be buffered away, overwritten, and copied back without any
|
|
problems. It is also possible to have different incarnations of the resident
|
|
code portion, such as with and without decompression, or with different
|
|
decompression routines.
|
|
|
|
See loader/samples/minexample for a minimal loader usage example.
|
|
|
|
Refer to loader/include/loader.inc for the definition of the error codes and
|
|
various ca65-syntax convenience macros illustrating usage of the various loader
|
|
calls.
|
|
|
|
|
|
Setting the VIC bank
|
|
--------------------
|
|
The VIC bank may be set by writing to $dd00 at any time after installing the
|
|
loader, including setting it from an interrupt handler while loading. However,
|
|
the upper 6 bits must always be set to 0. This means that no masking of the
|
|
value read from $dd00 must be performed, and setting the VIC bank is as simple
|
|
as
|
|
|
|
lda #vicbank; 4 legal values: $00, $01, $02 and $03
|
|
sta $dd00
|
|
|
|
The 4 values are not inverted, i.e., $03 is the lowmost VIC bank at
|
|
$0000..$3fff, as usual.
|
|
|
|
When $dd00 is buffered and later restored, the value written back must also be
|
|
restricted to 0..3.
|
|
|
|
lda $dd00
|
|
and #$03
|
|
sta dd00buffer
|
|
|
|
[...]
|
|
|
|
lda dd00buffer
|
|
sta $dd00
|
|
|
|
Refer to loader/include/loader.inc for a ca65-syntax convenience macro to set
|
|
the VIC bank.
|
|
|
|
|
|
Zeropage usage
|
|
--------------
|
|
The loader's zeropage variables may be overwritten while it's idle. While
|
|
loading, the addresses from loader_zp_first up to and including loader_zp_last
|
|
as denoted in loadersymbols-c64.inc must not be written to.
|
|
|
|
|
|
Handling disk changes
|
|
---------------------
|
|
Waiting for a specific disk to be inserted is done by enabling the
|
|
FILE_EXISTS_API, then determining whether a specific disk is inserted by
|
|
checking in a loop whether a file that only exists on that disk is found.
|
|
The loop would look like this:
|
|
|
|
retry:
|
|
ldx #<filename
|
|
ldy #>filename
|
|
jsr fileexists
|
|
bcs retry; branch on file not found or error
|
|
; file exists
|
|
|
|
Once the file is found, it (or another file expected on the inserted disk) can
|
|
be loaded.
|
|
|
|
Without the FILE_EXISTS_API, an alternative way to handle disk changes is to try
|
|
to load a file that only exists on that disk in a loop until the file (or its
|
|
first byte) is loaded successfully.
|
|
Thus, it is sufficient to perform
|
|
|
|
retry:
|
|
ldx #<filename
|
|
ldy #>filename
|
|
jsr loadraw; or loadcompd
|
|
bcs retry; branch on error
|
|
; file loaded successfully
|
|
|
|
The detection whether the file exists on the currently inserted disk can be sped
|
|
up by having the file be merely one block big, or by resetting loadaddrhi to 0
|
|
before loading and then polling it for change from an interrupt handler.
|
|
|
|
Refer to loader/samples/turndisk for an example.
|
|
|
|
|
|
Compressing files
|
|
-----------------
|
|
Generally, files need to be compressed (and decompressed) in forward (memory
|
|
address ascending) direction, and the compressed file's load address should be
|
|
set so that the end of the compressed data aligns with the end of its
|
|
uncompressed counterpart, such that in-place decompression can be performed.
|
|
|
|
Refer to the Makefile in loader/ for the "tools" target to build all the
|
|
crunchers:
|
|
|
|
$ make tools
|
|
|
|
Also refer to loader/samples/benchmark/Makefile or loader/samples/test/Makefile
|
|
for the corresponding command line arguments for each of the supported crunchers.
|
|
|
|
|
|
Synchronisation
|
|
---------------
|
|
For linearly loading and executing productions like demos, the general practice
|
|
is to tie events such as loading files (and subsequently executing them) to
|
|
specific music frame counts.
|
|
A music frame usually coincides with a video frame, as the player is called once
|
|
a frame, such that a frame count may be updated with calling the player.
|
|
As loading generally does not take a well-determined period of time to finish
|
|
(there is quite some deviation between different disks, same-model drives, and
|
|
also loads from the same disk using the same drive), it is a good practice to
|
|
sync to a minimum value of the frame count rather than an exact one:
|
|
|
|
wait:
|
|
lda framecountlo
|
|
cmp nextloadlo
|
|
lda framecounthi
|
|
sbc nextloadhi
|
|
bcc wait; branch if framecount < nextload
|
|
; load next file
|
|
|
|
This way, given enough headroom between loads, after a delayed loading-finished
|
|
event, the next file would be loaded immediately, with the chain of events
|
|
eventually catching up with the desired synchronisation.
|
|
|
|
|
|
Dir-art
|
|
-------
|
|
Changing the amount of file blocks in the directory is strongly discouraged, as
|
|
this would disturb speculative loading and either slow down loading or cause
|
|
data to be loaded beyond the file's dedicated memory areas.
|
|
|
|
However, there are a few ways to have an artful directory without interfering
|
|
with the loading scheme:
|
|
|
|
Program files are recognised by the loader regardless of their apparent type, so
|
|
anything else than PRG is allowed, including deleted PRG files.
|
|
|
|
For non-files, set their starting track to 0, such that they would not
|
|
needlessly occupy space in the drive-side directory buffer, and also for the
|
|
loadnext feature to work correctly.
|
|
|
|
In order to only have only the first few characters of filenames be significant
|
|
for identification, set the FILENAME_MAXLENGTH option in the configuration file
|
|
to the amount of desired chars, e.g. 1, such that chars in the directory-listed
|
|
filenames beyond the first are ignored, and loading is effectively by names
|
|
like "1*", "A*", etc.
|
|
|
|
For full freedom with the directory, including manipulating the amount of file
|
|
blocks listed, set DIRTRACK in the configuration file to anything else than 18
|
|
(recommended is 17 or 19 to minimise seek times to and from the shadow directory
|
|
track), such that a shadow directory is used on that track by the loader. This
|
|
shadow directory would list the files normally, but be hidden and separate from
|
|
the normal directory listing, which can then be manipulated freely.
|
|
|
|
Consider using cc1541 in loader/tools/cc1541 for creating disk images with a
|
|
shadow directory.
|
|
|
|
|
|
Optimising for speed
|
|
--------------------
|
|
To get closer to the maximum speed, a few rules can be followed. These include
|
|
but are not limited to:
|
|
|
|
The fewer and the bigger files are required to load a set of data, the faster
|
|
the data is loaded. Thus, it can be beneficial to pad gaps between loaded data,
|
|
or to re-arrange data, and merge files, minimising the number of files loaded.
|
|
|
|
For better decrunch performance while loading, it may also be beneficial to
|
|
have the most compressible data at the beginning and progressively less
|
|
compressible data towards the end of a file.
|
|
|
|
Generally, it's advisable to store sequentially-loaded files on ascending
|
|
tracks rather than alternatingly around the directory track 18.
|
|
|
|
Loading uncompressed files, the loader's native interleave is 4 revolutions for
|
|
tracks 1-17, and 3 revolutions for tracks 18+. Thus, the highest raw throughput
|
|
is obtained on tracks 18-24, then 25-30, then 31+, and finally 1-17.
|
|
|
|
Loading compressed files, the loader's native interleave is 4 revolutions
|
|
across the entire disk. Thus, the highest throughput is obtained on tracks 1-17
|
|
then progressively decreasing with higher track numbers.
|
|
|
|
The loader's speculative loading detects interleaves. However, the expected
|
|
block allocation does not follow the same rules as saving with the drive's
|
|
native DOS.
|
|
|
|
This means that when adding the interleave on the current sector number in order
|
|
to obtain the next one, and there is a wrap (next sector >= number of sectors on
|
|
this track), then the new sector after wrap is a simple modulo on the number of
|
|
sectors the track can hold. I.e.,
|
|
|
|
sector = (sector + interleave) % number_of_sectors[track];
|
|
|
|
The DOS, instead, performs
|
|
|
|
sector += interleave;
|
|
if (sector >= number_of_sectors[track]) {
|
|
sector -= number_of_sectors[track];
|
|
if (sector > 0) {
|
|
--sector;
|
|
}
|
|
}
|
|
|
|
While loading to the RAM under the I/O registers at $d000-$dfff is optionally
|
|
possible, it may slow down loading and increases the size of the resident
|
|
loader portion. Avoiding this option and working around loading to $d000-$dfff
|
|
by copying after loading or decompressing to that range only after loading may
|
|
speed up general loader operation.
|
|
|
|
Consider using cc1541 in loader/tools/cc1541 for creating disk images with
|
|
optimised layouts.
|
|
|
|
|
|
Loading in the background
|
|
-------------------------
|
|
The loader normally loads in the mainline thread. If it shall load within a
|
|
periodically-executed interrupt handler, i.e., in another mainline thread, the
|
|
context must be switched accordingly. That is, stack pointer, registers and
|
|
condition flags must be buffered when switching from the current thread, and the
|
|
context of the thread being switched to must be restored. This can be
|
|
implemented on top of the loader and is orthogonal to its functionality.
|
|
|
|
|
|
Saving files
|
|
------------
|
|
To build the loader with file-save functionality, run, e.g.,
|
|
|
|
$ make save ZP=e8 INSTALL=1000 RESIDENT=cd00 TRANSIENT=2000
|
|
|
|
in loader or loader/src.
|
|
|
|
Additionally to install-c64.prg, loader-c64.prg, and loadersymbols-c64.inc, this
|
|
will produce save-c64.prg in loader/build, assembled to the TRANSIENT memory
|
|
region.
|
|
|
|
Files can be saved like so:
|
|
|
|
ldx #<saveparams
|
|
ldy #>saveparams
|
|
jsr save
|
|
bcs error
|
|
; file was saved successfully
|
|
|
|
[...]
|
|
|
|
saveparams:
|
|
.word filename
|
|
.word $2000; from
|
|
.word $0123; length in bytes
|
|
.word $2000; loadaddress
|
|
.word $8000; drive-code buffer, $0800 bytes to scratch
|
|
|
|
filename:
|
|
.petscii "hiscores"
|
|
.byte 0
|
|
|
|
The file to save must exist on disk prior to saving. I.e., it is overwritten
|
|
in its entirety, replacing the original file.
|
|
|
|
Note that save-c64.prg need not be resident in computer memory, and can be
|
|
loaded, used and then overwritten again.
|
|
|
|
Refer to loader/samples/save for an example.
|
|
|
|
|
|
Limitations
|
|
-----------
|
|
Internally, the loader's drive-side directory buffer stores 16-bit hash values
|
|
of filenames. There is a certain chance of collision, such that two files with
|
|
different filenames may have the same filename hash and thus appear as the same
|
|
file to the loader, and it would "randomly" load either file without reporting
|
|
an error. Using cc1541 in loader/tools/cc1541, hash collisions are detected and
|
|
can then be worked around by renaming files or changing the loader's
|
|
FILENAME_MAXLENGTH configuration setting, which must be reflected with cc1541's
|
|
-M option.
|
|
|
|
There must always be at least 2 file entries in the directory.
|
|
|
|
The final directory block must begin with 00 ff. Some custom disk image creation
|
|
tools produce an erroneous final directory block, such that its first bytes are
|
|
00 00. This is an invalid block length, and the loader rejects blocks with
|
|
invalid track/sector links or block sizes. In the case of an invalid directory
|
|
block, the loader keeps retrying to read it, effectively locking up.
|
|
|
|
As mentioned in the dir-art section, the number of file blocks listed in the
|
|
directory must not be changed, unless having a shadow directory (with the actual
|
|
number of file blocks).
|
|
|
|
If the loader is given extremely little raster time for loading, or even starved
|
|
for multiple video frames, it's adviseable to enable the DISABLE_WATCHDOG option
|
|
in loader/include/loader.inc. This will disengage the watchdog interrupt in the
|
|
drive-side code portion, which is otherwise triggering on protocol breaches and
|
|
would reset the drive on unexpected errors, such as the computer crashing or
|
|
resetting during loading, or cartridge freeze. The rationale is that the drive
|
|
would become accessible normally without the need to power-cycle or manually
|
|
reset it.
|
|
|
|
If not using Bitnax, LZSA2, tinycrunch, TSCrunch or ZX0, compressed files will
|
|
usually go a few bytes beyond their corresponding uncompressed data. This means
|
|
that some bytes directly after the uncompressed files' range in memory are
|
|
overwritten. This can be worked around by not using those areas while loading.
|
|
|
|
However, when crunched files are larger than their uncompressed counterparts,
|
|
data before the uncompressed memory region may be overwritten. To work around
|
|
this, add some padding at the end of the uncompressed files.
|
|
|
|
|
|
License
|
|
-------
|
|
TL;DR: Please give credit, thanks.
|
|
|
|
When using this software or a derivative thereof in own works, please give
|
|
appropriate credit, e.g., "Loader by Krill".
|
|
This does not need to be on prominent display within the actual running
|
|
production itself, but at least in an accompanying note, readme, info file,
|
|
directory, or a similar means of attribution contained in the official release
|
|
archive or disk images.
|
|
Exclusively external credits on websites or online forums are no such instances.
|
|
|
|
Copyright 2022 Gunnar Ruthenberg
|
|
|
|
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 in
|
|
an appropriate form, e.g., "Loader by Krill" displayed anywhere in the actual
|
|
production itself and/or accompanying documentation contained in the official
|
|
release archive or disk images.
|
|
|
|
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.
|
|
|
|
Third-party software (particularly in loader/tools and loader/samples/resources)
|
|
and its derivatives (particularly in loader/src/decompress) are not affected and
|
|
retain their original licenses.
|
|
|
|
|
|
Thanks
|
|
------
|
|
For extensive beta-testing, i thank, in alphabetical order:
|
|
|
|
Adder, Algorithm, ChristopherJam, Dr. Science, Jojeli, Mr Ammo, Nomistake,
|
|
Perplex, q0w, root42, willymanilly.
|
|
|
|
For some extra-extensive testing effort, i thank, again:
|
|
|
|
q0w and Dr. Science.
|
|
|
|
Special thanks to Bitbreaker for coming up with the partial overlap GCR decoding
|
|
trick that has made the 124-cycle read loop possible, and to ChristopherJam aka
|
|
Shrydar, again, for some valuable ideas and input, and the concentric circles
|
|
bitmap.
|