Here is the information requested by Timothy Browse in his letter of 13th
August regarding ADFS disc structures.

StartMasSeq and EndMasSeq are there to check whether the directory was
completely written out when it was last written out. For an unbroken
directory they are always equal, and are increased by one (wrapping at 255
back to 0) whenever the directory is updated. This means that if the writing
of the directory was stopped halfway through then the start and end master
sequence numbers will not be the same, and so the directory will then be
identified as broken. Their values should equal each other, but, apart from
that, they can be anything.

DirCheckByte:
This is an accumulation of the used bytes in a directory. The used bytes are
all the bytes excluding the hole between the last directory entry and the
beginning of the structure at the tail of the directory. The generation of
the check byte is best described as an algorithm:
*  Starting at 0 an accumulation process is performed on a number of values.
Whatever the sort of the value (byte or word) it is accumulated in the same
way. Assuming r0 is the accumulation register and r1 the value to accumulate
this is the accumulation performed:
 EOR r0, r1, r0, ROR #13
*  All the whole words at the start of the directory are accumulated. This
will leave a number of bytes (0 to 3) in the last directory entry (or at the
end of the start structure in a directory if it's empty).
*  The last few bytes at the start of the directory are accumulated
individually.
*  The first few bytes at the beginning of the end structure of the
directory are accumulated. This is done to leave only a whole number of
words left in the directory to be accumulated.
*  The last whole words in the directory are accumulated, except the very
last word which is excluded as it contains the check byte.
*  The accumulated word has its four bytes exclusive-ored (EOR) together.
This value is the check byte.

The above description was written down from reading the FileCore code, so I
don't know whether a routine following this description will generate a
correct check byte.

Booting discs:
FileCore has the concept of Floppies and Winnies. The way each of these
boots is different. A floppy boots by reading the first sector on the disc.
The first 64 bytes of this are the disc record (with extra padding), which
is followed by the allocated space map. A winnie boots by reading sectors 12
and 13. These start off with the disc record and so, from this, FileCore
manages to find the real location of the map, but does not necessarily try
to access it yet.

In answer to Tim's question, the approved way to find the map is
<FS>_DescribeDisc, which, if it does not work on a Winnie, means the map is
hard to find automatically. As an alternative to this, when it fails, a disc
fixer program may choose to hunt for correctly formed map sectors somewhere
round the middle of the disc, and so hunt out the real map. Once a map has
been sussed like this (and checked that it matches the shape of the disc
etc) the disc fixer can attempt to fix the boot block, or siphon off the
information on the disc to somewhere safer. <FS>_DescribeDisc is slightly
more future proof than booting the disc 'manually' using <FS>_DiscOp to read
the boot block, but, having said that, the ADFS D and E disc formats are
pretty much cast in stone and are unlikely to change such that <FS>_DiscOp
doesn't work, <FS>_DescribeDisc does work, and an ADFS D/E format disc fixer
can still fix the disc, so, do it whichever way you feel best works for your
program. (Don't, whatever you do, try driving the hardware directly, as
there are enough interfaces provided (<FS>_DiscOp) and doing this is
definitely NOT future proof)

ZoneCheck:
This, as described previously, is a check byte on a given zone sector. It's
calculation is best described in assembler, but I have also coded one up in
C. Here are both those code fragments:

unsigned char map_zone_valid_byte
(
        void const * const map,
        disc_record const * const discrec,
        unsigned int zone
)
{
        unsigned char const * const map_base = map;
        unsigned int sum_vector0;
        unsigned int sum_vector1;
        unsigned int sum_vector2;
        unsigned int sum_vector3;
        unsigned int zone_start;
        unsigned int rover;

        sum_vector0 = 0;
        sum_vector1 = 0;
        sum_vector2 = 0;
        sum_vector3 = 0;

        zone_start = zone<<discrec->log2_sector_size;
        for ( rover = ((zone+1)<<discrec->log2_sector_size)-4; rover > zone_start; rover-=4 )
        {
                sum_vector0 += map_base[rover+0] + (sum_vector3>>8);
                sum_vector3 &= 0xff;
                sum_vector1 += map_base[rover+1] + (sum_vector0>>8);
                sum_vector0 &= 0xff;
                sum_vector2 += map_base[rover+2] + (sum_vector1>>8);
                sum_vector1 &= 0xff;
                sum_vector3 += map_base[rover+3] + (sum_vector2>>8);
                sum_vector2 &= 0xff;
        }

        /*
                Don't add the check byte when calculating its value
        */
        sum_vector0 +=                     (sum_vector3>>8);
        sum_vector1 += map_base[rover+1] + (sum_vector0>>8);
        sum_vector2 += map_base[rover+2] + (sum_vector1>>8);
        sum_vector3 += map_base[rover+3] + (sum_vector2>>8);

        return (unsigned char)((sum_vector0^sum_vector1^sum_vector2^sum_vector3) & 0xff);
}

; ========
; NewCheck
; ========

;entry
; R0 -> start
; R1 length ( must be multiple of 32 )

;exit
; LR check byte, Z=0 <=> good

NewCheck ROUT
 Push   "R1-R9,LR"
 MOV    LR, #0
 ADDS   R1, R1, R0              ;C=0
05                              ;loop optimised as winnies may have many zones
 LDMDB  R1!,{R2-R9}
 ADCS   LR, LR, R9
 ADCS   LR, LR, R8
 ADCS   LR, LR, R7
 ADCS   LR, LR, R6
 ADCS   LR, LR, R5
 ADCS   LR, LR, R4
 ADCS   LR, LR, R3
 ADCS   LR, LR, R2
 TEQS   R1, R0          ;preserves C
 BNE    %BT05
 AND    R2, R2, #&FF    ;ignore old sum
 SUB    LR, LR, R2
 EOR    LR, LR, LR, LSR #16
 EOR    LR, LR, LR, LSR #8
 AND    LR, LR, #&FF
 CMPS   R2, LR
 Pull   "R1-R9,PC"


CrossCheck:
These bytes provide a means to check that the set of zones match each other.
To check the set matches, these bytes are exclusive-ored (EOR) with each
other and the answer must be &FF (0xff). They are modified whenever more
than one zone map is modified (the algorithm is not important, just so long
as the bytes of the changed maps change and that the EOR of all these bytes
remains at &FF).

Check0 and Check1:
These are checksums of the previous bytes in the map. They are calculated
using repeated 8-bit ADCSs on the bytes if the relevant map block, starting
with a value 0:
If r0 is the accumulated checksum, then it starts at 0, and each byte is
added as follows:
 ADC    r0, r0, r1              ; r1 is the byte picked up
 MOVS   r0, r0, LSL #24         ; Shifts bit 8 into the carry bit
 MOV    r0, r0, LSR #24         ; not MOVS to preserve the carry bit
Note that the check byte itself isn't included in the checksum, its value
equals the checksum of the previous bytes.

