; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; >Identify

        TTL     "Identifying FileCore discs"

; change log:

; 15 May 1997: SBP: Changed SanityCheckEDiscRecord to support idlen up to 16.


; ============
; IdentifyDisc (r0,r5->r0,r6)
; ============
;
; Attempt to identify the specified disc
;
;entry
;  r0 = drive number of drive which disc to be identified is in
;  r5 -> disc record of disc to be identified
;
; exit
;  r0 = FileType of disc image (FileType_Data if service call failed)
;  r6 = readsectors cache
;  V not possible
;
IdentifyDisc ROUT
        Push    "r1,r2,r3,r8,r9,lr"

 [ DebugL
        DREG    R0, "Identifying drive ",cc
        DREG    R5, " on record at "
 ]

        ; Ensure an empty read sectors cache
        MOV     r6, #0

        ; If LastDiscOpWasFormat then disc must be a data disc
        DrvRecPtr lr, r0
        LDRB    lr, [lr, #DrvFlags]
        TST     lr, #LastDiscOpWasFormat
        MOVNE   r1, #Service_IdentifyDisc
        BNE     %FT50

        LDR     r9, [r5, #DiscRecord_Root]
        AND     r9, r9, #DiscBits

 [ DebugL
        DREG    r9, "Rootdir internally starts as: "
 ]

        ; Generate an initial root dir address
        MOV     r1, r0, ASL #(32-3)
        EOR     r1, r1, #bit31          ; Convert to external numbering
 [ DebugL
        DREG    r1, "Setting initial root dir address to: "
 ]
        STR     r1, [r5, #DiscRecord_Root]

        MOV     r1, #Service_IdentifyDisc
        MOV     r2, #0                  ; buffer
        MOV     r3, #0                  ; buffer size
        LDR     r8, PrivateWord

        BL      DoXOS_ServiceCall

        ; Fake unserviced service on VS result
        MOVVS   r1, #Service_IdentifyDisc
        CLRV

        LDR     lr, [r5, #DiscRecord_Root]
        BIC     lr, lr, #DiscBits
        ORR     lr, lr, r9
        STR     lr, [r5, #DiscRecord_Root]

 [ DebugL
        DREG    lr, "Restoring internal disc numbering of RootDir: "
 ]

50
        ; If not serviced then disc is just so much random data!
        TEQ     r1, #Service_Serviced
        ; Clear out DiscId and DiscName if mapping to FileType_Data
        MOVNE   r0, #0
        ASSERT  DiscRecord_DiscId :MOD: 4 = 0
        ASSERT  DiscRecord_DiscName = DiscRecord_DiscId+2
        STRNE   r0, [r5, #DiscRecord_DiscId]
        LDRNE   r0, =FileType_Data
        MOVEQ   r0, r2

 [ DebugL
        DREG    R0, "Disc identified as type "
 ]

95
        Pull    "r1,r2,r3,r8,r9,pc"

        LTORG

; ====================
; IdentifyFileCoreDisc
; ====================
;
;entry
;  R1 = Service_IdentifyDisc (&69)
;  r2 = pointer to buffer for text
;  r3 = length of buffer
;  R5 = Pointer to disc record
;  R6 = Sector cache handle
;  R8 = Pointer to FileCore instance private word to use
;
;exit
;If the format has been identified:
;  R1 = Service_Serviced
;  R2 = Filetype number for given disc format.
;  R5 = Pointer to disc record, which has been modified
;  R6 = New sector cache handle
;  R8 unchanged
;Otherwise:
;  R1 = Service_IdentifyDisc
;  R5 = Pointer to disc record, which may have been modified
;  R6 = New sector cache handle
;  R8 unchanged

IdentifyFileCoreDisc ROUT
        Push    "lr"
 [ DebugL
        DLINE   "Identifying a disc"
        DREG    R0, "Rin:",cc
        DREG    R1, ",",cc
        DREG    R2, ",",cc
        DREG    R3, ",",cc
        DREG    R4, ",",cc
        DREG    R5, ",",cc
        DREG    R6, ",",cc
        DREG    R7, ",",cc
        DREG    R8, ",",cc
        DREG    R9, ",",cc
        DREG    R10, ",",cc
        DREG    R11, ",",cc
        MOV     R12, R13
        DREG    R12, ","
 ]
        MOV     r12, r8
 [ DebugL
        DREG    r8, "r8 in is "
        DREG    r12, "Meaning ws is "
        Push    "r0"
        LDRB    r0, ReEntrance
        DREG    r0, "ReEntrance into service call servicing:"
        Pull    "r0"
 ]
        BL      IdentifyFileCoreFloppyDisc
        TEQ     r1, #Service_Serviced
        BLNE    IdentifyFileCoreHardDisc
 [ DebugL
        Push    "r0"
        LDRB    r0, ReEntrance
        DREG    r0, "ReEntrance out of service call servicing:"
        Pull    "r0"
        TEQ     r1, #Service_Serviced
        BNE     %FT01
        DREG    r2, "Service claimed - disc type is "
01
        DREG    R0, "Rot:",cc
        DREG    R1, ",",cc
        DREG    R2, ",",cc
        DREG    R3, ",",cc
        DREG    R4, ",",cc
        DREG    R5, ",",cc
        DREG    R6, ",",cc
        DREG    R7, ",",cc
        DREG    R8, ",",cc
        DREG    R9, ",",cc
        DREG    R10, ",",cc
        DREG    R11, ",",cc
        MOV     R12,R13
        DREG    R12, ","
 ]
        Pull    "pc"

        LTORG

; ==========================
; IdentifyFileCoreFloppyDisc
; ==========================
;entry
;  r1 = Service_IdentifyDisc (&69)
;  r5 = Pointer to disc record
;  r6 = Sector cache handle
;  r8 = Pointer to FileCore instance private word to use
;  r12 = Pointer to FileCore instance private word to use
;
;exit
;If the format has been identified:
;  r1 = Service_Serviced
;  r2 = Filetype number for given disc format.
;  r5 = Pointer to disc record, which has been modified
;  r6 = New sector cache handle
;  r8 unchanged
;  r12 unchanged
;Otherwise:
;  r1 = Service_IdentifyDisc
;  r5 = Pointer to disc record, which may have been modified
;  r6 = New sector cache handle
;  r8 unchanged
;  r12 unchanged

IdentifyFileCoreFloppyDisc ROUT
        Push    "r0,r1,r2,r3,r8,r12,lr"

 [ DebugL
        DLINE   "Identifying a floppy disc"
 ]

        ; Don't even try to identify hard discs as floppy discs.
        LDR     lr, [r5, #DiscRecord_Root]
        TST     lr, #bit31
        BNE     %FT95

        ; Sector numbers must start at 0 on a track
        LDRB    r0, [r5, #DiscRecord_LowSector]
        ASSERT  DiscRecord_LowSector_Shift = 0
        AND     lr, r0, #DiscRecord_LowSector_Mask
        TEQ     lr, #0
 [ DebugL
        BEQ     %FT01
        DLINE   "Failed on sector numbering"
01
 ]
        BNE     %FT95

        ; Must be two heads in reality, and turn off side sequencing and double stepping
        LDRB    r0, [r5, #DiscRecord_LowSector]
        BIC     r0, r0, #DiscRecord_DoubleStep_Flag
        STRB    r0, [r5, #DiscRecord_LowSector]
        BL      EnsureInterleavedSides

        ; Ensure 1(RAMFs) or 2(Others) heads on floppies
        TEQ     r0, #1
        TEQNE   r0, #2
 [ DebugL
        DREG    r0,"Heads="
        BEQ     %FT01
        DLINE   "Failed on number of heads"
01
 ]
        BNE     %FT95

        Push    "R3"
        ; Check if it sanity checks as an E format floppy disc
 [ BigDisc
        MOV     r2, #0  ; sector zero
        MOV     r3, #4  ; byte offset 4
 |
        MOV     r2, #4  ; byte disc address 4
 ]
        BL      SanityCheckEFormat
        Pull    "R3"
        BVS     %FT20

 [ DebugL
        DLINE   "It must be E format!"
 ]

        LDR     r2, [sp, #2*4]
 [ BigDir
        LDR     lr, [r5, #DiscRecord_BigDir_DiscVersion]   ; check if it's EX or FX
        TEQ     lr, #0
        BEQ     %FT15

        LDRB    lr, [r5, #DiscRecord_Density]

        TEQ     lr, #DensityQuad
        baddr   r1, EXFormat_DescribeTag, NE
        baddr   r1, FXFormat_DescribeTag, EQ
        BL      CopyFormatName

        B       %FT85

15
 ]

        LDRB    lr, [r5, #DiscRecord_Density]

        TEQ     lr, #DensityQuad
        baddr   r1, EFormat_DescribeTag, NE
        baddr   r1, FFormat_DescribeTag, EQ
        BL      CopyFormatName

        B       %FT85

20
        ; Turn side sequencing on for FileCore L format floppies
        BL      EnsureSequencedSides

        ; Side sequencing only works with a correct disc size...
        ADD     r0, r0, #1              ; Number of platters
        LDRB    r1, [r5, #DiscRecord_Log2SectorSize]
        MOV     r0, r0, ASL r1          ; Size of a cylinder-sector
        LDRB    r1, [r5, #DiscRecord_SecsPerTrk]
        MUL     r0, r1, r0              ; Size of a cylinder
        MOV     r0, r0, ASL #4          ; *16
        ADD     r0, r0, r0, ASL #2      ; *5   =*80
        STR     r0, [r5, #DiscRecord_DiscSize]
 [ BigDisc
        MOV     r0, #0
        STR     r0, [r5, #DiscRecord_BigMap_DiscSize2]
   [ BigShare
        STR     r0, [r5, #DiscRecord_BigMap_ShareSize]    ; sharing unit always 0 for floppies
   ]
 ]

        ; Check for L format (much more strict about what's OK)
        BL      SanityCheckLFormat
        BVS     %FT30
 [ BigDisc
        LDR     r0, [r5, #DiscRecord_BigMap_DiscSize2]
        MOVS    r0, r0
        BNE     %FT30                           ; can't be L format - far too big!
 ]
        LDR     r0, [r5, #DiscRecord_DiscSize]
        LDR     lr, =L_Size + 4*32*256          ; Allow up to 4 extra tracks worth - beyond that and a frigged format won't work
        CMP     r0, lr

        LDRLS   r2, [sp, #2*4]
        baddr   r1, LFormat_DescribeTag, LS
        BLLS    CopyFormatName

        BLS     %FT85

30
        ; Turn side interleaving on for checking FileCore D format floppies
        BL      EnsureInterleavedSides

        ; Check for D (old map) format disc
        BL      SanityCheckDFormat
        BVS     %FT85
 [ BigDisc
        LDR     r0, [r5, #DiscRecord_BigMap_DiscSize2]
        MOVS    r0, r0
        BNE     %FT75                           ; too big
 ]
        LDR     r0, [r5, #DiscRecord_DiscSize]
        LDR     lr, =D_Size + 4*10*1024         ; Allow up to 4 extra tracks worth - beyond that and a frigged format won't work
        CMP     r0, lr

        LDRLS   r2, [sp, #2*4]
        baddr   r1, DFormat_DescribeTag, LS
        BLLS    CopyFormatName

75
        SETV    HI

85
        BVS     %FT95
 [ DebugL
        DLINE   "Its a floppy!"
 ]
        LDR     r2, =FileType_FileCoreFloppyDisc
        STR     r2, [sp, #2*4]          ; Returned disc type
        MOV     r0, #Service_Serviced
        STR     r0, [sp, #1*4]

95
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check FileCore floppy disc"
01
 ]
        CLRV
        Pull    "r0,r1,r2,r3,r8,r12,pc"

        LTORG

; ========================
; IdentifyFileCoreHardDisc
; ========================
;entry
;  r1 = Service_IdentifyDisc (&69)
;  r5 = Pointer to disc record
;  r6 = Sector cache handle
;  r8 = Pointer to FileCore instance private word to use
;  r12 = Pointer to FileCore instance private word to use
;
;exit
;If the format has been identified:
;  r1 = Service_Serviced
;  r2 = Filetype number for given disc format.
;  r5 = Pointer to disc record, which has been modified
;  r6 = New sector cache handle
;  r8 unchanged
;  r12 unchanged
;Otherwise:
;  r1 = Service_IdentifyDisc
;  r5 = Pointer to disc record, which may have been modified
;  r6 = New sector cache handle
;  r8 unchanged
;  r12 unchanged

IdentifyFileCoreHardDisc ROUT
        Push    "r0,r2,r3,r4,r5,r7-r12,lr"
 [ DebugL
        DLINE   "Identifying a hard disc"
 ]

        ; Convert disc record to non-sequenced sides
        BL      EnsureInterleavedSides

        BL      SanityCheckBadBlockList
        BVS     %FT90
 [ BigDisc
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]            ; for conversion from byte to sector
        MOV     r3, #DefectListDiscAdd          ; disc addr of boot block...
        MOV     r2, r3, LSR lr                  ; ...in sector form
        SUB     r3, r3, r2, LSL lr              ;
        ADD     r3, r3, #DefectStruc            ; offset of disc record
 |
        LDR     r2, =DefectListDiscAdd + DefectStruc
 ]
        BL      SanityCheckEFormat
        LDRVC   r2, [sp, #1*4]
 [ BigDir
        BVS     %FT01
        LDR     lr, [r5, #DiscRecord_BigDir_DiscVersion]
        TEQS    lr, #0
        baddr   r1, FFormat_DescribeTag, EQ
        baddr   r1, FXFormat_DescribeTag, NE
01
 |
        baddr   r1, FFormat_DescribeTag, VC
 ]
        BLVC    CopyFormatName
        LDRVC   r2, =FileType_FileCoreHardDisc
        BVC     %FT90
        BL      SanityCheckDFormat
        LDRVC   r2, [sp, #1*4]
        baddr   r1, DFormat_DescribeTag, VC
        BLVC    CopyFormatName
        LDRVC   r2, =FileType_FileCoreHardDisc
90
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check FileCore hard disc"
        B       %FT02
01
        DLINE   "It's a hard disc!"
02
 ]
        STRVC   r2, [sp, #1*4]
        MOVVC   r1, #Service_Serviced
        CLRV
        Pull    "r0,r2,r3,r4,r5,r7-r12,pc"

        LTORG

; ==============
; CopyFormatName
; ==============
;
; entry
;  r1 = pointer to tag
;  r2 = buffer (0 if no buffer)
;  r3 = buffer length
;
; exit
;  flags etc preserved
CopyFormatName ROUT
        Push    "r0-r8,lr"
        SavePSR r8
        TEQ     r2, #0
        BEQ     %FT95

        SUB     sp, sp, #16
        MOV     r0, sp
        ADRL    r1, message_filename
        MOV     r2, #0
        SWI     XMessageTrans_OpenFile
        BVS     %FT90

        MOV     r0, sp
        ADD     lr, sp, #16+1*4
        LDMIA   lr, {r1-r3}
        MOV     r4, #0
        MOV     r5, #0
        MOV     r6, #0
        MOV     r7, #0
        SWI     XMessageTrans_Lookup

        MOV     r0, sp
        SWI     XMessageTrans_CloseFile

90
        ADD     sp, sp, #16
95
        RestPSR r8,,f
        Pull    "r0-r8,pc"

; =======================
; SanityCheckBadBlockList
; =======================
;
; entry
;  r5 = pointer to disc record
;  r6 = sector cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; exit
;  r6 = new sector cache handle
;  r12 = Unchanged
;  VC if OK
;  VS if bad
;
SanityCheckBadBlockList ROUT
 [ {TRUE}
        CLRV
        MOV     pc,lr
 |
        Push    "r0-r4,r5,r7-r12,lr"
        SUB     sp, sp, #SzDefectList

 [ DebugL
        DLINE   "Sanity checking bad block list"
 ]

 [ BigDisc :LAND: {FALSE}
        LDR     r2, [r5, #DiscRecord_Root]              ; to get drive number
        AND     r2, r2, #DiscBits
        MOV     r3, #DefectListDiscAdd          ; disc addr of defect list
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        ORR     r2, r2, r3, LSR lr              ; actual disc addr to read from
        MOV     r4, r3, LSR lr                  ; sector align disc addr
        SUB     r3, r3, r4, LSL lr              ; extra transfer needed
        ADD     r4, r3, #SzDefectList           ; amount to transfer
        SUB     sp, sp, r3                      ; transfer addr
        Push    "R3"
        MOV     r3, sp
        BL      SWI_SectorDiscOp_CachedReadSectors_Myself
        Pull    "R3"
        ADD     sp, sp, r3
 |
        LDR     r2, [r5, #DiscRecord_Root]
        AND     r2, r2, #DiscBits
        ORR     r2, r2, #DefectListDiscAdd
        MOV     r3, sp
        MOV     r4, #SzDefectList
        BL      SWI_DiscOp_CachedReadSectors_Myself
 ]
 [ DebugL
        BVC     %FT01
        ADD     r1, r0, #4
        DSTRING r1, "Failed on reading:"
01
 ]
        BVS     %FT90

        MOV     r0, sp
        MOV     r1, #SzDefectList
        BL      CheckSum
 [ DebugL
        BVC     %FT01
        DLINE   "Failed on check sum"
01
 ]

90
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check bad block list"
01
 ]
        ADD     sp, sp, #SzDefectList
        Pull    "r0-r4,r7-r12,pc"
 ]

        LTORG

; ==================
; SanityCheckEFormat
; ==================
;
; entry
 [ BigDisc
;  r2 = disc address of sector containing boot record
;  r3 = offset within sector of boot record (word aligned)
 |
;  r2 = disc address of boot record (without disc number)
 ]
;  r5 = pointer to disc record
;  r6 = sector cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; exit
;   r6 = new sector cache handle
;   r12 = unchanged
;  AND
;   VC: Matches OK
;   VS: Mismatch and r0 does _not_ point to an error
;
SanityCheckEFormat ROUT
        Push    "lr"

 [ DebugL
        DLINE   "Sanity checking E format"
   [ BigDisc
        DREG    r2, "Boot record sector="
        DREG    r3, "Boot record offset="
   ]
 ]

        ; Sanity check disc record vs disc
        BL      SanityCheckEDiscRecord

        ; Read and check map from disc
        BLVC    SanityCheckNewMap

95
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check E format"
        B       %FT02
01
        DLINE   "E format is the business!"
02
 ]
        Pull    "pc"

        LTORG

; ======================
; SanityCheckEDiscRecord
; ======================
;
; entry
 [ BigDisc
;  r2 = disc address of sector containing boot record
;  r3 = offset within sector of boot record (word aligned)
 |
;  r2 = disc address of boot record (without disc number)
 ]
;  r5 = pointer to disc record
;  r6 = sector cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; exit
;   r6 = new sector cache handle
;   r12 = unchanged
;  AND
;   VC: Matches OK
;   VS: Mismatch and r0 does _not_ point to an error
;
SanityCheckEDiscRecord ROUT
        Push    "r0,r1,r2,r3,r4,r5,r7,r8,r9,r10,r11,r12,lr"
 [ BigDisc
        SUB     sp, sp, #SzDiscRecSig2
        SUB     sp, sp, r3              ; adjust for disc record offset
 |
        SUB     sp, sp, #SzDiscRecSig
 ]

 [ DebugL
        DLINE   "Sanity checking E disc record"
        DREG    r2, "Disc addr ="
        DREG    r3, "Offset ="
        DREG    sp, "SP ="
 ]

        MOV     r1, #DiscOp_CachedReadSecs
        LDR     r14, [r5, #DiscRecord_Root]
        AND     r14, r14, #DiscBits
        ORR     r2, r2, r14

 [ BigDisc
        MOV     r4, #SzDiscRecSig2      ; length we want
        ADD     r4, r4, r3              ; actual transfer length to compensate for offset
        Push    "R3"
        ADD     r3, sp, #4
        BL      SWI_SectorDiscOp_CachedReadSectors_Myself
        Pull    "R3"
        ADD     sp, sp, r3      ; pointer to disc record actual data
 |
        MOV     r3, sp
        MOV     r4, #SzDiscRecSig
        BL      SWI_DiscOp_CachedReadSectors_Myself
 ]

 [ DebugL
        BVC     %FT01
        ADD     r1, r0, #4
        DSTRING r1, "Failed on reading"
01
 ]
        BVS     %FT90

        ; Sanity check disc record vs disc

        ; Check sectorsize, secpertrk, heads and density all match the real values
        ASSERT  DiscRecord_Log2SectorSize :MOD: 4 = 0
        ASSERT  DiscRecord_SecsPerTrk = DiscRecord_Log2SectorSize+1
        ASSERT  DiscRecord_Heads = DiscRecord_SecsPerTrk+1
        ASSERT  DiscRecord_Density = DiscRecord_Heads+1
        LDR     r0, [r5, #DiscRecord_Log2SectorSize]
        LDR     r1, [sp, #DiscRecord_Log2SectorSize]

        ; On floppy discs ignore SecsPerTrk to robustify against copy-protection schemes
        LDR     lr, [r5, #DiscRecord_Root]
        TST     lr, #bit31
        EOR     lr, r0, r1
        BICNE   lr, lr, #&0000ff00
        TEQ     lr, #0

 [ DebugL
        BEQ     %FT01
        DREG    r0,"Failed on mismatched sector sizes etc: Rec=",cc
        DREG    r1, " boot="
01
 ]
        BNE     %FT85

 [ BigMaps
        ; Check 0 < idlen < 20
        LDRB    r0, [sp, #DiscRecord_IdLen]
        CMP     r0, #19

 [ DebugL
        BLS     %FT01
        DLINE   "Failed on idlen >= 20"
01
 ]

 |
        ; Check 0 < idlen < 16
        LDRB    r0, [sp, #DiscRecord_IdLen]
        CMP     r0, #15
 [ DebugL
        BLS     %FT01
        DLINE   "Failed on idlen >= 16"
01
 ]

 ]
        BHI     %FT85
        CMP     r0, #0
 [ DebugL
        BNE     %FT01
        DLINE   "Failed on idlen = 0"
01
 ]
        BEQ     %FT85

        ; Check sufficient link bits for max number of fragments
        LDRB    r0, [sp, #DiscRecord_Log2SectorSize]
        MOV     r1, #8                  ; 8 bits per byte
        MOV     r1, r1, ASL r0          ; bits per sector
        ASSERT  DiscRecord_ZoneSpare :MOD: 4 = 2
        LDR     r0, [sp, #DiscRecord_ZoneSpare - 2]
        SUB     r1, r1, r0, LSR #16     ; map bits per sector
        LDRB    r0, [sp, #DiscRecord_IdLen]
        ADD     r0, r0, #1
        DivRem  r2, r1, r0, lr          ; r2 = max objects per zone
 [ BigMaps
        LDRB    r0, [sp, #DiscRecord_NZones]
        LDRB    r1, [sp, #DiscRecord_BigMap_NZones2]    ; r1 is the target for the MUL below
        ADD     r0, r0, r1, LSL #8
 |
        LDRB    r0, [sp, #DiscRecord_NZones]
 ]
 [ DebugL
        DREG    r0, "zones: "
 ]
        MUL     r1, r2, r0              ; r1 = max objects in map
        LDRB    r0, [sp, #DiscRecord_IdLen]
 [ DebugL
        DREG    r0, "idlen: "
 ]
        MOVS    r14, r1, LSR r0
 [ DebugL
        BEQ     %FT01
        DLINE   "Failed on 2^idlen < Max Total objects"
01
 ]
        BNE     %FT85                   ; If shifting right by idlen leaves a non-0
                                        ; answer, then there aren't enough link bits!

        ; Check sufficient link bits for free space list in a sector
        LDRB    r0, [sp, #DiscRecord_Log2SectorSize]
        ADD     r0, r0, #3              ; 2^3 = 8 = bits per byte
        LDRB    r2, [sp, #DiscRecord_IdLen]

 [ BigMaps
        ; if BigMaps then idlen can be more than MaxFreeLink, so test against
        ; MaxFreeLink (we're being paranoid here, since maximum sector is 1024
        ; bytes, needing 13 link bits)

        CMP     r2, #MaxFreeLinkBits
        MOVHI   r2, #MaxFreeLinkBits
 ]

        CMP     r2, r0
 [ DebugL
        BHS     %FT01
        DLINE   "Failed on idlen < Zone length in bits"
01
 ]
        BLO     %FT85                   ; Definitely not enough

        ; Check DiscRecord_Skew not being silly
        LDRB    r0, [sp, #DiscRecord_Skew]
        LDRB    r2, [sp, #DiscRecord_SecsPerTrk]
        CMP     r0, r2
 [ DebugL
        BLO     %FT01
        DLINE   "Failed on DiscRecord_Skew >= SecsPerTrk"
01
 ]
        BHS     %FT85                   ; skew >= secspertrk is silly!

 [ BigShare
        ; Check RootDir is sensible:
        ; Must be &000002nn where nn=(2*Zones)>>ShareSize+1
 [ BigMaps

        LDRB    r1, [sp, #DiscRecord_IdLen]
        CMP     r1, #15
        BHI     %FT10

        LDRB    r1, [sp, #DiscRecord_BigDir_DiscVersion]
        TEQS    r1, #0
        BNE     %FT10                   ; don't sanity check rootdir address


 ]

 [ BigMaps
        LDRB    r1, [sp, #DiscRecord_NZones]
        LDRB    r0, [sp, #DiscRecord_BigMap_NZones2]    ; r0 due for corruption later
        ADD     r1, r1, r0,LSL #8
 |
        LDRB    r1, [sp, #DiscRecord_NZones]
 ]
        MOV     r1, r1, ASL #1
        LDRB    r0, [sp, #DiscRecord_BigMap_ShareSize]  ; factor in sharesize
        MOV     lr, #1
        ADD     r1, r1, lr, LSL r0
        SUB     r1, r1, #1
        MOV     r1, r1, LSR r0
        ADD     r1, r1, #1
        ADD     r1, r1, #&200
        LDR     r0, [sp, #DiscRecord_Root]
        TEQ     r0, r1
 |
        ; Check RootDir is sensible: must be &000002nn
        ; where nn = (2*Zones) + 1
 [ BigMaps
        LDRB    r1, [sp, #DiscRecord_NZones]
        LDRB    r0, [sp, #DiscRecord_BigMap_NZones2]    ; r0 due to be corrupted
        ADD     r1, r1, r0, LSL #8
 |
        LDRB    r1, [sp, #DiscRecord_NZones]
 ]
        MOV     r1, r1, ASL #1
        ADD     r1, r1, #1
        ADD     r1, r1, #&200
        LDR     r0, [sp, #DiscRecord_Root]
        TEQ     r0, r1
 ]
 [ DebugL
        BEQ     %FT01
        DREG    r1, "Failed on RootDir != "
01
 ]
        BEQ     %FT10

        ; or, another last ditch attempt if Zones=1 and density=0
        ; where nn = ((bootblock + bootblocksize + (1<<DiscRecord_Log2SectorSize) - 1) >> DiscRecord_Log2SectorSize) + 1
 [ BigMaps
        LDRB    r1, [sp, #DiscRecord_NZones]
        LDRB    r14, [sp, #DiscRecord_BigMap_NZones2]
        ORR     r1, r1, r14, LSL #8
 |
        LDRB    r1, [sp, #DiscRecord_NZones]
 ]
        LDRB    r14, [sp, #DiscRecord_Density]
        ORR     r14, r1, r14, LSL #16
        TEQ     r14, #1                                 ; EQ iff density=0 zones=1
        BNE     %FT85

 [ DebugL
        DLINE   "Might be a 1 zone fixed disc"
 ]
        MOV     r14, #1
        LDRB    r1, [sp, #DiscRecord_Log2SectorSize]
        MOV     r14, r14, ASL r1
        SUB     r14, r14, #1
        ADD     r14, r14, #DefectListDiscAdd + SzDefectList
        MOV     r14, r14, LSR r1
        ADD     r14, r14, #1
        ORR     r14, r14, #&200
        TEQ     r0, r14
 [ DebugL
        BEQ     %FT01
        DREG    r1, "Failed on RootDir != "
01
 ]
        BNE     %FT85

10

        ; Check disc size:
        ; Must be > ((Zones-1)*(8<<DiscRecord_Log2SectorSize-ZoneSpare)-Zone0Bits)<<BitSize
 [ {FALSE}
        ; (this check should not be made as may be partitioned for UNIX)
        ; Must be < 512M (due to duff DiscOp interface)
 ]
; check for Big disc on Small filing system
 [ BigDisc
        LDRB    r1, [sp, #DiscRecord_BigMap_Flags]      ; check if its a big disc
        TSTS    r1, #DiscRecord_BigMap_BigFlag
        BEQ     %FT01
        LDR     r1, [r12]
        LDR     r1, [r1,#:INDEX:FS_Flags]
        TST     r1, #CreateFlag_BigDiscSupport
        BEQ     %FT85                   ; small filing system, big disc

01      ; not big or big on big filing system
 ]
        MOV     r0, #8
        LDRB    r1, [sp, #DiscRecord_Log2SectorSize]
        MOV     r0, r0, ASL r1
        ASSERT  DiscRecord_ZoneSpare :MOD: 4 = 2
        LDR     r1, [sp, #DiscRecord_ZoneSpare - 2]
        SUB     r0, r0, r1, LSR #16
 [ BigMaps
        LDRB    r1, [sp, #DiscRecord_NZones]
        LDRB    lr, [sp, #DiscRecord_BigMap_NZones2]    ; lr is safe to use
        ADD     r1, r1, lr, LSL #8
 |
        LDRB    r1, [sp, #DiscRecord_NZones]
 ]
        SUB     r1, r1, #1
        MUL     r0, r1, r0
        SUB     r0, r0, #Zone0Bits
        LDRB    r1, [sp, #DiscRecord_Log2bpmb]
 [ BigDisc
; first, get the comparison size in sectors
        LDRB    lr, [sp, #DiscRecord_Log2SectorSize]
        SUBS    r1, r1, lr
        MOVPL   r0, r0, LSL r1
        RSBMI   r0, r0, #0
        MOVMI   r0, r0, LSR r1
; then the disc size in sectors
        LDR     r1, [sp, #DiscRecord_DiscSize]
        MOV     r1, r1, LSR lr
        LDR     r2, [sp, #DiscRecord_BigMap_DiscSize2]
 [ DebugL

        DREG    r2, "Discsize2 was "
 ]
        RSB     lr, lr, #32
        ORR     r1, r1, r2, LSL lr      ; now full DiscSize in sectors in r1
        CMP     r1, r0
 |
        MOV     r0, r0, ASL r1
        LDR     r1, [sp, #DiscRecord_DiscSize]
        CMP     r1, r0
 ]
 [ DebugL
        BGT     %FT01
        DREG    r0, "Failed on Disc Size (signed)<= "
01
 ]
        BLE     %FT85
 [ {FALSE}
        ; Don't check for upper limit - its OK to be bigger
        CMP     r1, #512*1024*1024
 [ DebugL
        BLO     %FT01
        DLINE   "Failed on Disc Size > 512M"
01
 ]
        BHS     %FT85
 ]

        ; All checked out - this disc record is cool!
        ; Copy the info into the r5 disc record
        ; Those fields which must be left to later are:
        ; DiscId
        ; DiscName
        ; BootOpt       (Although the byte which would be the BootOption is copied)
        ASSERT  DiscRecord_IdLen :MOD: 4 = 0
        ASSERT  DiscRecord_Log2bpmb = DiscRecord_IdLen+1
        ASSERT  DiscRecord_Skew = DiscRecord_Log2bpmb+1
        ASSERT  DiscRecord_BootOpt = DiscRecord_Skew+1
        LDR     r0, [sp, #DiscRecord_IdLen]
        STR     r0, [r5, #DiscRecord_IdLen]
        LDRB    r0, [sp, #DiscRecord_NZones]
        STRB    r0, [r5, #DiscRecord_NZones]
 [ BigMaps
        LDRB    r0, [sp, #DiscRecord_BigMap_NZones2]
        STRB    r0, [r5, #DiscRecord_BigMap_NZones2]
 ]
        LDRB    r0, [sp, #DiscRecord_ZoneSpare]
        STRB    r0, [r5, #DiscRecord_ZoneSpare]
        LDRB    r0, [sp, #DiscRecord_ZoneSpare+1]
        STRB    r0, [r5, #DiscRecord_ZoneSpare+1]
        LDR     r0, [sp, #DiscRecord_Root]
        LDR     r1, [r5, #DiscRecord_Root]
        AND     r1, r1, #DiscBits
        ORR     r0, r0, r1
 [ DebugL
        DREG    r1, "DiscBits from disc record are: "
        DREG    r0, "Setting RootDir to: (Identify, 968) "
 ]
        STR     r0, [r5, #DiscRecord_Root]
        LDR     r0, [sp, #DiscRecord_DiscSize]
        STR     r0, [r5, #DiscRecord_DiscSize]
 [ BigDisc
        LDR     r0, [sp, #DiscRecord_BigMap_DiscSize2]
        STR     r0, [r5, #DiscRecord_BigMap_DiscSize2]
   [ BigShare
        LDR     r0, [sp, #DiscRecord_BigMap_ShareSize]
        STR     r0, [r5, #DiscRecord_BigMap_ShareSize]
   ]
 ]
 [ BigDir
        LDR     r0, [sp, #DiscRecord_BigDir_DiscVersion]
        STR     r0, [r5, #DiscRecord_BigDir_DiscVersion]
        LDR     r0, [sp, #DiscRecord_BigDir_RootDirSize]
        STR     r0, [r5, #DiscRecord_BigDir_RootDirSize]
 ]
        CLRV
        B       %FT90

85
        SETV
90
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check E disc record"
01
 ]
 [ BigDisc
        ADD     sp, sp, #SzDiscRecSig2
 |
        ADD     sp, sp, #SzDiscRecSig
 ]
95
        Pull    "r0,r1,r2,r3,r4,r5,r7,r8,r9,r10,r11,r12,pc"

        LTORG

; =================
; SanityCheckNewMap
; =================
;
; entry
;  r5 -> disc record (with data from disc signature filled in)
;  r6 = cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; on exit
;  r5 unchanged
;  r6 = new cache handle
;  r12 = unchanged
;  VC => Map checked OK
;  VS => Map duff
;
SanityCheckNewMap ROUT
        Push    "r0-r5,r7-r10,lr"

 [ DebugL
        DLINE   "Sanity checking new map"
 ]

        LDRB    r4, [r5, #DiscRecord_Log2SectorSize]
        MOV     lr, #1
        SUB     sp, sp, lr, ASL r4

 [ BigMaps
        LDRB    r9, [r5, #DiscRecord_NZones]
        LDRB    r7, [r5, #DiscRecord_BigMap_NZones2]
        ADD     r9, r9, r7, LSL #8
 |
        LDRB    r9, [r5, #DiscRecord_NZones]
 ]
        LDRB    r7, [r5, #DiscRecord_Log2SectorSize]

        BL      MapDiscAdd
        MOV     r7, r2

        ; Offset to start of map block (chooses between the two map copies)
        MOV     r8, #0

10
        ; Sector offset within map copy
        MOV     r9, #0

        ; Current EOR of cross checks
        MOV     r10, #0

20
        ; Read a sector
        ADD     r2, r7, r8
        LDRB    r4, [r5, #DiscRecord_Log2SectorSize]
 [ BigDisc
        ADD     r2, r2, r9
 |
        ADD     r2, r2, r9, ASL r4
 ]
        MOV     r3, sp
        MOV     lr, #1
        MOV     r4, lr, ASL r4
 [ BigDisc
        BL      SWI_SectorDiscOp_CachedReadSectors_Myself
 |
        BL      SWI_DiscOp_CachedReadSectors_Myself
 ]
 [ DebugL
        BVC     %FT01
        ADD     r1, r0, #4
        DSTRING r1, "(Read failed:"
01
 ]
        BVS     %FT30

        ; Make a simple test on the free link
        LDRB    r0, [sp, #FreeLink+1]
        TST     r0, #&80
 [ DebugL
        BNE     %FT01
        DLINE   "(Top bit of free link is clear)"
01
 ]
        BEQ     %FT30

        ; Check it
        MOV     r0, sp
        LDRB    r1, [r5, #DiscRecord_Log2SectorSize]
        MOV     lr, #1
        MOV     r1, lr, ASL r1
        BL      NewCheck
 [ DebugL
        BEQ     %FT01
        DLINE   "(NewCheck failed)"
01
 ]
        BNE     %FT30

        ; Construct the cross check
        LDRB    r0, [sp, #CrossCheck]
        EOR     r10, r10, r0

        ; Advance to the next sector of the map
        ADD     r9, r9, #1
 [ BigMaps
        LDRB    r0, [r5, #DiscRecord_NZones]
        LDRB    r2, [r5, #DiscRecord_BigMap_NZones2]    ; r2 should be ok here
        ADD     r0, r0, r2, LSL #8
 |
        LDRB    r0, [r5, #DiscRecord_NZones]
 ]
        CMP     r9, r0
        BLO     %BT20

        ; Check the cross check
        TEQ     r10, #&ff
 [ DebugL
        BEQ     %FT01
        DREG    r10, "(Cross check failed):"
01
 ]
        BEQ     %FT40

30
        ; A failure has happened

        ; If not on first copy of map, then map is bad, so give up!
        TEQ     r8, #0
 [ DebugL
        BEQ     %FT01
        DLINE   "Failed on both map copies"
01
 ]
        BNE     %FT85

        ; Try on the second map copy
 [ BigMaps
        LDRB    r8, [r5, #DiscRecord_NZones]
        LDRB    r0, [r5, #DiscRecord_BigMap_NZones2]
        ADD     r8, r8, r0, LSL #8
 |
        LDRB    r8, [r5, #DiscRecord_NZones]
 ]
        LDRB    r0, [r5, #DiscRecord_Log2SectorSize]
 [ BigDisc
 |
        MOV     r8, r8, ASL r0
 ]

        B       %BT10

40
        ; Map cross-checks OK


        ; Check the disc record in the first map sector against
        ; the disc's disc record.

        ADD     r2, r7, r8
        LDRB    r4, [r5, #DiscRecord_Log2SectorSize]

        MOV     r3, sp
        MOV     lr, #1
        MOV     r4, lr, ASL r4
 [ BigDisc
        BL      SWI_SectorDiscOp_CachedReadSectors_Myself
 |
        BL      SWI_DiscOp_CachedReadSectors_Myself
 ]
 [ DebugL
        BVC     %FT01
        ADD     r1, r0, #4
        DSTRING r1, "Failed on reread of 1st map block"
01
 ]
        BVS     %FT90

        ; Cross check the boot disc record with the map 1st sector
        ; disc record. Those fields which should match are:
        ; sector size
        ; sectors per track
        ; heads
        ; density
        ; fragment id length
        ; bytes per map bit
        ; skew
        ; lowsector
        ; zones
        ; zone spare
        ; root directory
        ; disc size
        ; Those fields which don't match are:
        ; boot option
        ; disc id
        ; disc name
        ADD     r4, sp, #ZoneHead
        MOV     r14, r5

        ASSERT  DiscRecord_Log2SectorSize=0
        ASSERT  DiscRecord_SecsPerTrk=1
        ASSERT  DiscRecord_Heads=2
        ASSERT  DiscRecord_Density=3
        ASSERT  DiscRecord_IdLen=4
        ASSERT  DiscRecord_Log2bpmb=5
        ASSERT  DiscRecord_Skew=6
        ASSERT  DiscRecord_BootOpt=7
        LDMIA   r4!, {r0-r1}
        LDMIA   r14!, {r2-r3}
        TEQ     r0, r2
 [ DebugL
        BEQ     %FT01
        DREG    r0,"DHSS:",cc
        DREG    r2,"!="
01
 ]
        EOR     r0, r1, r3
        BICS    r0, r0, #&ff000000      ; Boot option missing from boot record (pervy eh?)
 [ DebugL
        BEQ     %FT01
        DREG    r1,"BRBL:",cc
        DREG    r3,"!="
01
 ]

        ASSERT  DiscRecord_LowSector=8
        ASSERT  DiscRecord_NZones=9
        ASSERT  DiscRecord_ZoneSpare=10
        ASSERT  DiscRecord_Root=12
        LDMEQIA r4!, {r0-r1}
        LDMEQIA r14!, {r2-r3}
        TEQEQ   r0, r2
 [ DebugL
        BEQ     %FT01
        DLINE   ".",cc
01
 ]
        BICEQ   r3, r3, #DiscBits       ; Test root dir modulo disc number
        TEQEQ   r1, r3
 [ DebugL
        BEQ     %FT01
        DLINE   ".",cc
01
 ]

        ASSERT  DiscRecord_DiscSize=16
 [ BigDisc
        ASSERT  DiscRecord_BigMap_DiscSize2=36
 ]
        LDREQ   r0, [r4], #4
        LDREQ   r2, [r14], #4
        TEQEQ   r0, r2
 [ BigDisc
   [ BigShare
        ASSERT  DiscRecord_BigMap_ShareSize=40
        ADDEQ   r4,r4,#DiscRecord_BigMap_DiscSize2-DiscRecord_DiscSize-4
        ADDEQ   r14,r14,#DiscRecord_BigMap_DiscSize2-DiscRecord_DiscSize-4
        LDMEQIA r4!, {r0-r1}
        LDMEQIA r14!, {r2-r3}
        TEQEQ   r0, r2
        TEQEQ   r1, r3
   |
        ADDEQ   r4,r4,#DiscRecord_BigMap_DiscSize2-DiscSize-4
        ADDEQ   r14,r14,#DiscRecord_BigMap_DiscSize2-DiscSize-4
        LDREQ   r0, [r4], #4
        LDREQ   r2, [r14], #4
        TEQEQ   r0, r2
   ]
 ]
 [ DebugL
        BEQ     %FT01
        DLINE   ".",cc
01
 ]
 [ DebugL
        BEQ     %FT01
        DLINE   "Failed on disc record cross-check"
01
 ]
        BNE     %FT85

        ; All hunky-dory, copy over DiscId, Boot Option and Disc Name
        ASSERT  DiscRecord_DiscId :MOD: 4 = 0
        ASSERT  DiscRecord_DiscName = DiscRecord_DiscId+2
        LDR     r0, [sp, #ZoneHead + DiscRecord_DiscId]
        STR     r0, [r5, #DiscRecord_DiscId]
        LDR     r0, [sp, #ZoneHead + DiscRecord_DiscId+4]
        STR     r0, [r5, #DiscRecord_DiscId+4]
        LDR     r0, [sp, #ZoneHead + DiscRecord_DiscId+8]
        STR     r0, [r5, #DiscRecord_DiscId+8]
        LDRB    r0, [sp, #ZoneHead + DiscRecord_BootOpt]
        STRB    r0, [r5, #DiscRecord_BootOpt]
        B       %FT90

85
        SETV
90
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check new map"
01
 ]
        LDRB    r4, [r5, #DiscRecord_Log2SectorSize]
        MOV     lr, #1
        ADD     sp, sp, lr, ASL r4
95
        Pull    "r0-r5,r7-r10,pc"

        LTORG

; ==================
; SanityCheckLFormat
; ==================
;
; entry
;  r5 = pointer to disc record
;  r6 = sector cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; exit
;   r6 = new sector cache handle
;   r12 = unchanged
;  AND
;   VC: Matches OK
;   VS: Mismatch and r0 does _not_ point to an error
;
SanityCheckLFormat ROUT
        Push    "r0,lr"
 [ DebugL
        DLINE   "Sanity checking L format"
 ]

        ; Must be lowsector=0, 2 heads, 15x256 byte sectors, and double density
        LDRB    r0, [r5, #DiscRecord_LowSector]
        ASSERT  DiscRecord_LowSector_Shift = 0
        AND     r0, r0, #DiscRecord_LowSector_Mask
        TEQ     r0, #0
        BNE     %FT90
        BL      EnsureSequencedSides
        TEQ     r0, #1
 [ DebugL
        BEQ     %FT01
        DREG    r0,"Heads="
01
 ]
        BNE     %FT90
        LDRB    r0, [r5, #DiscRecord_SecsPerTrk]
        TEQ     r0, #16
 [ DebugL
        BEQ     %FT01
        DREG    r0,"SecsPerTrk="
01
 ]
        BNE     %FT90
        LDRB    r0, [r5, #DiscRecord_Log2SectorSize]
        TEQ     r0, #8
 [ DebugL
        BEQ     %FT01
        DREG    r0,"Log2SectorSize="
01
 ]
        BNE     %FT90
        LDRB    r0, [r5, #DiscRecord_Density]
        TEQ     r0, #DensityDouble
 [ DebugL
        BEQ     %FT01
        DREG    r0,"Density="
01
 ]
        BNE     %FT90

 [ BigDisc
        ; Fake a disc size for sequence sides
        MOV     r0, #640*1024
        STR     r0, [r5, #DiscRecord_DiscSize]

        ; Upper word of disc size
        MOV     r0, #0
        STR     r0, [r5, #DiscRecord_BigMap_DiscSize2]
        ; r0 is already 0 for SanityCheckOldMap
 |
        ; Fake a disc size for sequence sides
        MOV     r0, #640*1024
        STR     r0, [r5, #DiscRecord_DiscSize]

        ; Read and check map from disc
        MOV     r0, #0
 ]
        BL      SanityCheckOldMap
 [ DebugL
        BVC     %FT01
        DLINE   "OldMapBroken"
01
 ]
        BVC     %FT95

90
        SETV
95
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check L format"
        B       %FT02
01
        DLINE   "L format is the business!"
02
 ]
        Pull    "r0,pc"

        LTORG

; ==================
; SanityCheckDFormat
; ==================
;
; entry
;  r5 = pointer to disc record
;  r6 = sector cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; exit
;   r6 = new sector cache handle
;   r12 = unchanged
;  AND
;   VC: Matches OK
;   VS: Mismatch and r0 does _not_ point to an error
;
SanityCheckDFormat ROUT
        Push    "lr"
 [ DebugL
        DLINE   "Sanity checking D format"
 ]

        ; Read and check map from disc
        MOV     r0, #1
        BL      SanityCheckOldMap

95
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check D format"
        B       %FT02
01
        DLINE   "D format is the business!"
02
 ]
        Pull    "pc"

        LTORG

; =================
; SanityCheckOldMap
; =================
;
; entry
;  r0 =flags:
;       if <> 0 'Directories are large (77 entries)' and at D_Root
;  r5 -> disc record (with data from disc signature filled in)
;  r6 = cache handle
;  r12 = Pointer to FileCore instance private word to use
;
; on exit
;  r5 unchanged
;  r6 = new cache handle
;  r12 = unchanged
;  VC => Map checked OK and disc name and size copied to disc record
;  VS => Map duff
;
SanityCheckOldMap ROUT
        Push    "r0-r4,r7,lr"
        SUB     sp, sp, #SzOldFs

 [ DebugL
        DLINE   "Sanity checking old map"
 ]

        LDR     r7, [r5, #DiscRecord_Root]
        AND     r7, r7, #DiscBits

        ; Read the 1st map copy
        MOV     r2, r7
        MOV     r3, sp
        MOV     r4, #SzOldFs
        BL      SWI_DiscOp_CachedReadSectors_Myself

 [ DebugL
        DLINE   "Point A"
        BVC     %FT01
        ADD     r1, r0, #4
        DSTRING r1, "Failed on read map (1st copy):"
01
 ]
        BVS     %FT90

        MOV     r0, sp
        MOV     r1, #SzOldFs/2
        BL      CheckSum
 [ DebugL
        DLINE   "Point B"
        BVC     %FT01
        DLINE   "Failed on check sum, 1st half"
01
 ]
        BVS     %FT90

        ; CheckSum 2nd section of old map
        ADD     r0, sp, #SzOldFs/2
        MOV     r1, #SzOldFs/2
        BL      CheckSum
 [ DebugL
        DLINE   "Point D"
        BVC     %FT01
        DLINE   "Failed on check sum, 2nd half"
01
 ]
        BVS     %FT90

        ; CheckSum clean - sanity check free space list

        ; r0 = pointer into free list
        ; r1 = end of previous free block (starts at end of root dir) (in 256-bytes)
        ; r3 = disc size (in 256-bytes)
        ; r4 = Number of free space entries to go
        ADD     r0, sp, #FreeStart
        LDR     lr, [sp, #SzOldFs]
        TEQ     lr, #0
        MOVEQ   r1, #L_Root + OldDirSize
        MOVNE   r1, #D_Root + NewDirSize
        MOV     r1, r1, LSR #8
        SUB     r1, r1, #1
        ASSERT  (OldSize :MOD: 4) = 0
        LDR     r3, [sp, #OldSize]
        BIC     r3, r3, #&ff000000      ; r3 = disc size/256
        CMP     r3, #8+2                ; 2*256 for map, 8*256 for large (new) directory
        SETV    LO
 [ DebugL
        BVC     %FT01
        DREG    r3, "Disc size too small:"
01
 ]
        BVS     %FT90

        LDRB    r4, [sp, #FreeEnd]
        B       %FT20

10
        ; Construct Start
        LDRB    r2, [r0, #0]
        LDRB    lr, [r0, #1]
        ORR     r2, r2, lr, ASL #8
        LDRB    lr, [r0, #2]
        ORR     r2, r2, lr, ASL #16

        ; If Start <= previous end then bad map
        CMP     r2, r1
 [ DebugL
        BHI     %FT01
        DREG    r2, "Failed on Start(",cc
        DREG    r1, ")<=Previous end(",cc
        DLINE   ")"
01
 ]
        SETV    LS
        BVS     %FT90

        ; Construct End
        ADD     r0, r0, #FreeLen - FreeStart
        LDRB    lr, [r0, #0]
        ADD     r1, r1, lr
        LDRB    lr, [r0, #1]
        ADD     r1, r1, lr, ASL #8
        LDRB    lr, [r0, #2]
        ADD     r1, r1, lr, ASL #16
        ADD     r0, r0, #FreeStart - FreeLen + 3

        ; If End>DiscSize then puke
        CMP     r1, r3
 [ DebugL
        BLS     %FT01
        DREG    r1, "Failed on End(",cc
        DREG    r3, ")>Disc size(",cc
        DLINE   ")"
01
 ]
        SETV    HI
        BVS     %FT90

20
        ; Count down to the end
        SUBS    r4, r4, #3
        BHI     %BT10

        ; Copy OldBoot to BootOpt
        LDRB    r0, [sp, #OldBoot]
        STRB    r0, [r5, #DiscRecord_BootOpt]

        ; Copy the OldId to DiscId
        LDRB    r0, [sp, #OldId+0]
        STRB    r0, [r5, #DiscRecord_DiscId+0]
        LDRB    r0, [sp, #OldId+1]
        STRB    r0, [r5, #DiscRecord_DiscId+1]

        ; Copy the name to the disc record
        LDRB    r0, [sp, #OldName0+0]
        STRB    r0, [r5, #DiscRecord_DiscName+0]
        LDRB    r0, [sp, #OldName1+0]
        STRB    r0, [r5, #DiscRecord_DiscName+1]
        LDRB    r0, [sp, #OldName0+1]
        STRB    r0, [r5, #DiscRecord_DiscName+2]
        LDRB    r0, [sp, #OldName1+1]
        STRB    r0, [r5, #DiscRecord_DiscName+3]
        LDRB    r0, [sp, #OldName0+2]
        STRB    r0, [r5, #DiscRecord_DiscName+4]
        LDRB    r0, [sp, #OldName1+2]
        STRB    r0, [r5, #DiscRecord_DiscName+5]
        LDRB    r0, [sp, #OldName0+3]
        STRB    r0, [r5, #DiscRecord_DiscName+6]
        LDRB    r0, [sp, #OldName1+3]
        STRB    r0, [r5, #DiscRecord_DiscName+7]
        LDRB    r0, [sp, #OldName0+4]
        STRB    r0, [r5, #DiscRecord_DiscName+8]
        LDRB    r0, [sp, #OldName1+4]
        STRB    r0, [r5, #DiscRecord_DiscName+9]

        ; Copy disc size to disc record
        MOV     r3, r3, ASL #8
        STR     r3, [r5, #DiscRecord_DiscSize]
 [ BigDisc
        MOV     r3, #0
        STR     r3, [r5, #DiscRecord_BigMap_DiscSize2]
 ]

90
 [ DebugL
        BVC     %FT01
        DLINE   "Failed to check old map"
        B       %FT02
01
        DLINE   "Old map is the business!"
02
 ]
        ADD     sp, sp, #SzOldFs
95
        Pull    "r0-r4,r7,pc"

        LTORG

; ====================
; EnsureSequencedSides
; ====================
;
; In
;    R5 = pointer to disc record
; Out
;    R0 = number of heads (adjusted for sequence sides)
;    R5 = pointer to sequence sided disc record
;
EnsureSequencedSides ROUT
        LDRB    r0, [r5, #DiscRecord_LowSector]
        TST     r0, #DiscRecord_SequenceSides_Flag
        ORR     r0, r0, #DiscRecord_SequenceSides_Flag
        STRB    r0, [r5, #DiscRecord_LowSector]
        LDRB    r0, [r5, #DiscRecord_Heads]
        SUBEQ   r0, r0, #1
        STREQB  r0, [r5, #DiscRecord_Heads]
        MOV     pc, lr

; ======================
; EnsureInterleavedSides
; ======================
;
; In
;    R5 = pointer to disc record
; Out
;    R0 = number of heads (adjusted for interleaved sides)
;    R5 = pointer to sequence sided disc record
;
EnsureInterleavedSides ROUT
        LDRB    r0, [r5, #DiscRecord_LowSector]
        TST     r0, #DiscRecord_SequenceSides_Flag
        BIC     r0, r0, #DiscRecord_SequenceSides_Flag
        STRB    r0, [r5, #DiscRecord_LowSector]
        LDRB    r0, [r5, #DiscRecord_Heads]
        ADDNE   r0, r0, #1
        STRNEB  r0, [r5, #DiscRecord_Heads]
        MOV     pc, lr

; ===================================
; SWI_DiscOp_CachedReadSectors_Myself
; ===================================
;
; In
;       r2-r4 DiscOp parameters
;       r5 = disc record
;       r6 = cache
;       r12 = private workspace pointer
;
; Out
;       r12 = unchanged
;       results of DiscOp
;
SWI_DiscOp_CachedReadSectors_Myself ROUT
        Push    "r1,r7-r12,lr"
 [ Debug2 :LOR: Debug2D
        DREG    r1, "DiscOpMyself(",cc
        DREG    r2, ",",cc
        DREG    r3, ",",cc
        DREG    r4, ",",cc
        DLINE   ")"
 ]
        MOV     r11, sp
        MOV     r1, r2, LSR #29
        BIC     r2, r2, #DiscBits
        MOV     lr, #0
        Push    "r1,r2,lr"
        MOV     r8, r12
        MOV     r2, sp
        MOV     r1, #DiscOp_CachedReadSecs :OR: DiscOp_Op_IgnoreEscape_Flag
        SWI     XFileCore_DiscOp64
 [ DebugL
        BVC     %FT01
        ADD     r0, r0, #4
        DSTRING r0, "Cached read sector error:"
        SUB     r0, r0, #4
01
 ]
        LDMIA   r2, {r2, lr}
        BIC     lr, lr, #DiscBits
        ORR     r2, lr, r2, LSL #29
        MOV     sp, r11
        Pull    "r1,r7-r12,pc"

; ===================================
; SWI_DiscOp_CachedReadSectors_Myself
; ===================================
;
; In
;       r2-r4 DiscOp parameters
;       r5 = disc record
;       r6 = cache
;       r12 = private workspace pointer
;
; Out
;       r12 = unchanged
;       results of DiscOp
;

SWI_SectorDiscOp_CachedReadSectors_Myself ROUT
        Push    "r1,r7-r12,lr"
 [ Debug2 :LOR: Debug2D
        DREG    r1, "SectorDiscOpMyself(",cc
        DREG    r2, ",",cc
        DREG    r3, ",",cc
        DREG    r4, ",",cc
        DLINE   ")"
 ]
        MOV     r11, sp
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        MOV     r1, r2, LSR #29
        BIC     r2, r2, #DiscBits
        RSB     r8, lr, #32
        MOV     r7, r2, LSL lr
        MOV     lr, r2, LSR r8
        Push    "r1,r7,lr"
        MOV     r8, r12
        MOV     r2, sp
        MOV     r1, #DiscOp_CachedReadSecs :OR: DiscOp_Op_IgnoreEscape_Flag
        SWI     XFileCore_DiscOp64
 [ DebugL
        BVC     %FT01
        ADD     r0, r0, #4
        DSTRING r0, "Cached read sector error:"
        SUB     r0, r0, #4
01
 ]
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        LDMIA   r2, {r1, r2, r7}
        RSB     r8, lr, #32
        MOV     r2, r2, LSR lr
        ORR     r2, r2, r7, LSL r8
        BIC     r2, r2, #DiscBits
        ORR     r2, r2, r1, LSL #29
        MOV     sp, r11
        Pull    "r1,r7-r12,pc"

        END