formats index
*** SPY (SPYne archives)
** Document revision 1.1
Created by John Iannetta around 1995, these are mostly found in
Compuserve's CBM applications forum, but do appear in the COMP.BINARIES.CBM
newsgroup as well. It is a self-extracting archive, and does not employ any
compression. The word "SPYNE" does not represent any acronym, but is meant
to represent a "spine", with files linked together like a human spine.
This format has some similarities to LNX archives in that all the files
are simply stored (with no compression), one after the other, and are byte
aligned to take up multiples of 254 bytes (256 on a real 1541). This does
result in a little dead space at the end of each file stored, but its a
small price to pay for how easy it is to construct and break up files on a
real C64/1541.
SPYne files do not contain a BASIC program (like LNX) at the beginning
telling you to "Use XXX to dissolve this file", since SPYne archives are
self-extracting, and are meant to be loaded ",8,1" on a real C64. There is
no specific signature to determine if the file is actually a SPYne. SPYne
archives can store up to 144 files.
Each file inside the archive can have up to 65535 blocks. However, the
archive itself is limited to (best case) 65519 blocks total (65535 minus 15
blocks for extraction code, and a minimum of 1 block for directory entries,
1-8 entries). Worst case for the archive size is 65502 blocks (15 blocks
for extraction and 18 blocks for a directory of 137-144 files).
This many not make sense at first, but how can you load a SPYne file
which exceeds 258 blocks (the maximum memory of the C64)? SPYne files can
be as large as 65535 blocks, and it would seem impossible to work with
files this large. The answer is that the SPYne file loads at $02A7,
trapping the necessary vectors to prevent it from loading the entire
archive, but just the extraction code. Neat trick!
The first 15 blocks (up to offset 3809 or $0EE1) of the file contain the
self-extracting code. It is an auto-running application, with a load
address of $02A7.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
----------------------------------------------- ----------------
0000: A7 02 20 A0 E5 A9 0A 8D 1E D0 A9 16 8D 18 D0 A9 ЩЩ
0010: 04 8D 1F D0 20 44 E5 A9 D8 85 FC A0 00 84 FB A2 D
0020: 04 A9 0D 91 FB C8 D0 FB E6 FC CA D0 F6 20 A5 FF
Starting at offset $0EE2 (3810) is the central directory. This address is
exactly 15 blocks into the file (15*254=3810). Each directory entry is 32
bytes long, with the last two of these being "filler", and unused.
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
----------------------------------------------- ----------------
0EE2: 82 00 00 30 32 2E 44 49 47 49 54 41 4C 20 4D 41 02.DIGITALMA
0EF2: 47 49 43 00 00 00 00 88 18 B5 FF 00 2B 00 00 00 GIC+
0F02: 82 00 00 30 39 2E 2D 2D 2D 3E 20 42 59 20 3C 2D 09.--->BY<-
0F12: 2D 2D 2D 00 00 00 00 00 7A 44 FF 00 2D 00 00 00 ---zD-
0F22: 82 00 00 30 33 2E 2D 3E 20 46 4F 52 43 45 53 20 03.->FORCES
0F32: 3C 2D 2D 00 00 00 00 11 BE 89 FF 00 20 00 00 00 <--
0F42: 82 00 00 30 37 2E 2D 3E 20 4F 46 20 45 56 49 4C 07.->OFEVIL
0F52: 20 3C 2D 00 00 00 00 4F 78 11 FF 00 26 00 00 00 <-Ox&
0F62: 82 00 00 30 34 2E 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 04.----------
0F72: 2D 2D 2D 00 00 00 00 40 58 D2 FF 00 28 00 00 00 ---@X(
0F82: 82 00 00 30 31 2E 20 52 45 4C 45 41 53 45 44 20 01.RELEASED
0F92: 4F 4E A0 00 00 00 00 05 D5 0F FF 00 29 00 00 00 ON)
0FA2: 82 00 00 30 35 2E 20 4A 41 4E 55 41 52 59 20 31 05.JANUARY1
0FB2: 53 54 20 00 00 00 00 F7 FA EC FF 00 24 00 00 00 ST$
0FC2: 82 00 00 30 38 2E 20 20 20 31 39 39 36 A0 A0 A0 08.1996
0FD2: A0 A0 A0 00 00 00 00 39 71 C8 FF 00 36 00 82 00 9q6
0FE2: 00 30 36 2E 20 28 4D 4F 52 45 20 4C 49 4B 45 29 06.(MORELIKE)
0FF2: A0 00 00 00 00 40 33 CF FF 00 32 00 00 00 82 00 @32
1002: 00 31 30 2E 20 28 31 32 2F 32 38 2F 39 35 21 29 10.(12/28/95!)
1012: A0 00 00 00 00 AD 1C 0F FF 00 2B 00 00 00 82 00 +
1022: 00 44 49 47 49 54 41 4C 20 4E 4F 54 45 A0 A0 A0 DIGITALNOTE
1032: A0 00 00 00 00 49 B0 CB 00 00 14 00 00 00 00 00 I
Byte: $00: File type
supported values for this location are:
$81 - SEQ
82 - PRG
83 - USR
01-02: Undefined (00's for now)
03-12: Filename (padded with $A0's)
13-16: Undefined (00's for now)
17-18: File checksum (in low/hi ghbyte format)
19: LSU byte (see "INTRO.TXT" document for description of
"LSU")
1A: Last file marker
FF - more files in archive
00 - last file in archive
1B: Set to $00
1C-1D: File sector count (in low/high byte format)
1E-1F: Not used (and not present on a 254-byte boundary)
File type support is limited to valid files only. No write-protected,
"splat" or DEL files can be archived. Without DEL type support, this means
that many "separator" files in the directory cannot be included. REL files
are partially supported, as only the data portion is copied, and its
filetype is converted to USR format.
The checksum at byte offset 17-18 is a simple 16-bit addition (without
carry) of the entire file, but does not include the directory entry
information.
Looking at the above HEX dump, at address $0FE0 a new filename starts.
This is two bytes earlier than expected. This occurs because the address
$0FE0 is at a "254-byte boundary address", meaning it is divisible by 254,
with no remainder. Only in the directory area is this boundary important,
and when it happens, the last two "filler" bytes in the directory entry
don't exist.
SPYne supports two extraction methods, fast and slow. The fast method is
destructive in that is just breaks up the archive into sections, only works
on a 1541/1571 drive, and has no checksum verification. The slow method
should work on any device, is not destructive to the original archive, and
uses the checksum in the directory entry to verify the data integrity.
File data starts in the next block following the end of the directory.
You only know where the first file's data starts once the directory has
been completely read. Since the extraction code takes up 15 blocks, and
each 8 files take up another block, we can calculate how many blocks into
the archive the actual file data starts.
If we have 11 files, we have two directory blocks (one full with 8
entries, one partial with 3 entries). Therefore the first file's data
starts at block 17 (17*254 = 4318, or $10DE). The second file's starting
position can be calculated by adding the block count of the first file
(multiplied by 254), and adding this to the first files starting position.