; 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.
;
;>Commands

; change log (added by SBP 14 May 1997)
;
; 14 May 1997: SBP: Incorporated old fix for *verify to work when verifying >2G discs (DoVerify)
; 19 Aug 1997: SBP: Changes for big free space maps.
; 13 Sep 2011: RPS: Fix *DEFECT aborting when object occupies defect address

        TTL    "Command handling"

FsCom   bit     (31-24)
IntHelp *       (International_Help:SHR:24)

        MACRO
        ComEntry  $Com,$MinArgs,$MaxArgs,$GsTransBits,$HiBits
        ASSERT  $MinArgs<=$MaxArgs
Com$Com DCB     "$Com",0
        ALIGN
        DCD     Do$Com          - Module_BaseAddr
 =       $MinArgs
 =       $GsTransBits
 =       $MaxArgs
 =       $HiBits
        DCD     Syn$Com         - Module_BaseAddr
        DCD     Help$Com        - Module_BaseAddr
        MEND


        MACRO
        Config  $Com
        DCB     "$Com",0
        ALIGN
        DCD     Con$Com         - Module_BaseAddr
        DCD     1 :SHL: 30
        DCD     0
        DCD     ConHelp$Com     - Module_BaseAddr
        MEND

ComTab                                          ;general star commands
                                         ;filing system star commands
        ComEntry  Backup,        2,4,&F,FsCom:OR:IntHelp
        ComEntry  Bye,           0,0,0,FsCom:OR:IntHelp
        ComEntry  CheckMap,      0,1,1,FsCom:OR:IntHelp
        ComEntry  Compact,       0,1,1,FsCom:OR:IntHelp
        ComEntry  Defect,        2,2,3,FsCom:OR:IntHelp
        ComEntry  Dismount,      0,1,1,FsCom:OR:IntHelp
        ComEntry  Drive,         1,1,1,FsCom:OR:IntHelp
        ComEntry  Free,          0,1,1,FsCom:OR:IntHelp
        ComEntry  Map,           0,1,1,FsCom:OR:IntHelp
        ComEntry  Mount,         0,1,1,FsCom:OR:IntHelp
        ComEntry  NameDisc,      2,2,3,FsCom:OR:IntHelp
        ComEntry  NameDisk,      2,2,3,FsCom:OR:IntHelp
        ComEntry  Verify,        0,1,1,FsCom:OR:IntHelp
                                         ;status/configure options
        =       0
        ALIGN

; ----- FILING SYSTEM STAR COMMANDS -----

         ^ 0    ; Backup temporary workspace
SrcDrv          # 1
SrcDrvChar      # 1
DestDrv         # 1
DestDrvChar     # 1

SameDrive       # 1
BufOpt          # 1
FirstTime       # 1
                # 1

FSColonColon    # 4
DrvPlace        # 4
TotalSectors    # 4
UsedMap         # 4
ReadSectors     # 4 ; Where we're up to
AmountRead      # 4 ; How much was read this time round
WriteSectors    # 4 ; Where we're up to
BufferGotten    # 4 ; How much buffer we've got

 [ BigDisc
SrcDiscRec      # SzDiscRecSig2
 |
SrcDiscRec      # SzDiscRecSig
 ]
BackupWork      # 0


; >>>>>>>>
; DoBackup
; >>>>>>>>

BC0     DCB     "BC0",0
BC1     DCB     "BC1",0
BC2     DCB     "BC2",0
 [ {FALSE}
 ; 'Same disc' error now redundent
BC3     DCB     "BC3",0
 ]
        ALIGN

DoBackup ROUT
        SemEntry  Flag,Light    ;leaves SB,LR stacked
        Push    "R7-R11"

        SUB     sp, sp, #BackupWork

        MOV     r11, sp

        ; Clear out BufOpt to prevent unwanted return by OS_Exit
        MOV     lr, #0
        STRB    lr, [r11, #BufOpt]

        ; Parse source drive
        MOV     r1, r0
        BL      SkipSpaces      ;(R1->R0,R1,C)
        TEQS    r0, #DiscSpecChar
        ADDEQ   r1, r1, #1
        LDRB    lr, [r1]
        STRB    lr, [r11, #SrcDrvChar]
        BL      ParseDrive      ;(R1->R0,R1,V) source drive
        BVS     BackupErrorExit
        ADD     r1, r1, #1
        STRB    r0, [r11, #SrcDrv]
        MOV     r6, r0

        ; Parse dest drive
        BL      SkipSpaces
        TEQS    r0, #DiscSpecChar
        ADDEQ   r1, r1, #1
        LDRB    lr, [r1]
        STRB    lr, [r11, #DestDrvChar]
        BL      ParseDrive      ;(R1->R0,R1,V) dest drive
        BVS     BackupErrorExit
        ADD     r1, r1, #1
        STRB    r0, [r11, #DestDrv]

        ; Ensure neither drive is a winnie
        ANDS    lr, r0, r6
        TSTS    lr, #4
        MOVEQ   r0, #BadDriveErr ;can't do winnies
        SETV    EQ
        BVS     BackupErrorExit

        ; Note down whether the drives are the same or not
        EORS    r8, r0, r6
        STRB    r8, [r11, #SameDrive]

        ; Work out which buffers we're permitting ourselves (process Q option)
        MOVEQ   r8, #(1:SHL:UseDirSpace) :OR: (1:SHL:UseWimpFree) :OR: (1:SHL:UseRmaHeap) :OR: (1:SHL:UseSysHeap) :OR: DiscOp_Op_ScatterList_Flag
        MOVNE   r8, #(1:SHL:UseScratchSpace) :OR: (1:SHL:UseSpareScreen) :OR: (1:SHL:UseWimpFree) :OR: (1:SHL:UseRmaHeap) :OR: (1:SHL:UseSysHeap) :OR: DiscOp_Op_ScatterList_Flag
        BL      SkipSpaces      ;(R1->R0,R1,C)
        TEQS    r0, #"Q"
        TEQNES  r0, #"q"
        ORREQ   r8, r8, #(1:SHL:UseApplicArea)
        ADDEQ   r1, r1, #1        ;if Q skip it
        STRB    r8, [r11, #BufOpt]

        ; Process Y option and Confirm if not present
        BL      SkipSpaces      ;(R1->R0,R1,C)
        CMPS    r0, #"Y"
        CMPNES  r0, #"y"
        BLNE    Confirm         ;(->R0,Z,V)
        BNE     BackupErrorExit

        ; Parameters now read and understood

        ; Construct <FS>::<Drv>\n
        LDR     r1, FS_Title
        BL      strlen
        ADD     r0, r3, #4+3            ; ::<drv>\n + round-up
        BIC     r0, r0, #3
        SUB     sp, sp, r0
        STR     sp, [r11, #FSColonColon]
        MOV     r2, r1
        MOV     r1, sp
        BL      strcpy
        ADD     r1, r1, r3
        MOV     r0, #":"
        STRB    r0, [r1], #1
        STRB    r0, [r1], #1
        STR     r1, [r11, #DrvPlace]
        MOV     r0, #0
        STRB    r0, [r1, #1]

        ; repeat

        MOV     r0, #1
        STRB    r0, [r11, #FirstTime]
        MOV     r0, #0
        STR     r0, [r11, #ReadSectors]
        STR     r0, [r11, #WriteSectors]

        ; {source reading part}
        ;   If source=dest:
        ;     If first time round:
        ;       Prompt user to write-protect source disc
        ;     Prompt for source disc
        ;     Wait for user key response
        ;   {source disc now in source drive}
        ;   If first time round
        ;     read file parameters for source disc
        ;     allocate space map for source disc
        ;     read space map for source disc
        ;     take a copy of the disc record for the source disc
        ;     Claim memory for transfer
        ;   read in source disc next part

        ; {dest writing part}
        ;   If source=dest
        ;     Prompt user for dest disc
        ;     Wait for user key response
        ;   {dest disc now in dest drive}
        ;   If first time round
        ;     read dest defect list
        ;     If defect list not empty
        ;       complain dest has defects
        ;     read disc record for dest disc
        ;     If dest disc record <> source disc record enough
        ;       complain source and dest don't match
        ;     Dismount dest disc
        ;   {dest disc acceptable}
        ;   Write out dest disc next part

        ; until no more to read

BackupMainLoop
        ; If first time (0<r0) OR not finished (read<total) then do another step
        LDRB    r0, [r11, #FirstTime]
        RSBS    r0, r0, #0

        LDRHS   r0, [r11, #ReadSectors]
        LDRHS   r1, [r11, #TotalSectors]
        CMPHS   r0, r1
        BHS     BackupDone

        LDRB    lr, [r11, #SameDrive]
        TEQ     lr, #0
        BNE     BackupNoPromptSource

        ; Prompt for source disc

        LDRB    lr, [r11, #FirstTime]
        TEQ     lr, #0
        baddr   r0, BC0, NE             ; PLEASE WRITE PROTECT SOURCE DISC
        BLNE    message_gswrite0
        baddr   r3, BC1, VC             ; Insert source disc in drive %0 then press SPACE bar
        LDRVCB  r0, [r11, #SrcDrvChar]
        BLVC    BackupPromptDisc
        BVS     BackupErrorExit

BackupNoPromptSource
        LDRB    lr, [r11, #FirstTime]
        TEQ     lr, #0
        BEQ     BackupScatterListPresent

        ; Read file parameters for source disc
        LDRB    r1, [r11, #SrcDrv]

 [ DebugQ
        DREG    r1, "Initial look at drive "
 ]

        BL      WhatDisc
        BVS     BackupErrorExit

 [ DebugQ
        DLINE   "Drive OK"
 ]

        ; take a copy of the disc record for the source disc
        LDMIA   r3, {r0-r2,r4-r8}
        ADD     lr, r11, #SrcDiscRec
        STMIA   lr, {r0-r2,r4-r8}
 [ BigDisc ; SBP: Thu 15th December 1994 - walked through, looks OK
        LDR     r0, [r3,#DiscRecord_BigMap_DiscSize2]
        STR     r0, [lr,#DiscRecord_BigMap_DiscSize2]

        ; Size/Log2SectorSize (rounding up) = total sectors
; bottom 32 bits
        LDR     r0, [r3, #DiscRecord_DiscSize]
        LDRB    lr, [r3, #DiscRecord_Log2SectorSize]
        MOV     r0, r0, LSR lr
        Push    "R1"
; top 32 bits
        LDR     r1, [r3, #DiscRecord_BigMap_DiscSize2]
        RSB     lr, lr, #32
        ORR     r0, r0, r1, LSL lr      ; combine them
        Pull    "R1"
        STR     r0, [r11, #TotalSectors]
 |
        ; Size/DiscRecord_Log2SectorSize (rounding up) = total sectors
        LDR     r0, [r3, #DiscRecord_DiscSize]
        LDRB    lr, [r3, #DiscRecord_Log2SectorSize]
        MOV     r0, r0, LSR lr
        STR     r0, [r11, #TotalSectors]
 ]

        ; Work out how many words of stack space will be needed
        ADD     r0, r0, #31
        BIC     r0, r0, #31

        ; allocate space map for source disc
        SUB     sp, sp, r0, LSR #3
        STR     sp, [r11, #UsedMap]

        ; Read space map for source disc
        MOV     r5, r0, LSR #3
        LDRB    r0, [r11, #SrcDrvChar]
        LDR     lr, [r11, #DrvPlace]
        STRB    r0, [lr]
        MOV     r0, #FSControl_UsedSpaceMap
        LDR     r1, [r11, #FSColonColon]
        MOV     r2, sp
        BL      DoXOS_FSControl
        BVS     BackupErrorExit

        ; Claim memory for transfer
        LDRB    r0, [r11, #BufOpt]      ; options
        MOV     r1, #1024               ; min. = Largest sector size
 [ BigDisc; SBP: Thu 15th December 1994 - walked through, looks OK
        ; make sure don't ask for silly amount of memory
        LDR     r2, [r11, #SrcDiscRec + DiscRecord_BigMap_DiscSize2]      ; get top 32bits of disc size
        MOVS    r2, r2
        MOVNE   r2, #2048*1024          ; limit max size
        BNE     BackupNotGigantic
        LDR     r2, [r11, #SrcDiscRec + DiscRecord_DiscSize] ; max
        CMP     r2, #2048*1024
        MOVHI   r2, #2048*1024          ; limit max size
BackupNotGigantic
 |
        LDR     r2, [r11, #SrcDiscRec + DiscSize] ; max
 ]
        MOV     r3, #1024               ; granularity
        BL      FindBuffer              ; (r0-r3,r0-r3,V)
        BVS     BackupErrorExit
        LDRB    lr, [r11, #SrcDiscRec + DiscRecord_Log2SectorSize]
        MOV     r2, r2, LSR lr
        STR     r2, [r11, #BufferGotten] ; in sectors

BackupScatterListPresent
        ;   Read in source disc next part
        LDRB    r4, [r11, #SrcDrv]
        MOV     r0, #DiscOp_ReadSecs
        LDR     r1, [r11, #UsedMap]
        LDR     r2, [r11, #ReadSectors]
 [ BigDisc; SBP: Thu 15th December 1994 - changed to use r5 as temp reg
          ; (r5 gets corrupted just below anyway)
; number of sectors to transfer
        LDR     r3, [r11, #SrcDiscRec + DiscRecord_DiscSize]
        LDRB    lr, [r11, #SrcDiscRec + DiscRecord_Log2SectorSize]
        MOV     r3, r3, LSR lr
        LDR     r5, [r11, #SrcDiscRec + DiscRecord_BigMap_DiscSize2]
        RSB     lr, lr, #32
        ORR     r3, r3, r5, LSL lr
 |
        LDR     r3, [r11, #SrcDiscRec + DiscRecord_DiscSize]
        LDRB    lr, [r11, #SrcDiscRec + DiscRecord_Log2SectorSize]
        MOV     r3, r3, LSR lr
 ]

        LDR     lr, [r11, #BufferGotten]
        CMP     r3, lr
        MOVHI   r3, lr
        ADD     r5, r11, #SrcDiscRec
 [ DebugQ
        DREG    r11, "r11 into BackupReadWriteSections is "
 ]
        BL      BackupReadWriteSections
 [ DebugQ
        DREG    r11, "r11 out of BackupReadWriteSections is "
 ]
        BVS     BackupErrorExit
        STR     r2, [r11, #ReadSectors]
        STR     r3, [r11, #AmountRead]

BackupPromptDestDisc
        ; Prompt for Dest if same drive
        LDRB    lr, [r11, #SameDrive]
        TEQ     lr, #0
        baddr   r3, BC2, EQ             ; Insert destination disc in drive %0 then press SPACE bar
        LDREQB  r0, [r11, #DestDrvChar]
 [ DebugQ
        BNE     %FT01
        DLINE   "About to prompt for destination"
01
 ]
        BLEQ    BackupPromptDisc
 [ DebugQ
        DLINE   "Dest in drive"
 ]
        BVS     BackupErrorExit

        LDRB    lr, [r11, #FirstTime]
        TEQ     lr, #0
        BEQ     BackupDestChecked

        ;     Read dest defect list
        LDR     lr, [r11, #DrvPlace]
        LDRB    r0, [r11, #DestDrvChar]
        STRB    r0, [lr]
        SUB     sp, sp, #512
        MOV     r0, #FSControl_DefectList
        LDR     r1, [r11, #FSColonColon]
        MOV     r2, sp
        MOV     r5, #512
        BL      DoXOS_FSControl
        BVS     BackupErrorExit

        ;     If defect list not empty
        LDR     r0, [sp]
        TST     r0, #DiscBits
        ;       complain dest has defects
        MOVEQ   r0, #DestDefectsErr
        SETV    EQ
        BVS     BackupErrorExit

        ADD     sp, sp, #512

        ;     Read disc record for dest disc
        LDRB    lr, [r11, #DestDrv]
        DrvRecPtr lr, lr
        LDRB    lr, [lr, #DrvsDisc]
        DiscRecPtr lr, lr

        ;     If dest disc same complain about same-discness
        ADD     r0, r11, #SrcDiscRec

 [ {FALSE}
 ; 'Same disc' checks now redundent and undesirable.

        ;     If dest disc record <> source disc record enough
        LDMIA   r0!, {r1-r4}
        LDMIA   lr!, {r6-r9}
        TEQ     r1, r6
        TEQEQ   r2, r7
        TEQEQ   r3, r8
        TEQEQ   r4, r9
        LDMEQIA r0, {r1-r4}
        LDMEQIA lr, {r6-r9}
        TEQEQ   r1, r6
        TEQEQ   r2, r7
        TEQEQ   r3, r8
        TEQEQ   r4, r9
        ;       complain source and dest don't match
        BNE     BackupDiscsDifferent

        baddr   r0, BC3
        BL      message_gswrite0
        BVS     BackupErrorExit
        B       BackupPromptDestDisc

BackupDiscsDifferent
        ; Check discs enough alike to work
        SUB     lr, lr, #4*4
        SUB     r0, r0, #4*4
 ]

        LDRB    r1, [lr, #DiscRecord_Log2SectorSize]
        LDRB    r2, [r0, #DiscRecord_Log2SectorSize]
        TEQ     r1, r2
        LDREQB  r1, [lr, #DiscRecord_SecsPerTrk]
        LDREQB  r2, [r0, #DiscRecord_SecsPerTrk]
        TEQEQ   r1, r2
        LDREQB  r1, [lr, #DiscRecord_Density]
        LDREQB  r2, [r0, #DiscRecord_Density]
        TEQEQ   r1, r2
        LDREQB  r1, [lr, #DiscRecord_LowSector]
        LDREQB  r2, [r0, #DiscRecord_LowSector]
        BICEQ   r1, r1, #DiscRecord_SequenceSides_Flag
        BICEQ   r2, r2, #DiscRecord_SequenceSides_Flag
        TEQEQ   r1, r2
        MOVNE   r0, #SizeErr
        SETV    NE
        BVS     BackupErrorExit

        ;     Dismount dest disc
        LDRB    r1, [r11, #DestDrv]
        DrvRecPtr r1, r1
        LDRB    r1, [r1, #DrvsDisc]
        BL      ActiveDismountDisc
        BVS     BackupErrorExit

BackupDestChecked

        ;   Write out dest disc next part
        LDRB    r4, [r11, #DestDrv]
        MOV     r0, #DiscOp_WriteSecs
        LDR     r1, [r11, #UsedMap]
        LDR     r2, [r11, #WriteSectors]
        LDR     r3, [r11, #AmountRead]
        ADD     r5, r11, #SrcDiscRec
        BL      BackupReadWriteSections
        BVS     BackupErrorExit
        STR     r2, [r11, #WriteSectors]

        ; Note the next loop isn't first time
        MOV     lr, #0
        STRB    lr, [r11, #FirstTime]

        B       BackupMainLoop

BackupDone
        ; Stamp the dest image now
        LDRB    r0, [r11, #DestDrvChar]
        LDR     lr, [r11, #DrvPlace]
        STRB    r0, [lr]
        MOV     r0, #FSControl_StampImage
        LDR     r1, [r11, #FSColonColon]
        MOV     r2, #1
        BL      DoXOS_FSControl
        BVS     BackupErrorExit

BackupErrorExit
 [ DebugQ
        ADD     r0, r0, #4
        DSTRING r0, "Error:"
        SUB     r0, r0, #4
 ]
        ; Free claimed resources
        BL      ReturnBuffer
        LDRB    lr, [r11, #BufOpt]
        TST     lr, #1 :SHL: UseApplicArea
        BNE     QuickBack

        BLVS    FindErrBlock
        MOV     sp, r11
        ADD     sp, sp, #BackupWork
        BL      FileCoreExit
        Pull    "r7-r11,SB,pc"

QuickBack
        BVC     %FT99
        BL      FindErrBlock    ;(R0->R0,V)
        BL      FileCoreExit
        SWI     OS_GenerateError
99
        LDR     R1, ABEX
        MOV     R2, #0
        BL      FileCoreExit
        SWI     OS_Exit

ABEX    = "ABEX"

; BackupPromptDisc
;
; In
;   r0 = drive number (ASCII char)
;   r3 = message token
;
; Out
;   r0 trashed
;   disc prompted for and response received
;
BackupPromptDisc ROUT
        Push    "r0-r4,lr"              ; r0 (drive char) gets nul-terminated for free

        MOV     r0, r3
        MOV     r4, sp
        BL      message_gswrite01
        BVS     %FT95

        ; wait for a space
10
        BL      FlushAndReadChar
        BVS     %FT95
        TEQS    R0, #" "
        BNE     %BT10

95
        STRVS   r0, [sp]
        Pull    "r0-r4,pc"

; BackupReadWriteSections
;
; In
;   r0 = op (DiscOp_ReadSecs/DiscOp_WriteSecs)
;   r1 = pointer to used space map
;   r2 = position so far (in sectors)
;   r3 = RAM limit (in sectors)
;   r4 = drive
;   r5 = disc record to use
;
; Out
;   R0, V error possible
;   r2 updated
;   r3 = amount transfered
;
BackupReadWriteSections ROUT
        Push    "r0-r11,lr"

 [ DebugQ
        DREG    r0, ">BackupReadWriteSections(",cc
        DREG    r1, ",",cc
        DREG    r2, ",",cc
        DREG    r3, ",",cc
        DREG    r4, ",",cc
        DREG    r5, ",",cc
        DLINE   ")"
        DREG    sp, "SP at start="
 ]

        BL      InitScatter
        sbaddr  r11, ScatterList

        MOV     r10, #0
        MOV     r9, r0
        MOV     r8, r4

 [ BigDisc; SBP: Thu 15th December 1994 - walked thru, push/pull r7 not needed
; get number of sectors on disc
        LDR     r6, [r5, #DiscRecord_DiscSize]
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        MOV     r6, r6, LSR lr
        LDR     r7, [r5,#DiscRecord_BigMap_DiscSize2]
        RSB     lr, lr, #32
        ORR     r6, r6, r7, LSL lr
 |
        LDR     r6, [r5, #DiscRecord_DiscSize]
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        MOV     r6, r6, LSR lr
 ]

        ; r1 - the map
        ; r2 - position so far
        ; r3 - RAM limit (sectors)
        ; r5 - the disc record
        ; r6 - the disc size (in sectors)
        ; r8 - drive
        ; r9 - the op
        ; r10 - amount transfered
        ; r11 - scatter rover

BackupFindAnotherSection
        MOV     lr, #1
BackupFindAnotherSectionLoop
        CMP     r2, r6
        BHS     BackupSectionsExhausted

        LDRB    r0, [r1, r2, LSR #3]
 [ DebugQ :LAND: {FALSE}
        DREG    r0, ">",cc
 ]
        AND     r4, r2, #7
        ADD     r2, r2, #1
        TST     r0, lr, ASL r4
        BEQ     BackupFindAnotherSectionLoop

        SUB     r7, r2, #1              ; Start of section

        ; End where RAM or disc ends, whichever is lower
        ADD     r9, r7, r3
        CMP     r6, r9
        MOVLO   r9, r6

        ; Check if room for this section
        CMP     r2, r9
        SUBHI   r2, r2, #1
        BHI     BackupSectionsExhausted

BackupFindSectionEnd
        CMP     r2, r9
        BHS     BackupSectionExhausted

        LDRB    r0, [r1, r2, LSR #3]
 [ DebugQ
        DREG    r0, "<",cc
 ]
        AND     r4, r2, #7
        ADD     r2, r2, #1
        TST     r0, lr, ASL r4
        BNE     BackupFindSectionEnd

BackupSectionExhausted
        ; Section end found - construct operation
        SUB     lr, r2, r7
        SUB     r3, r3, lr
        ADD     r10, r10, lr

        Push    "r0-r4"
        LDR     r9, [sp, #5*4 + 0*4]    ; r0 in
        ORR     r1, r9, #DiscOp_Op_ScatterList_Flag
        SUB     r4, r2, r7
        MOV     r2, r7
        MOV     r3, r11
        LDRB    lr, [r5, #DiscRecord_Log2SectorSize]
        MOV     r4, r4, ASL lr
 [ BigDisc;SBP: Thu 15th December 1994 - walked thru, OK
 |
        MOV     r2, r2, ASL lr
 ]
        ORR     r2, r2, r8, ASL #(32-3)
 [ DebugQ
        DREG    r4, "Transfer length "
        DLINE   "Scatter list",cc
        Push    "r0-r2,r4"
        MOV     r2, r3
01
        LDMIA   r2!, {r0,r1}
        DREG    r0," (",cc
        DREG    r1,",",cc
        DLINE   ")",cc
        SUBS    r4, r4, r1
        STR     lr, [r0]
        SUB     r1, r1, #4
        STR     lr, [r0, r1]
        BGT     %BT01
        DLINE
        Pull    "r0-r2,r4"
 ]
 [ DebugQ
        DREG    r1, "Transfer:",cc
        DREG    r2, ",",cc
        DREG    r3, ",",cc
        DREG    r4, ",",cc
        DREG    r5, ","
 ]
        BL      RetryDriveOp
        MOV     r11, r3
        STRVS   r0, [sp]
        Pull    "r0-r4"

        BVC     BackupFindAnotherSection

BackupSectionsExhausted
        STR     r2, [sp, #2*4]
        STR     r10, [sp, #3*4]
        STRVS   r0, [sp]

 [ DebugQ
        DREG    r1, "Advance:"
        DREG    r10, "Transfer:"
        DREG    sp, "SP at end="
 ]

        Pull    "r0-r11,pc"


; >>>>>
; DoBye
; >>>>>

DoBye   ROUT
        SemEntry  Flag,Dormant  ;leaves SB,LR stacked
        BL      DoOsFunShutDown ;(->R0,V)
        BLVS    FindErrBlock    ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "SB,PC"


; >>>>>>>>>>
; DoCheckMap
; >>>>>>>>>>

CMC0    DCB     "CMC0",0
CMC1    DCB     "CMC1",0
CMC2    DCB     "CMC2",0
CMC3    DCB     "CMC3",0
        ALIGN

DoCheckMap
        SemEntry   Flag,Dormant         ;leaves SB,LR stacked
        Push    "R7-R11"
        TEQS    R1, #0
        BLEQ    IdentifyCurrentDisc     ;(->R0,R3,V) if disc param missing
        BLEQ    DiscAddToRec            ;(R3->LR)
        LDREQ   R3, [LR, #DiscRecord_Root]
        MOVNE   R1, R0
        MOVNE   R2, #MustBeDisc
        BLNE    FullLookUp      ;(R1,R2->R0-R6,V)
        BVS     %FT80

        ; Reject non-FileCore discs
        BL      DiscMustBeFileCore      ;(R3->V,R0)
        BVS     %FT80

        BL      TestMap         ;(R3->Z)
        BEQ     %FT05

        baddr   r0, CMC0        ; Old Map
        BL      message_gswrite0
        B       %FT80

05
        MOV     R1, #8
        BL      CloseAllByDisc  ;(R1->R0,V)
        BVS     %FT80
        BL      DiscAddToRec    ;(R3->LR)
        MOV     R6, LR
        LDRB    LR, [R6, #DiscsDrv]
        CMPS    LR, #8
        BHS     %FT15
                        ;IF we have a map in RAM invalidate it
        DrvRecPtr  R7, LR
 [ DynamicMaps
        LDR     R10,[R7,#DrvsFsMapAddr]
 |
        LDR     R10,[R7, #DrvsFsMap]
        BIC     R10,R10,#HiFsBits
 ]

 [ BigMaps
        LDRB    LR, [R6, #DiscRecord_BigMap_NZones2]
        LDRB    R8, [R6, #DiscRecord_NZones]
        ADD     R8, R8, LR, LSL #8
        LDRB    LR, [R6, #DiscRecord_Log2SectorSize]
 |
        LDRB    LR, [R6, #DiscRecord_Log2SectorSize]
        LDRB    R8, [R6, #DiscRecord_NZones]
 ]

        ADD     R9, R10,R8, LSL LR
        MOV     LR, #0
10                      ;loop to clear zone flags
        STRB    LR, [R9], #1
        SUBS    R8, R8, #1
        BPL     %BT10
        LDRB    LR, [R6, #DiscFlags]
        BIC     LR, LR, #AltMapFlag
        STRB    LR, [R6, #DiscFlags]
 [ DynamicMaps
        MOV     LR, #0          ; clear map flags
        STR     R0, [R7, #DrvsFsMapFlags]
 |
        STR     R10,[R7, #DrvsFsMap]
 ]
15
        BL      BeforeReadFsMap ;(R3->R0,V)
        BVS     %FT80           ;can't recover new map if both copies broken

        MOV     R0, #"$"
        BL      DoXOS_WriteC
        BVS     %FT70

20                      ;RECURSE DOWN ENTRY POINT
        BL      FindDir         ;(R3->R0,R5,R6,V)
        BVS     %FT70
 [ BigDir
        BL      GetDirFirstEntry        ; (R3,R5->R4)
        BL      TestBigDir              ; (R3->LR,Z)
        SUBNE   R4, R4, #NewDirEntrySz  ; small dir
        SUBEQ   R4, R4, #BigDirEntrySize; big dir
 |
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 ]
        B       %FT35

25
        MOV     R0, #"."
        BL      DoXOS_WriteC
        BLVC    TermCommon      ;(R3,R4,R5->R10)
        BVS     %FT70

        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        MOV     R2, LR
        BL      ReadIntAtts     ;(R3,R4->LR)
        TSTS    LR, #DirBit
        MOVNE   R3, R2
        BNE     %BT20           ;recurse down if dir
30
 [ fix_5
 ; need to treat big dirs as a special case
        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        MOV     R1, LR
        BL      ReadIntAtts     ;check if it's a dir
        TSTS    LR, #DirBit     ;is it a dir?
        BICNE   R1, R1, #&ff    ;yes then need to pretend isn't for reading size
        BL      MeasureFileAllocSize_GotMap ;(R1,R3,R4->R0)
        MOVS    R1, R0
 |
        BL      ReadLen         ;(R4->LR)
        MOVS    R1, LR
 ]
 [ BigDir
        BL      TestBigDir
        BNE     %FT01

        Push    "R6"
        MOV     R6, R4
        BL      ReturnWholeSpaceNotFudged ;(R1-R6->R0)
        Pull    "R6"
        B       %FT02
01
        Push    "R5"
        SUB     R5, R4, #DirFirstEntry
        BL      ReturnWholeSpace        ;(R1-R5->R0)
        Pull    "R5"

02
 |
        Push    "R5"
        SUB     R5, R4, #DirFirstEntry
        BL      ReturnWholeSpace        ;(R1-R5->R0)
        Pull    "R5"
 ]

        MOV     R10,#DeleteChar
        BL      UnTermCommon    ;(R3,R4,R5,R10)
        MOV     R0, #DeleteChar
        BL      DoXOS_WriteC
        BVS     %FT70

35
 [ BigDir
        BL      TestBigDir      ; (R3->LR,Z)
        BNE     %FT37           ; not big dir

        ; big dir
        ADD     R4, R4, #BigDirEntrySize        ; go to next entry
        BL      BigDirFinished  ; (R4,R5->Z)
        BNE     %BT25           ; more to do
        B       %FT38           ; finished
37
        ; not big dir
        LDRB    LR, [R4, #NewDirEntrySz] !
        CMPS    LR, #" "
        BHI     %BT25
38
 |
        LDRB    LR, [R4, #NewDirEntrySz] !
        CMPS    LR, #" "
        BHI     %BT25
 ]

        MOV     R7, R3
        BL      ToParent                ;(R3,R6->R3)
        CMPS    R3, R7
        BEQ     %FT45                   ;V=0 end when back to root if no match found

        BL      FindDir                 ;(R3->R0,R5,R6,V)
        BVS     %FT70

        BL      AgeDir                  ;(R7)
 [ BigDir
        BL      GetDirFirstEntry        ; (R3,R5->R4)
        BL      TestBigDir              ; (R3->LR,Z)
        SUBNE   R4, R4, #NewDirEntrySz
        SUBEQ   R4, R4, #BigDirEntrySize
 |
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 ]
40
 [ BigDir
        BL      TestBigDir
        ADDEQ   R4, R4, #BigDirEntrySize
        ADDNE   R4, R4, #NewDirEntrySz
 |
        LDRB    LR, [R4, #NewDirEntrySz] !
 ]
        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        TEQS    LR, R7
        BNE     %BT40
        MOV     R2, R7
        B       %BT30

45
        MOV     R0, #DeleteChar
        BL      DoXOS_WriteC
        BVS     %FT70
        BL      CritInitReadNewFs       ;(->R10,R11)
        BL      DiscAddToRec            ;(R3->LR)

 [ BigMaps
        MOV     R5, LR
        LDRB    R0, [R5, #DiscRecord_NZones]
        LDRB    LR, [R5, #DiscRecord_BigMap_NZones2]
        ADD     R0, R0, LR, LSL #8
 |
        MOV     R5, LR
        LDRB    R0, [R5, #DiscRecord_NZones]
 ]
        B       %FT65

50
        baddr   r0, CMC1        ; Map inconsistent with directory tree
        BL      message_gswrite0
        B       %FT70

55
        BL      InitZoneObj     ;(R0,R10->R8,R9,R11,LR)
        MOV     R1, LR
60
 [ BigMaps
        CMPS    R9,R11          ;gap?
        BNE     %FT62           ; not gap

; gap
        BL      FreeRdLenLinkBits ;(R10,R11->R7,R8)
        ADD     R9, R11, R8
        ADD     R11, R11,R7
        CMPS    R11,R1
        BLO     %BT60
        B       %FT65

62
; not gap
        BL      FragRdLenLinkBits ;(R10,R11->R7,R8)
        BIC     lr, r3, #DiscBits
        CMP     R8, lr, LSR #8  ; is this the root dir?
        BEQ     %FT63           ; it's root dir

        CMP     R8, #2          ; is it the map/boot block fragment?
        BLS     %FT63           ; it is so ok

        ; here if broken map

 [ DebugO
 DREG   r8, "Fragment ID hanging around:"
 DREG   r7, "BitLength:"
 DREG   r0, "In zone:"
 DREG   r11, "At bit offset:"
 DREG   r9, "Next free fragment at bit offset:"
 ]
        B       %BT50

63
        ADD     R11,R11,R7
        CMPS    R11,R1
        BLO     %BT60
        B       %FT65

 |

        BL      RdLenLinkBits   ;(R10,R11->R7,R8)
        CMPS    R9, R11
        ADDEQ   R9, R11,R8
        CMPNES  R8, #2
 [ DebugO
 BLS    %FT01
 DREG   r8, "Fragment ID hanging around:"
 DREG   r7, "BitLength:"
 DREG   r0, "In zone:"
 DREG   r11, "At bit offset:"
 DREG   r9, "Next free fragment at bit offset:"
01
 ]
        BHI     %BT50
        ADD     R11,R11,R7
        CMPS    R11,R1
        BLO     %BT60
 ]

65
        SUBS    R0, R0, #1
        BPL     %BT55
        BL      UnlockMap
        BL      BeforeReadFsMap ;(R3->R0,V)
        BVS     %FT70
        LDRB    LR, [R5, #DiscFlags]
        TSTS    LR, #AltMapFlag
        baddr   R0, CMC3,EQ             ; Map good
        baddr   R0, CMC2,NE             ; One copy of the map is corrupt. Overwrite it with the good copy (Y/N) ?
        BL      message_gswrite0
        BVS     %FT70
        LDRB    LR, [R5, #DiscFlags]
        TSTS    LR, #AltMapFlag
        BEQ     %FT70

        BL      TerseConfirm    ;(V->Z,V)
        BVS     %FT70
        BNE     %FT70
        MOV     R1, #DiscOp_WriteSecs :OR: DiscOp_Op_IgnoreEscape_Flag
 [ BigMaps
        LDRB    R7, [R5, #DiscRecord_BigMap_NZones2]
        LDRB    R9, [R5, #DiscRecord_NZones]
        ADD     R9, R9, R7, LSL #8
        LDRB    r7, [R5, #DiscRecord_Log2SectorSize]
 |
        LDRB    R7, [R5, #DiscRecord_Log2SectorSize]
        LDRB    R9, [R5, #DiscRecord_NZones]
 ]
        BL      MapDiscAdd              ;(R5,R7,R9->R2)
        MOV     R3, R10
        MOV     R4, R9, LSL R7
        BL      RetryDiscOp     ;(R1-R4->R0,R2-R4,V)
        LDRVC   LR, [R5, #DiscFlags]
        BICVC   LR, LR, #AltMapFlag
        STRVC   LR, [R5, #DiscFlags]
70
        BL      UnlockMap
80
        BLVS    FindErrBlock    ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "R7-R11,SB,PC"


; >>>>>>>>>
; DoCompact
; >>>>>>>>>

DoCompact
        SemEntry  Flag,Dormant          ;leaves SB,LR stacked
        Push    "R7-R11"
        TEQS    R1, #0
        MOV     R1, R0
        BLEQ    IdentifyCurrentDisc     ;(->R0,R3,V) if disc param missing
        MOVNE   R2, #MustBeDisc
        BLNE    FullLookUp              ;(R1,R2->R0-R6,C,V)
        BLVC    BeforeAlterFsMap        ;(R3->R0,V)
        BVS     %FT99

        BL      DiscWriteBehindWait             ;(R3)
        ; Check for drive type based on drive number, not disc number
        MOV     LR, R3, LSR #(32-3)
        DiscRecPtr  LR, LR
        LDRB    LR, [LR, #DiscFlags]
        TST     LR, #FloppyFlag
        LDRB    LR, Interlocks
        Push    "LR"
        LDREQ   R0, WinnieProcessBlk
        LDRNE   R0, FloppyProcessBlk
        BL      ClaimController         ;(R0)
        BL      TestMap         ;(R3->Z)
        BL      DiscAddToRec    ;(R3->LR)
        BNE     %FT03

 [ BigMaps
        LDRB    R0, [LR, #DiscRecord_NZones]
        LDRB    LR, [LR, #DiscRecord_BigMap_NZones2]
        ADD     R0, R0, LR, LSL #8
 |
        LDRB    R0, [LR,#DiscRecord_NZones]
 ]
        BL      CritInitReadNewFs        ;(->R10,R11)
        SUB     R0, R0, #1
01
        MOV     R1, #8*K
        BL      DefCompactZone  ;(R0,R1,R10->R0,R2,V)
        BVS     %FT02
        SUBS    R0, R0, #1
        BPL     %BT01
02
        BL      UnlockMap
        B       %FT98
03

         ^ 0
LenListPtr      # 4
LenList         # 0
        ASSERT  NewDirEntries>=OldDirEntries
        SUB     SP, SP, #(NewDirEntries+1)*4+LenList    ;space to sort lengths

        LDR     R3, [LR,#DiscRecord_Root]
        MOV     R7, R3

05
;RECURSE DOWN COMES BACK TO HERE
; R7 disc address of parent dir
; R3 disc address of sub dir to enter

        BL      GetDir          ;(R3->R0,R5,R6,V)
        BVS     %FT95

        Push    "R3"
        BL      ToParent        ;(R3,R6->R3)
        TEQS    R7, R3
        BLNE    InvalidateBufDir
        MOVNE   R3, R7
        BLNE    WriteParent     ;(R3,R6) ensure parent ptr ok for return later
        Pull    "R3"

 [ NewDirEntrySz=OldDirEntrySz   ;init ptr to last sub dir Compacted
        SUB     R6,R5,#NewDirEntrySz-DirFirstEntry
 |
        BL      TestDir      ;(R3->LR)
        SUBEQ   R6, R5, #NewDirEntrySz-DirFirstEntry
        SUBNE   R6, R5, #OldDirEntrySz-DirFirstEntry
 ]
10
        ADD     R1, SP, #LenList        ;init ptr to sort buffer
 [ NewDirEntrySz=OldDirEntrySz
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 |
        BL      TestDir      ;(R3->LR)
        MOVEQ   R11,#NewDirEntrySz
        MOVNE   R11,#OldDirEntrySz
        ADD     R4, R5, #DirFirstEntry
        SUB     R4, R4, R11
 ]
        MOV     R7, #0          ;init dir index
        B       %FT20

15
        CMPS    R6, R4          ;dont consider moving dirs already scanned as
        BLO     %FT16           ;parent ptrs to it would not be updated
        BL      ReadIntAtts
        TSTS    LR, #DirBit
        BNE     %FT18
16      BL      ReadLen         ;(R3,R4->LR)
        MOVS    R0, LR
        BLNE    RoundUp         ;(R0,R3->R0) ignore zero length files
        STRNE   R0, [R1]        ;note length
        STRNEB  R7, [R1],#4     ;bottom byte = dir index
18
        ADD     R7, R7, #1
20
 [ NewDirEntrySz=OldDirEntrySz
        LDRB    LR, [R4,#NewDirEntrySz] !
 |
        LDRB    LR, [R4,R11] !
 ]
        CMPS    LR, #" "
        BHI     %BT15

        MOV     LR, #0
        STR     LR, [R1]
        ADD     R0, SP, #LenList
        BL      Sort            ;(R0,R1)
        B       %FT50

25
        STR     R0, [SP,#LenListPtr]
        BIC     R1, LR, #&FF    ;rounded length
        AND     R4, LR, #&FF    ;dir index
 [ NewDirEntrySz=OldDirEntrySz
        ASSERT  NewDirEntrySz=26
        ADD     LR, R4, R4, LSL #1 ;*3
        ADD     R4, R4, LR, LSL #2 ;*13
        ADD     R4, R5, R4, LSL #1
 |
 ?
 ]
        ADD     R4, R4, #DirFirstEntry
        BL      InitReadOldFs   ;(R3->R9,R10,R11)
        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        MOV     R2, LR
        ADD     R0, R1, R2      ;end of file's reserved space
        MOV     R9, #-1         ;init smallest gap bigger than file
30
        BL      NextOldFs       ;(R3,R10,R11->R7,R8,R10,R11,Z)
        BEQ     %FT48           ;files exhausted
        CMPS    R8, R0
        BHS     %FT35           ;gap is after object
        CMPS    R7, R9
        MOVLO   R9, R7
        ADD     LR, R8, R7      ;end of gap
        CMPS    LR, R2
        BEQ     %FT40
        BNE     %BT30

35                  ;if gap joins end of file is there a better gap for file
        ADDEQ   LR, R1, R7      ;(file+gap) length
        CMPEQS  R9, LR
        BHS     %FT48
40                      ;here if file worth moving
        BL      ReturnWholeSpace;(R1,R2,R3->R0,V)
 [ Dev
        mess    VS, "*** ReturnWholeSpace GAVE ERROR TO *Compact ***",NL
 ]
        MOV     R8, R2
        MOV     R10,R1          ;size
        MOV     R11,#fsfile_Create
        BL      ClaimFreeSpace  ;(R3,R10,R11->R0,R2,V)
 [ Dev
        mess    VS,"*** ClaimFreeSpace GAVE ERROR TO *Compact ***",NL
 ]
        Push    "R2-R4"
        MOV     R0, #(1:SHL:UseScratchSpace) :OR: (1:SHL:UseSpareScreen) :OR: (1:SHL:UseWimpFree) :OR: (1:SHL:UseRmaHeap) :OR: (1:SHL:UseSysHeap) :OR: DiscOp_Op_ScatterList_Flag
        MOV     R3, R1          ;size
        MOV     R1, R8          ;source
        BL      MoveData        ;(R0-R3->R0-R3,V)
        Pull    "R2-R4"
        BVS     %FT95
        MOV     R0, R2
        BL      WriteIndDiscAdd ;(R0,R3,R4)

        sbaddr  LR, FirstFcb-FcbNext
        B       %FT42
41                              ;deal with relocating open files
        LDR     R0, [LR,#FcbIndDiscAdd]
        TEQS    R0, R8
        STREQ   R2, [LR,#FcbIndDiscAdd]
        LDR     R0, [LR,#FcbDir]
        TEQS    R0, R8
        STREQ   R2, [LR,#FcbDir]
42
        LDR     LR, [LR,#FcbNext];get next FCB
        TST     LR, #BadPtrBits
        BEQ     %BT41

        BL      ReadIntAtts     ;(R3,R4->LR)
        TSTS    LR, #DirBit
        BEQ     %FT48           ;skip if file

        ASSERT  LibDir=UserRootDir+4    ;deal with side effects of moving dir
        ASSERT  CurDir=LibDir+4
        ASSERT  BackDir=CurDir+4
        sbaddr  R0, UserRootDir
        ADD     R1, R0, #BackDir-UserRootDir
43
        LDR     LR, [R0],#4
        TEQS    LR, R8
        STREQ   R2, [R0,#-4]
        CMPS    R0, R1
        BLS     %BT43

        sbaddr  R0, RootCache
        MOV     R1, R0
        B       %FT45
44
        LDR     LR, [R0,#CacheDir]
        TEQS    LR, R8
        STREQ   R2, [R0,#CacheDir]
45
        LDR     R0, [R0,#CacheOlder]
        ADD     R0, SB, R0
        TEQS    R0, R1
        BNE     %BT44

48
        LDR     R0, [SP,#LenListPtr]
50
        LDR     LR, [R0],#4     ;get next entry from sorted length table
        TEQS    LR, #0
        BNE     %BT25

        LDR     LR, BufDir      ;avoid unnecessary writing of map
        CMPS    LR, #-1         ;for new floppy ids
        BL      ClearV
        BLEQ    WriteDirThenFsMap       ;(->R0,V)
        BVS     %FT95
                                ;SEARCH FOR NEXT SUB DIR
 [ NewDirEntrySz=OldDirEntrySz
55
        LDRB    LR, [R6,#NewDirEntrySz] !
 |
        BL      TestDir
        MOVEQ   R0, #NewDirEntrySz
        MOVNE   R0, #OldDirEntrySz
55
        LDRB    LR, [R6,R0] !
 ]
        CMPS    LR, #" "
        BLS     %FT60           ;no more sub dirs
        MOV     R4, R6
        BL      ReadIntAtts     ;(R3,R4->LR)
        TSTS    LR, #DirBit
        BEQ     %BT55           ;loop if file

        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        MOV     R7, R3
        MOV     R3, LR
        B       %BT05           ;recurse down dir

60                              ;dir done, return to parent
        MOV     R7, R3
        BL      TestDir
        ADDEQ   R6, R5, #NewDirSize
        ADDNE   R6, R5, #OldDirSize
        BL      ToParent        ;(R3,R6->R3)
        EORS    R0, R3, R7
        BEQ     %FT90           ;if dir = parent then must be $ so done
        BL      GetDir          ;(R3->R0,R5,R6,V)
        BVS     %FT95
        BL      AgeDir          ;(R7)
 [ NewDirEntrySz=OldDirEntrySz   ;init ptr to last sub dir Compacted
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 |
 ?
 ]

65
 [ NewDirEntrySz=OldDirEntrySz   ;init ptr to last sub dir Compacted
        LDRB    LR, [R4,#NewDirEntrySz] !
 |
 ?
 ]
        BL      ReadIndDiscAdd  ;(R3,R4->LR)
        TEQS    LR, R7
        BNE     %BT65
        MOV     R6, R4
        B       %BT10

90
        BL      SetVOnR0
95
        BL      UnlockMap
        ADD     SP, SP, #LenList+(NewDirEntries+1)*4    ;return temp space
98
        Pull    "LR"
        STRB    LR, Interlocks
99
        BLVS    FindErrBlock    ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "R7-R11,SB,PC"


; ======
; AgeDir
; ======

;entry R7 ind disc address of dir to age

AgeDir
        Push    "R0-R3,R11,LR"
        MOV     R3, R7
        BL      TryCache        ;(R3->R11,V)
        BVS     %FT90
        BL      LockDirCache
        BL      RemoveCacheDir  ;(R11)
                                        ;LINK DIR AS OLDEST
        MOV     R0, #:INDEX:RootCache
        ADD     R1, SB, R0
        LDR     R2, [R1,#CacheYounger]
        ADD     R3, SB, R2
        BL      InvalidateDirCache
        SUB     LR, R11, SB
        STR     R0, [R11,#CacheOlder]
        STR     R2, [R11,#CacheYounger]
        STR     LR, [R1, #CacheYounger]
        STR     LR, [R3, #CacheOlder]
        STR     R11,[R11,#CachePriority]        ;restore non zero priority
        BL      ValidateDirCache

        BL      UnlockDirCache
90
        STRVS   R0, [sp]
        Pull    "R0-R3,R11,PC"


; >>>>>>>>
; DoDefect
; >>>>>>>>

DC0     DCB     "DC0",0
DC1     DCB     "DC1",0
        ALIGN

DoDefect
        SemEntry   Flag,Dormant ;leaves SB,LR stacked
        Push    "R7-R11"
        MOV     R1, R0
        MOV     R2, #MustBeDisc
        BL      FullLookUp      ;(R1,R2->R0-R6,V)

        BLVC    DiscMustBeFileCore
        BVS     %FT81

        Push    "r1"
        MOV     r1, r3, LSR #(32-3)
        BL      CloseAllByDisc          ;(R1->R0,V)
        Pull    "r1"
        BLVC    BeforeAlterFsMap        ;(R3->R0,V)
        BLVC    SkipSpaces
        BLVC    CritInitReadNewFs       ;(->R10,R11)

 [ BigDisc
        ; SBP: XOS_ReadUnsigned will only work if defect is within 4G.  Need to
        ;      do >32bit number handling for big discs.  Also need to watch for
        ;      above loading of DiscSize.
        MOV     r0, r1                  ; pointer to disc addr string
        BL      ReadHex64               ; (r0->r1,r2)

        ; check for sector alignment - ie shift right by sectorsize
        ; then shift left should not change ls word.
        LDRB    LR,[R10,#ZoneHead+DiscRecord_Log2SectorSize]
        MOV     R0, R1, LSR LR
        MOV     R0, R0, LSL LR
        CMP     R1, R0
        BEQ     %FT01
        MOV     R0, #BadParmsErr        ; Bad Parameter (how user friendly!)
        BL      SetV
        B       %FT78

01
        ; check that disc address is on the disc
        LDR     LR, [R10, #ZoneHead+DiscRecord_DiscSize]
        SUBS    R0, R1, LR
        LDR     LR, [R10, #ZoneHead+DiscRecord_BigMap_DiscSize2]
        SBCS    R0, R2, LR
        BMI     %FT02                   ; if -ve then OK

        MOV     R0, #BadParmsErr
        BL      SetV
        B       %FT78
02
        LDRB    LR, [R10, #ZoneHead+DiscRecord_Log2SectorSize]
        MOV     R0, R1, LSR LR
        RSB     LR, LR, #32
        ORR     R0, R0, R2, LSL LR
 |
        MOVVC   r0, #16                 ; base 16
        ORRVC   r0, r0, #bit31 :OR: bit29 ; terminated and within range
        LDRVC   r2, [r10, #ZoneHead+DiscSize] ; bound above by DiscSize-1
        SUBVC   r2, r2, #1
        BLVC    OnlyXOS_ReadUnsigned      ;(R0-R2->R0-R2,V)
        BVS     %FT78

        ; Check also on a sector boundary
        MOV     R0, R2
        LDRB    R1, [R10,#ZoneHead+DiscRecord_Log2SectorSize]
        MOV     LR, R0, LSR R1
        TEQS    R0, LR, LSL R1          ;check disc address at sector boundary
        MOVNE   R0, #BadParmsErr
        BLNE    SetV
        BVS     %FT78
 ]
        BL      DoDefectMapOut
        BVC     %FT78

        ; Don't summarise problem on old map discs
        BL      TestMap
        BNE     %FT78

        ; Start summarising the problem
        CMPS    R8, #2                  ;V=0
        MOVEQ   R0, #DefectErr
        BLEQ    SetV

 [ DebugQ
        DREG    r3, "Entering dir tree scan on root ",cc
        DREG    r2, " and defect "
 ]
        MOVVC   R0, #"$"
        BLVC    DoXOS_WriteC
        BVS     %FT78

        MOV     R9, #0                  ;clear found flag
        MOV     R11,R2                  ;defect disc add

54      ; >> RECURSE DOWN ENTRY POINT
        BL      FindDir                 ;(R3->R0,R5,R6,V)
        BVS     %FT78
 [ BigDir
        BL      GetDirFirstEntry        ; (R3,R5->R4)
        BL      TestBigDir              ; (R3->LR,Z)
        SUBNE   R4, R4, #NewDirEntrySz  ; small dir
        SUBEQ   R4, R4, #BigDirEntrySize; big dir
 |
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 ]
        B       %FT72

57
        BL      ReadIntAtts             ;(R3,R4->LR)
        ANDS    R7, LR, #DirBit
        BEQ     %FT60
        TEQS    R9, #0
        BNE     %FT72

        MOV     R0, #"."
        BL      DoXOS_WriteC
        BLVC    TermCommon              ;(R3,R4,R5->R10) print out directory object
        BVS     %FT78

60
        BL      ReadIndDiscAdd          ;(R3,R4->LR)
        MOV     R1, LR
        BIC     R0, LR, #DiscBits
        TEQS    R8, R0, LSR #8
        BNE     %FT69

        CMPS    R7, #0                  ;V=0
        BNE     %FT61
        MOV     R0, #"."
        BL      DoXOS_WriteC
        BVS     %FT78
        BL      TermCommon              ;(R3,R4,R5->R10) print out filename
61
        Push    "R4,R5"
        BL      ReadLen                 ;(R4->LR)
        MOV     R0, LR
        MOV     R5, #0
        MOV     R9, #-1                 ;init pre gap map ptr, also match found flag
63
        MOV     R2, R1
        BL      FindFileFragment        ;(R2,R5,R9->R2,R4,R9,LR)
        CMPS    R4, R0
        MOVHI   R4, R0
        BIC     R2, R2, #DiscBits
        ADD     LR, R2, R4
        SUBS    R2, R11, R2
        CMPHS   LR, R11
        BHI     %FT66                   ;defect inside this object
        ADD     R5, R5, R4
        SUBS    R0, R0, R4
        BHI     %BT63

        Pull    "R4,R5"
        baddr   r0, DC0                 ;  must be moved
        BL      message_gswrite0
        BVC     %FT69
        BVS     %FT78

66
        ADD     R0, R5, R2
        Pull    "R4,R5"

        SUB     SP, SP, #12
        MOV     R1, SP
        MOV     R2, #12
        BL      OnlyXOS_ConvertHex8     ;(R0-R2->R0-R2)
67
        LDRB    LR, [R0], #1
        CMPS    LR, #"0"+1
        CMPLOS  R0, R1
        BLO     %BT67
        SUB     R0, R0, #1
        MOV     r4, r0
        baddr   r0, DC1                 ;  has defect at offset %0
        BL      message_gswrite01
        ADD     SP, SP, #12
        BVS     %FT78
        CMPS    R7, #0
        BNE     %FT78                   ;V=0 don't try to enter dir with defect

69
        TEQS    R7, #DirBit             ; if it's a directory
        TEQEQS  R9, #0                  ; and no match found...
        MOVEQ   R3, R1
        BEQ     %BT54
        ; THEN RECURSE DOWN >>

72
 [ BigDir
        BL      TestBigDir              ; (R3->LR,Z)
        BNE     %FT01                   ; not big dir

        ; big dir
        ADD     R4, R4, #BigDirEntrySize        ; go to next entry
        BL      BigDirFinished          ; (R4,R5->Z)
        BNE     %BT57                   ; more to do
        B       %FT02                   ; finished
01
        ; not big dir
        LDRB    LR, [R4, #NewDirEntrySz] !
        CMPS    LR, #" "
        BHI     %BT57
02
 |
        LDRB    LR, [R4, #NewDirEntrySz] !
        CMPS    LR, #" "
        BHI     %BT57
 ]

        TEQS    R9, #0
        BNE     %FT78                   ;V=0 don't return to parent once match found

        MOV     R7, R3
        BL      ToParent                ;(R3,R6->R3)
        CMPS    R3, R7
        BEQ     %FT78                   ;V=0 end when back to root if no match found

        BL      FindDir                 ;(R3->R0,R5,R6,V)
        BVS     %FT78

        BL      AgeDir                  ;(R7)
 [ BigDir
        BL      GetDirFirstEntry        ; (R3,R5->R4)
        BL      TestBigDir              ; (R3->LR,Z)
        SUBNE   R4, R4, #NewDirEntrySz
        SUBEQ   R4, R4, #BigDirEntrySize
 |
        SUB     R4, R5, #NewDirEntrySz-DirFirstEntry
 ]
75
 [ BigDir
        BL      TestBigDir              ; (R3->LR,Z)
        ADDEQ   R4, R4, #BigDirEntrySize
        ADDNE   R4, R4, #NewDirEntrySz
 |
        LDRB    LR, [R4, #NewDirEntrySz] !
 ]
        BL      ReadIndDiscAdd          ;(R3,R4->LR)
        TEQS    LR, R7
        BNE     %BT75
        MOV     R10,#DeleteChar
        BL      UnTermCommon            ;(R3,R4,R5,R10)
        MOV     R0, #DeleteChar
        BL      DoXOS_WriteC
        BVC     %BT72

78
        BL      UnlockMap
81
        BLVS    FindErrBlock            ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "R7-R11,SB,PC"


TermCommon
        ; Print out a name from a dir
        MOV     R10,#0
UnTermCommon
        ; Unprint a name from a dir
        ; Entry  R4 = pointer to entry within the directory
        ; BigDir R3 = disc address
        ;        R5 = directory start
        ; Exit   R10 corrupted for 'TermCommon'
        Push    "R0,R2,R11,LR"
 [ BigDir
        BL      TestBigDir      ;(R3->LR,Z)
        BNE     %FT47           ; when not a big dir use simpler code

        LDR     R11, [R4, #BigDirObNameLen]
        BL      GetBigDirName   ; (R4,R5->LR) get the name ptr
        MOV     R2, LR
45
        LDRB    LR, [R2], #1
        MOVS    R0, R10
        MOVEQ   R0, LR

        BL      DoXOS_WriteC
        ADDVS   SP, SP, #4*4
        BVS     %BT78
        SUBS    R11,R11,#1
        BHI     %BT45
        Pull    "R0,R2,R11,PC"
47
 ]
        ADD     R2, R4, #DirObName
        MOV     R11,#NameLen
48
        LDRB    LR, [R2], #1
        MOVS    R0, R10
        MOVEQ   R0, LR
        CMPS    LR, #DeleteChar
        CMPNES  LR, #" "
        Pull    "R0,R2,R11,PC",LS

        BL      DoXOS_WriteC
        ADDVS   SP, SP, #4*4
        BVS     %BT78
        SUBS    R11,R11,#1
        BHI     %BT48
        Pull    "R0,R2,R11,PC"

; ==============
; DoDefectMapOut
; ==============

; entry:
;       r0 = disc address to be mapped out
;       r3 = top 3 bits disc number
;       Map for disc must be BeforeAlterFsMapped

; exit:
;       V, r0=error possible
;       r8 = extra info on error (in particular the obj Id containing the defect)

DoDefectMapOut ROUT
        Push    "R0-R11,LR"

 [ DebugQ
        DREG    r0,"DoDefectMapOut(",cc
        DREG    r3,",",cc
        DLINE   ")"
 ]

        ; Can't map out defects on old maps - tough
        BL      TestMap         ;(R3->Z)
        MOVNE   R0, #DefectErr
        BLNE    SetV
 [ DebugQ
        BVC     %FT01
        DLINE   "Wrong sort of map"
01
 ]
        BVS     %FT81

        ; Get the parameters for this map
        BL      CritInitReadNewFs       ;(->R10,R11)

        ; Find the map ptr and zone for the defect
        LDR     r0, [sp, #0*4]
        BL      DiscAddToMapPtr         ;(R0,R10->R11,LR)
        MOV     R1, R11
        MOV     R0, LR

        ; Start wandering through that zone
        BL      InitZoneObj     ;(R0,R10->R8,R9,R11,LR)
        MOV     R5, LR
        TEQS    R9, R8
        MOVEQ   R9, #0
        MOV     R0, #bit31      ;rogue previous fragment
        B       %FT12

06
        TEQS    R11,R9
        BNE     %FT09
 [ BigMaps
        BL      FreeRdLinkBits  ;(R10,R11->R8,Z)
 |
        BL      RdLinkBits      ;(R10,R11->R8,Z)
 ]
        MOVEQ   R9, #0
        ADDNE   R9, R11,R8
        MOV     R8, R11
09
        MOV     R0, R11
        MOV     R11,R4
12
 [ BigMaps
        TEQS    R11,R9          ;is it a gap?
        BLEQ    FreeRdLenBits   ; yes (R10,R11->R7)
        BLNE    FragRdLenBits   ; no  (R10,R11->R7)
 |
        BL      RdLenBits       ;(R10,R11->R7)
 ]
        ADD     R4, R11,R7
        CMPS    R4, R1
        BLS     %BT06           ;loop if not reached defect fragment

        TEQS    R11,R9
        BNE     %FT51           ;defect not in a free space

        Push    "R8,R9"
 [ BigMaps
        BL      FreeRdLinkBits  ;(R10,R11->R8,Z)
 |
        BL      RdLinkBits      ;(R10,R11->R8,Z)
 ]
        MOVEQ   R9, #0
        ADDNE   R9, R11,R8
        STR     R9, [SP, #4]
        MOV     R3, R11
        BL      AllocBitWidth   ;(R10->LR)
        MOV     R9, LR
        SUB     LR, LR, #1
        BIC     R1, R1, LR      ;round defect down to start of allocation unit
        ADD     R6, R1, R9      ;earliest possible defect end
        BL      MinMapObj       ;(R10->LR)
        MOV     R2, LR

        ; Registers here:
        ; R0 = previous fragment start - top bit set if none
        ; R1 = Defect start at allocation unit boundary
        ; R2 = MinMapObj
        ; R3 = Start of free object bad block was found in
        ; R4 = Start of fragment after free fragment containing bad block
        ; R5 = end of fragment list
        ; R6 = Earliest end of defect object (based on allocation width)

15
 [ DebugQ
        DREG    r1,"Trying defect from ",cc
        DREG    r6," to "
 ]
        SUB     LR, R1, R3      ; Space before defect
        CMPS    LR, R2
        BHS     %FT18           ; Branch if enough gap before defect for a whole object

        ; Not enough room before defect to fit an object
        MOV     R1, R3
        MOVS    R11,R0
 [ BigMaps
        BLPL    FragRdLenLinkBits ;(R10,R11->R7,R8)
 |
        BLPL    RdLenLinkBits   ;(R10,R11->R7,R8)
 ]
        TEQPLS  R8, #1
        MOVEQ   R1, R0          ; EQ if previous object is a bad block object, ie
                                ; if there's no room for free space before the defect
                                ; and the previous object is a bad block then stretch
                                ; that to cover the defect

18
        SUB     LR, R4, R6      ; Space after defect
        CMPS    LR, R2
        BHS     %FT21           ; Branch if there's still room for another fragment

        ; There isn't room after the defect for an object and there's an object after
        ; the free fragment containing the defect
        MOV     R6, R4
        MOV     R11,R4

        ; Branch if there's not another fragment after this free fragment
        CMP     R4, R5
        BHS     %FT21

        ; If there is another fragment, is it a bad block fragment?
 [ BigMaps
        BL      FragRdLenLinkBits ;(R10,R11->R7,R8)
 |
        BL      RdLenLinkBits   ;(R10,R11->R7,R8)
 ]
        TEQS    R8, #1
        ADDEQ   R6, R4, R7      ; Object after defect is a bad block - stretch it down over the defect

21
        ; Now check defect fragment is big enough
        SUB     LR, R6, R1
        CMPS    LR, R2
        BHS     %FT24

        ; Defect fragment isn't big enough, so stretch defect
        ; fragment up if possible, else stretch it down and try again...
        CMPS    R6, R4
        ADDLO   R6, R6, R9
        SUBHS   R1, R1, R9
        B       %BT15

24
        Pull    "R8,R9"

        ; Write the defect fragment
        MOV     R0, #1
 [ BigMaps
        BL      FragWrLinkBits  ;(R0,R1,R10)
 |
        BL      WrLinkBits      ;(R0,R1,R10)
 ]
        SUB     R0, R6, R1
 [ DebugQ
        DREG    R0, "Writing defect block length of "
 ]
 [ BigMaps
        BL      FragWrLenBits   ;(R0,R1,R10)
 |
        BL      WrLenBits       ;(R0,R1,R10)
 ]

        ; next free fragment
        SUBS    R7, R1, R3
        MOV     R1, R8
        MOVHI   R0, R3
 [ DebugQ
        BLS     %FT01
        DREG    R0, "(a)Writing freelink to ",cc
        DREG    R1, " at "
01
 ]
        BLHI    WrFreeNext      ;(R0,R1,R10)
        MOVHI   R0, R7
        MOVHI   R1, R3
 [ DebugQ
        BLS     %FT01
        DREG    R0, "(b)Writing length of ",cc
        DREG    R1, " at "
01
 ]
 [ BigMaps
        BLHI    FreeWrLenBits   ;(R0,R1,R10)
 |
        BLHI    WrLenBits       ;(R0,R1,R10)
 ]

        ; previous free fragment
        CMPS    R4, R6
        MOVHI   R0, R6
 [ DebugQ
        BLS     %FT01
        DREG    R0, "(c)Writing freelink to ",cc
        DREG    R1, " at "
01
 ]
        BLHI    WrFreeNext      ;(R0,R1,R10)
        SUBHI   R0, R4, R6
        MOVHI   R1, R6
 [ DebugQ
        BLS     %FT01
        DREG    R0, "(d)Writing length of ",cc
        DREG    R1, " at "
01
 ]
 [ BigMaps
        BLHI    FreeWrLenBits   ;(R0,R1,R10)
 |
        BLHI    WrLenBits       ;(R0,R1,R10)
 ]

        ; previous previous free fragment
        MOV     R0, R9
 [ DebugQ
        DREG    R0, "(e)Writing freelink to ",cc
        DREG    R1, " at "
 ]
        BL      WrFreeNext      ;(R0,R1,R10)

        ; Write the whole lot out
 [ DebugQ
        DLINE   "Writing out the FsMap"
 ]
        BL      WriteFsMap      ;(->R0,V)
 [ DebugQ
        DLINE   "FsMap now written out"
 ]
        BVS     %FT78

27
        ; Now update the bad block list (if present)

        ; Pick up bad block's address
        LDR     r2, [sp, #0*4]          ; r0in

        ; Base presence of defect list on presence of defect list
        LDR     r3, [sp, #3*4]
        MOV     LR, R3, LSR #(32-3)
        DiscRecPtr LR, LR
        LDRB    LR, [LR, #DiscsDrv]
        DrvRecPtr LR, LR
        LDRB    LR, [LR, #DrvFlags]
        TST     LR, #HasDefectList
        BEQ     %FT78                   ; No defect list - sad

 [ BigDisc ;SBP: Thu 15th December 1994 - walked thru, looks OK
        BL      UpdateBadBlockList      ; UpdateBadBlockList now sep. routine
        B       %FT78
51
        ; Defect found in non-free block - has it already been mapped out?
 [ BigMaps
        BL      FragRdLinkBits  ;(R10,R11->R8,Z)
 |
        BL      RdLinkBits      ;(R10,R11->R8,Z)
 ]
        TEQS    R8, #1
        BEQ     %BT27           ; Yes, already mapped out

 [ DebugQ
        DLINE   "Not in free space"
 ]

        MOV     r0, #DefectErr
        BL      SetV
        STR     r8, [sp, #8*4]
78
81
 [ DebugQ
        DLINE   "Defect map-out attempt complete"
 ]
        STRVS   r0, [sp, #0*4]
        Pull    "R0-R11,PC"

 |

 [ DebugQ
        DLINE   "Updating the bad block list"
 ]

        MOV     LR, R3, LSR #(32-3)
        DiscRecPtr LR, LR
        LDRB    LR, [LR, #DiscsDrv]
        LDR     R0, DefectSpace
        ADD     R0, SB, R0
        ASSERT  SzDefectList = 1 :SHL: 9
        ADD     R0, R0, LR, LSL #9

        SUB     R1, R0, #4
30
        LDR     LR, [R1, #4]!
        CMPS    LR, R2
        BLO     %BT30

 [ DebugQ
        BNE     %FT01
        DLINE   "Defect already in list"
01
 ]
        BEQ     %FT78           ;defect already in list

        MOV     R4, R1
33
        TSTS    LR, #DiscBits
        LDREQ   LR, [R4, #4]!
        BEQ     %BT33

        LDR     LR, [R4, #4]!
        TEQS    LR, #0
 [ DebugQ
        BEQ     %FT01
        DLINE   "Defect list full"
01
 ]
        BNE     %FT78           ;defect list full

36
        LDR     LR, [R4, #-4]!  ;move up defect list
        STR     LR, [R4, #4]
        CMPS    R4, R1
        BHI     %BT36
        STR     R2, [R1]

        MOV     R4, R0
        MOV     R1, #0
39
        LDR     LR, [R4], #4
        TSTS    LR, #DiscBits
        EOREQ   R1, LR, R1, ROR #13
        BEQ     %BT39
        EOR     R1, R1, R1, LSR #16
        EOR     R1, R1, R1, LSR #8
        STRB    R1, [R4, #-4]

        MOV     R1, #SzDefectList
        BL      CheckSum        ;(R0,R1->R0-R2,V)
        SUB     R1, R1, #1
        STRB    R2, [R0, R1]
        MOV     R1, #DiscOp_WriteSecs :OR: DiscOp_Op_IgnoreEscape_Flag
        AND     R2, R3, #DiscBits
        ADD     R2, R2, #DefectListDiscAdd
        MOV     R3, R0
        MOV     R4, #SzDefectList
        BL      DoDiscOp        ;(R1-R4->R0,R2-R4,V)
 [ DebugQ
        DLINE   "Bad block list now written out"
 ]
        B       %FT78

51
        ; Defect found in non-free block - has it already been mapped out?
        BL      RdLinkBits      ;(R10,R11->R8,Z)
        TEQS    R8, #1
        BEQ     %BT27           ; Yes, already mapped out

 [ DebugQ
        DLINE   "Not in free space"
 ]

        MOV     r0, #DefectErr
        BL      SetV
        STR     r8, [sp, #8*4]

78

81
 [ DebugQ
        DLINE   "Defect map-out attempt complete"
 ]
        STRVS   r0, [sp, #0*4]
        Pull    "R0-R11,PC"

 ] ; BigDisc

 [ BigDisc

; ==================
; UpdateBadBlockList
; ==================

; entry:
;       r2 = disc address to be mapped out
;       r3 = top 3 bits disc number
;       Map for disc must be BeforeAlterFsMapped

; exit:
;       V, r0=error possible

UpdateBadBlockList ROUT

 [ DebugQ
        DLINE   "Updating the bad block list"
 ]

 ; keep disc record pointer in R5

        MOV     LR, R3, LSR #(32-3)
        DiscRecPtr R5, LR
        LDRB    LR, [R5, #DiscsDrv]
        LDR     R0, DefectSpace
        ADD     R0, SB, R0
        ASSERT  SzDefectList = 1 :SHL: 9
        ADD     R0, R0, LR, LSL #9

        MOV     R4, #512*1024*1024      ; 512 meg
        LDRB    LR, [R5, #DiscRecord_Log2SectorSize]   ; get sector size
        MOV     R4, R4, LSR LR
        CMP     R2, R4                  ; is the defect in the first or second list?
        BHS     %FT50                   ; in second defect list

; in first list.  shift left to get byte address

        MOV     R2, R2, LSL LR

        SUB     R1, R0, #4
30
        LDR     LR, [R1, #4]!
        CMPS    LR, R2
        BLO     %BT30

 [ DebugQ
        BNE     %FT01
        DLINE   "Defect already in list"
01
 ]
        BEQ     %FT81           ;defect already in list

        MOV     R4, R1
33
        TSTS    LR, #DiscBits
        LDREQ   LR, [R4, #4]!
        BEQ     %BT33

; found end of first list - is there a second list?

 [ UseBigFlag
        ; use BigFlag field for test
        LDRB    LR, [R5,#DiscRecord_BigMap_Flags]
        TSTS    LR, #DiscRecord_BigMap_BigFlag
        BNE     %FT40
 |
        ; use size of disc for test
        LDR     LR, [R5, #DiscRecord_BigMap_DiscSize2]
        CMPS    LR, #0
        BNE     %FT40
        LDR     LR, [R5, #DiscRecord_DiscSize]
        TSTS    LR, #DiscBits
        BNE     %FT40
 ]

; no second defect list, use normal code

35
        LDR     LR, [R4, #4]!
        TEQS    LR, #0
 [ DebugQ
        BEQ     %FT01
        DLINE   "Defect list full"
01
 ]
        BNE     %FT81           ;defect list full

36
        LDR     LR, [R4, #-4]!  ;move up defect list
        STR     LR, [R4, #4]
        CMPS    R4, R1
        BHI     %BT36
        STR     R2, [R1]

37
        MOV     R4, R0
        MOV     R1, #0
39
        LDR     LR, [R4], #4
        TSTS    LR, #DiscBits
        EOREQ   R1, LR, R1, ROR #13
        BEQ     %BT39
        EOR     R1, R1, R1, LSR #16
        EOR     R1, R1, R1, LSR #8
        STRB    R1, [R4, #-4]

        MOV     R1, #SzDefectList
        BL      CheckSum        ;(R0,R1->R0-R2,V)
        SUB     R1, R1, #1
        STRB    R2, [R0, R1]

; generate disc address
        LDRB    R1, [R5, #DiscRecord_Log2SectorSize]
        MOV     LR, #DefectListDiscAdd
        AND     R2, R3, #DiscBits
        ADD     R2, R2, LR, LSR R1

        MOV     R3, R0
        MOV     R4, #SzDefectList
        MOV     R1, #DiscOp_WriteSecs :OR: DiscOp_Op_IgnoreEscape_Flag
        BL      DoDiscOp        ;(R1-R4->R0,R2-R4,V)
 [ DebugQ
        DLINE   "Bad block list now written out"
 ]
        B       %FT81

40

; second defect list present.  Have to find its end too, and bump it up if need be

        MOV     R6, R4
        LDR     LR, [R6, #4]!
43
        TSTS    LR, #DiscBits
        LDREQ   LR, [R6, #4]!
        BEQ     %BT43

45
        LDR     LR, [R6, #4]!
        TEQS    LR, #0
 [ DebugQ
        BEQ     %FT01
        DLINE   "Defect list full"
01
 ]
        BNE     %FT81           ;defect list full

46
        LDR     LR, [R6, #-4]!  ;move up defect list
        STR     LR, [R6, #4]
        CMPS    R6, R1
        BHI     %BT46
        STR     R2, [R1]

        B       %BT37           ;use original code to checksum list

50
; here the defect is in the second defect list.  Move to this list,
; and use similar code to that above to adjust the list, then
; jump back to 37 again to checksum.

 [ UseBigFlag
        ; for safety, check if second defect list actually present
        ; if not there then do nothing.  (Could be unix disc)
        LDRB    LR, [R5, #DiscRecord_BigMap_Flags]
        CLRV    ; this is not an error condition
        TSTS    LR, #DiscRecord_BigMap_BigFlag
        BEQ     %FT81
 ]

; first, find end of the first defect list

        MOV     R6, R0
51
        LDR     LR, [R6], #4
        TSTS    LR, #DiscBits
        BEQ     %BT51

; R6 now points to start of 2nd defect list

        SUB     R1, R6, #4
52
        LDR     LR, [R1, #4]!
        CMPS    LR, R2
        BLO     %BT52

 [ DebugQ
        BNE     %FT01
        DLINE   "Defect already in list"
01
 ]
        BEQ     %FT81           ;defect already in list

        MOV     R4, R1
53
        TSTS    LR, #DiscBits
        LDREQ   LR, [R4, #4]!
        BEQ     %BT53

        LDR     LR, [R4, #4]!
        TEQS    LR, #0
 [ DebugQ
        BEQ     %FT01
        DLINE   "Defect list full"
01
 ]
        BNE     %FT81           ;defect list full

56
        LDR     LR, [R4, #-4]!  ;move up defect list
        STR     LR, [R4, #4]
        CMPS    R4, R1
        BHI     %BT56
        STR     R2, [R1]

        MOV     R4, R6
        MOV     R1, #0
59
        LDR     LR, [R4], #4
        TSTS    LR, #DiscBits
        EOREQ   R1, LR, R1, ROR #13
        BEQ     %BT39
        EOR     R1, R1, R1, LSR #16
        EOR     R1, R1, R1, LSR #8
        STRB    R1, [R4, #-4]

        MOV     R1, #SzDefectList
        BL      CheckSum        ;(R0,R1->R0-R2,V)
        SUB     R1, R1, #1
        STRB    R2, [R0, R1]

; generate disc address (sector address)
        LDRB    R1, [R5, #DiscRecord_Log2SectorSize]
        MOV     LR, #DefectListDiscAdd
        AND     R2, R3, #DiscBits
        ADD     R2, R2, LR, LSR R1

        MOV     R3, R0
        MOV     R4, #SzDefectList
        MOV     R1, #DiscOp_WriteSecs :OR: DiscOp_Op_IgnoreEscape_Flag
        BL      DoDiscOp        ;(R1-R4->R0,R2-R4,V)
 [ DebugQ
        DLINE   "Bad block list now written out"
 ]
        B       %FT81
81
 [ DebugQ
        DLINE   "Defect map-out attempt complete"
 ]
        STRVS   r0, [sp, #0*4]
        Pull    "R0-R11,PC"
 ]

; >>>>>>>>>>
; DoDismount
; >>>>>>>>>>

DoDismount ROUT
        SemEntry  Flag,Dormant          ;leaves SB,LR stacked
        Push    "R0"
 [ DebugL
 DSTRING r0, "Dismount on "
 ]
        TEQS    R1, #0
        BNE     %FT20

        BL      IdentifyCurrentDisc     ;(->R0,R3,V) If no param dismount current disc
10
        MOVVC   R1, R3, LSR #(32-3)
        BLVC    ActiveDismountDisc      ;(R1->R0,V)
        B       %FT95

20
        MOV     R1, R0                  ;string ptr
        MOV     R2, #MustBeDisc
        BLNE    FullLookUp              ;(R1,R2->R0-R6,V)
        BVC     %BT10                   ;If disc name parsed ok then dismount it

        TEQS    R0, #AmbigDiscErr
        BNE     %FT50

        MOV     R2, #0          ;clear error flag
        MOV     R3, #0
        sbaddr  R4, DiscRecs+DiscRecord_DiscName
30
        LDRB    LR, [R4,#Priority - DiscRecord_DiscName]
        TEQS    LR, #0
        BEQ     %FT40           ;unused disc rec
        BL      TestDir         ;(R3->LR,Z)
        MOVEQ   R5, #&FF        ;set up bit 7 chars mask
        MOVNE   R5, #&7F
        BL      LexEqv          ;(R1,R4,R5->LO/EQ/HI)
        BNE     %FT40           ;mismatch

        Push    "R1"
        MOV     R1, R3, LSR #(32-3)
        BL      ActiveDismountDisc ;(R1->R0,V)
        MOVVS   R2, R0
        Pull    "R1"

40
        ADD     R4, R4, #SzDiscRec
        ADDS    R3, R3, #1 :SHL: (32-3) ;inc disc num bits
        BCC     %BT30                   ;loop for next disc rec

        B       %FT60

50
        MOV     R2, R0
        LDR     R1, [SP]
        BL      ParseDrive              ;(R1->R0,V)
        BLVC    DriveContentsUnknown    ;(R0)
60
        MOV     R0, R2
        BL      SetVOnR0
95
        BL      FileCoreExit
        BLVS    FindErrBlock            ;(R0->R0,V)
        Pull    "R1,SB,PC"


; ====================
; DriveContentsUnknown
; ====================

; force Drive contents unknown
;
; entry r0 = drive

DriveContentsUnknown
        Push    "r0,lr"
 [ Debug4 :LOR: DebugL
        DREG    r0, "DriveContentsUnknown(",cc
        DLINE   ")"
 ]
        BL      UnlinkByDrive   ;(R0)
        DrvRecPtr r0, r0
        MOV     lr, #Uncertain :OR: Unknown
        STRB    lr, [r0, #DrvsDisc]
        LDRB    lr, [r0, #DrvFlags]
        BIC     lr, lr, #LastDiscOpWasFormat
        STRB    lr, [r0, #DrvFlags]
 [ DebugL
        DLINE   "LastDiscOpWasFormat clear (C)"
 ]
        Pull    "r0,pc"


; ==================
; ActiveDismountDisc
; ==================

; entry R1 = disc
; exit If error V set, R0 error

; As DismountDisc, but this dismount is triggered by the user actively dismounting
; rather than the dismount happening behind the user's back. The main difference
; being that ActiveDismountDisc sends a Service_DismountDisc.

ActiveDismountDisc ROUT
        Push    "r1,r2,r3,r11,lr"
        MOV     r11,sp

        ; Calculate length needed for a suitable stack frame and grab that
        LDR     r1, FS_Title
        BL      strlen
        ADD     r3, r3, #2+NameLen+1+3  ; 2 for ::, NameLen for disc title, 1 for terminator, 3 for round-up
        BIC     r3, r3, #3
        SUB     sp, sp, r3

        ; Copy FS_Title
        MOV     r2, sp
10
        LDRB    lr, [r1], #1
        CMP     lr, #" "
        STRHIB  lr, [r2], #1
        BHI     %BT10

        ; Copy ::
        MOV     lr, #":"
        STRB    lr, [r2], #1
        STRB    lr, [r2], #1

        ; Generate disc name or drive number as appropriate
        LDR     r1, [r11, #0*4]
        DiscRecPtr r3, r1
        ADD     r3, r3, #DiscRecord_DiscName
        LDRB    lr, [r3], #1
        CMP     lr, #" "
        BLS     %FT30

        MOV     r1, #NameLen

20
        STRB    lr, [r2], #1
        LDRB    lr, [r3], #1
        CMP     lr, #" "
        SUBHIS  r1, r1, #1
        BHI     %BT20

        B       %FT40

30
        LDRB    lr, [r3, #DiscsDrv - DiscRecord_DiscName - 1]
        EOR     lr, lr, #4              ; Convert to external numbering
        ADD     lr, lr, #"0"
        STRB    lr, [r2], #1

40
        ; Terminate the string
        MOV     lr, #0
        STRB    lr, [r2], #1

        ; Do the service
        MOV     r1, #Service_DiscDismounted
        MOV     r2, sp
 [ DebugL
        DREG    r1, "Issuing service ",cc
        DSTRING r2, " with r2="
 ]
        BL      DoXOS_ServiceCall
        LDRVC   r1, [r11, #0*4]         ; r1 in
        BLVC    DismountDisc

        MOV     sp, r11
        Pull    "r1,r2,r3,r11,pc"

; ============
; DismountDisc
; ============

; entry R1 = disc
; exit If error V set, R0 result

DismountDisc
        Push    "R0-R7,LR"

        ; Hold the disc in R6
        MOV     R6, R1

        ; Check disc in use
        DiscRecPtr  LR, R6
        LDRB    R0, [LR,#Priority]
        TEQS    R0, #0
        BEQ     %FT95

        MOV     R5, #0          ; error accumulator

        BL      CloseAllByDisc  ;(R1->R0,V)
        MOVVS   R5, R0

  [ DebugL
        DREG    R6, "Dismounting disc "
  ]
        ; Test for defect list presence properly
        DiscRecPtr  LR, R6
        LDRB    R7, [LR, #DiscsDrv]
  [ DebugL
        DREG    R7, "  = drive "
  ]
        ; If not attached to a drive skip drive specific sequence
        TEQ     R7, #8
        BEQ     %FT50

        DrvRecPtr  R2, R7

  [ DynamicMaps

        ; get rid of any free space map area

        Push    "R0-R8"
        MOV     R0, #2
        LDR     R1, [R2, #DrvsFsMapArea]
        BL      OnlyXOS_DynamicArea
        BVS     %FT01                           ; if error, then do nothing

        MOV     R0, R1
        RSB     R1, R2, #0                      ; amount to change size by
        BL      OnlyXOS_ChangeDynamicArea       ; do the change
01
        Pull    "R0-R8"

        MOV     LR, #0
        STR     LR, [R2, #DrvsFsMapSize]        ; mark the size as zero
  ]

        ; Clear the LastDiscOpWasFormat flag
        LDRB    LR, [R2, #DrvFlags]
        BIC     LR, LR, #LastDiscOpWasFormat
        STRB    LR, [R2, #DrvFlags]
 [ DebugL
        DLINE   "LastDiscOpWasFormat clear (D)"
 ]

        ; Park disc if has defect list, and is a hard disc
        TST     LR, #HasDefectList
        BEQ     %FT04                   ; no parking needed
        DiscRecPtr LR, R6               ; get disc record ptr
        LDRB    R0, [LR, #DiscRecord_Density]      ; is it a hard disc
        TEQ     R0, #0
        BNE     %FT04                   ; if not, don't park it

        MOV     R1, #DiscOp_Seek :OR: DiscOp_Op_IgnoreEscape_Flag;if winnie seek to park address given
        LDR     R2, DefectSpace         ;in defect Map
        ADD     R2, SB, R2
        ASSERT  SzDefectList = (1 :SHL: 9)
        ADD     R2, R2, R7, LSL #9
        LDR     R2, [R2,#ParkDiscAdd]

 [ BigDisc
        DiscRecPtr LR,R6
        LDR     R0, [LR, #DiscRecord_BigMap_DiscSize2]    ; get discsize2
        MOVS    R0, R0                  ; is it small (discsize < 2^29 bytes?)
        LDREQ   R0, [LR, #DiscRecord_DiscSize]
        TSTEQS  R0, #DiscBits           ; if so then ParkDiscAdd is in bytes
        LDREQB  LR, [LR,#DiscRecord_Log2SectorSize]    ; so shift it down to a sector address
        MOVEQ   R2, R2, LSR LR          ; else it's in sectors, leave it alone
        ORR     R2, R2, R6, LSL #(32-3)
  [ DebugL
        DREG    R2, "Parking at address "
  ]
        BL      DoDiscOp                ;(R1-R4->R0-R4)
04
 |
        ORR     R2, R2, R6, LSL #(32-3)
  [ DebugL
        DREG    R2, "Parking at address "
  ]
        BL      DoDiscOp                ;(R1-R4->R0-R4)
 ]

04      MOVVS   R5, R0

50
        MOV     R0, R6
        BL      FreeDiscRec     ;(R0)

        MOV     R0, R5
95
        BL      SetVOnR0
        STRVS   R0, [SP]
        Pull    "R0-R7,PC"


; >>>>>>>
; DoDrive
; >>>>>>>

DoDrive ROUT
        SemEntry   Flag,Dormant ;leaves SB,LR stacked
        MOV     R1, R0          ;string ptr
        BL      ParseAnyDrive   ;(R1->R0,Z,C,V)
        STRVCB  R0, Drive
        BLVS    FindErrBlock    ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "SB,PC"


; =======
; Confirm
; =======

; exit V=1 <=> error
; Z=1 <=> confirmed

TerseConfirm ROUT       ;entry V=0
        Push    "R0,LR"
        B       %FT10

ConfirmText
        =       "AreYouSure",0
        ALIGN

Confirm                 ;entry V don't care
        Push    "R0,LR"
        ADR     r0, ConfirmText
        BL      message_gswrite0
10
        MOVVC   r0, #15
        MOVVC   r1, #1
        SWIVC   XOS_Byte
        SWIVC   XOS_Confirm
        BLVC    ConvertEscapeToError

        SavePSR r1
        SWIVC   XOS_WriteC
        SWIVC   XOS_NewLine

        ORRVS   r1, r1, #V_bit
        BICVS   r1, r1, #Z_bit  ; NE on error
        RestPSR r1,,f
        STRVS   R0, [SP]
        Pull    "R0,PC"


; ================
; FlushAndReadChar
; ================

;In:
;Out: r0 = char read or VS and r0=error (includes escapes)
FlushAndReadChar ROUT
        Push    "R1,R2,LR"
        MOV     R0, #15
        MOV     R1, #1
        BL      OnlyXOS_Byte
        BLVC    DoXOS_ReadC
        BLVC    ConvertEscapeToError
        Pull    "R1,R2,PC"

; ====================
; ConvertEscapeToError
; ====================

; In: VC and C=escape was pressed
; Out: r0=escape error is CS in

ConvertEscapeToError ROUT
        MOVCC   pc, lr
        Push    "r1,r2,lr"

        ; Acknowledge the escape
        MOV     r0, #OsbyteAckEscape
        BL      OnlyXOS_Byte

        ; Convert to error
        MOVVC   r0, #ExtEscapeErr
        SETV

        Pull    "r1,r2,pc"


; >>>>>>
; DoFree
; >>>>>>

FC0     DCB     "FC0",0
FC1     DCB     "FC1",0
FCK0    DCB     "FCK0",0
FCK1    DCB     "FCK1",0
        ALIGN

DoFree ROUT
        SemEntry   Flag, Dormant        ;leaves SB,LR stacked

        MOV     r5, sp

        MOV     r1, r0
        BL      strlen
        MOV     r2, r3
        LDR     r1, FS_Title
        BL      strlen
        ADD     r3, r3, r2
        ADD     r3, r3, #2 + 1 + 3      ; 2 for ::, one for terminator, 3 for round-up
        BIC     r3, r3, #3

        SUB     sp, sp, r3
        MOV     r2, r1                  ; FS_Title
        MOV     r1, sp
        BL      strcpy
        BL      strlen
        ADD     r1, r1, r3
        MOV     lr, #":"
        STRB    lr, [r1], #1            ; :

        LDRB    lr, [r0]                ; 2nd : if disc specifier present
        CMP     lr, #' '
        MOVHI   lr, #':'
        STRHIB  lr, [r1], #1

        MOV     r2, r0
        LDRB    lr, [r2]                ; disc specifier (excluding : prefix)
        TEQ     lr, #":"
        ADDEQ   r2, r2, #1
        BL      strcpy

        MOV     r1, sp

 [ BigDisc
        MOV     r6, r0                  ; keep copy of command tail ptr
        MOV     r0, #FSControl_ReadFreeSpace64  ; get the full free space info
  [ DebugQ
        DSTRING r1, "ReadFreeSpace on "
  ]
        BL      DoXOS_FSControl
        BVC     %FT01   ; if no error then do not try the other FSControl

; here, we have a FS which doesn't support FSControl_ReadFreeSpace64 - try
; FSControl_ReadFreeSpace

        MOV     r0, #FSControl_ReadFreeSpace
        MOV     r1, sp                  ; restore FS name
        MOV     r2, r6                  ; restore command tail
        BL      DoXOS_FSControl

; now munge results
        MOV     r4, #0                  ; high 32 0
        MOV     r3, r2                  ; low 32
        MOV     r2, r1                  ; max object size
        MOV     r1, #0                  ; high 32 0

01
  [ DebugQ
        DREG    r0, "FreeSpace is (",cc
        DREG    r1, "",cc
        DREG    r2, ",",cc
        DREG    r3, ",",cc
        DREG    r4, "",cc
        DLINE   ")"
  ]

 |
        MOV     r0, #FSControl_ReadFreeSpace
  [ DebugQ
        DSTRING r1, "ReadFreeSpace on "
  ]
        BL      DoXOS_FSControl
  [ DebugQ
        DREG    r0, "FreeSpace is (",cc
        DREG    R1, ",",cc
        DREG    R2, ",",cc
        DLINE   ")"
  ]

 ]

        MOV     sp, r5

        BVS     %FT80

 [ BigDisc
; first, determine whether big or not.  By big here we mean >4G
        CMPS    r4, #0
        MOV     r7, r4
        BEQ     %FT50

; big disc
        SUBS    r2, r3, r0
        SBC     r3, r4, r1
; now r0,r1=free space and r2,r3=used space
        baddr   r5, FCK0
        BL      %FT90
        MOVVC   r0, r2
        MOVVC   r1, r3
        baddr   r5, FCK1, VC
        BLVC    %FT90
        B       %FT80
50
; small disc
        SUB     r3, r3, r0
        baddr   r5, FC0
        BL      %FT90
        MOVVC   r0, r3
        MOVVC   r1, #0
        baddr   r5, FC1, VC
        BLVC    %FT90
 |
        SUB     r3, r2, r0
        baddr   r5, FC0
        BL      %FT90
        MOVVC   r0, r3
        baddr   r5, FC1, VC
        BLVC    %FT90
 ]

80
        BLVS    FindErrBlock            ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "SB,PC"

 [ BigDisc
; entry r0,r1=number to subst Hex16 into %0 and right justified formatted 4byte cardinal into %1 (Kbytes)
;      r7!=0 implies big disc
;        ==0 implies small disc
;       r5 = tag
; exit  r1,r4-r6 trashed
 |
; entry r0=number to subst Hex8 into %0 and right justified formatted 4byte cardinal into %1
;       r5 = tag
; exit  r1-r2,r4-r6 trashed
 ]
90
 [ BigDisc
        Push    "r0,r2,lr"
        SUB     sp, sp, #52                             ; Make a frame to do the conversions into
 |
        Push    "r0,lr"
        SUB     sp, sp, #28                             ; Make a frame to do the conversions into
 ]

        ; Convert into Hex and Decimal
        MOV     r4, r0
 [ BigDisc
; do hex number of bytes; do M.S. word first then tack on L.S. word
        MOV     r6, r1
        MOVS    r7, r7
        BEQ     %FT10
        MOV     r0, r1
        ADD     r1, sp, #16
        MOV     r2, #12
        SWI     XOS_ConvertHex8
        BVS     %FT99
        MOV     r0, r4
        ADD     r1, sp, #24
        MOV     r2, #12
        SWI     XOS_ConvertHex8
        B       %FT20
10
        MOV     r0, r4
        ADD     r1, sp, #16
        MOV     r2, #12
        SWI     XOS_ConvertHex8

20
 |
        ADD     r1, sp, #16
        MOV     r2, #12
        SWI     XOS_ConvertHex8
 ]

 [ BigDisc
        ; first generate number of Kbytes in r0
        BVS     %FT99           ; skip code that may corrupt VS state if already VS
        MOVS    r7, r7          ; check for big/small
        MOVEQ   r0, r4          ; if small then L.S only
        MOVNE   r0, r4, LSR #10 ; else shift right by 10 and combine
        ORRNE   r0, r0, r6, LSL #22 ; with L.S. word shifted
; now have either number of bytes or number of Kbytes as appropriate in r0

        MOV     r1, sp
        MOV     r2, #16
        BL      ConvertFormattedCardinal4
        BVS     %FT99
 |
        MOVVC   r0, r4
        MOVVC   r1, sp
        MOVVC   r2, #16
        BLVC    ConvertFormattedCardinal4
        BVS     %FT99
 ]

 [ DebugQ
        DLINE   "Conversions performed successfully"
 ]

        ; Copy decimal to end of buffer
92
        LDRB    lr, [r1], #-1
        STRB    lr, [r1, r2]
        CMP     r1, r0
        BHS     %BT92

 [ DebugQ
        DLINE   "Copied to end of buffer"
 ]

        ; Pad before with spaces
        MOV     lr, #" "
95
        SUBS    r2, r2, #1
        STRHIB  lr, [r1, r2]
        BHI     %BT95

 [ DebugQ
        DLINE   "Padded"
 ]

        MOV     r0, r5
        ADD     r4, sp, #16
        ADD     r5, sp, #2      ; Right justify into a 11-2=9 width field (11 = 12-1 for the terminator)
        BL      message_gswrite02

99
 [ BigDisc
        ADD     sp, sp, #52
        STRVS   r0, [sp]
        Pull    "r0,r2,pc"
 |
        ADD     sp, sp, #28
        STRVS   r0, [sp]
        Pull    "r0,pc"
 ]

ConvertFormattedCardinal4 ROUT
        ;       R0 is the value to convert
        ;       R1 is the buffer to convert into
        ;       R2 is the buffer size
        ;       Returns
        ;       R0 entry value of R1
        ;       R1 pointer to terminating zero
        ;       R2 size remaining in buffer, R2'=R2-(R1'-R0')

FormatFrameSize * 16

        Push    "r1, r2, r3, r4-r9, lr"                 ; R3 only there to make frame right
        DEC     sp, FormatFrameSize                     ; Frame for doing the conversion into
        MOV     r2, #FormatFrameSize - 1
FakeErrorCDATBufferOverflow
        MOV     r1, sp
        SWI     XOS_ConvertCardinal4
        SUBVC   r4, r1, r0                              ; Calculate the number of digits returned
        MOVVC   r0, #-1                                 ; Current territory
        MOVVC   r1, #1                                  ; Thousands separator
        SWIVC   XTerritory_ReadSymbols
        MOVVC   r5, r0                                  ; Save pointer
        MOVVC   r0, #-1                                 ; Current territory
        MOVVC   r1, #2                                  ; Character grouping
        SWIVC   XTerritory_ReadSymbols
        BVS     ExitConvertFormatted
        MOV     r6, r0                                  ; Save pointer
        SUB     r7, r5, #1                              ; Measure the separator string
SeparatorCountLoop
        LDRB    r0, [ r7, #1 ]!
        TEQ     r0, #0
        BNE     SeparatorCountLoop
        SUB     r7, r7, r5                              ; Compute the length
        ; Work out how long the result will be
        MOV     r8, r4                                  ; Length of result
        MOV     r9, r6                                  ; Grouping format pointer
        MOV     r2, #0                                  ; Current group size
        MOV     r1, #0                                  ; Distance along the source
FormatCountLoop
        LDRB    r14, [ r9 ]
        TEQ     r14, #0
        MOVNE   r2, r14                                 ; Use this grouping
        ADDNE   r9, r9, #1                              ; Ready for the next grouping
        TEQ     r2, #0                                  ; Don't do anything if format defective
        BEQ     FormatCountDone
        ADD     r1, r1, r2
        CMP     r1, r4                                  ; Would this group put us beyond the string?
        ADDLT   r8, r8, r7                              ; Add the separator length to the string
        BLT     FormatCountLoop
FormatCountDone
        ADD     r14, sp, #FormatFrameSize               ; Entry R1
        LDMIA   r14, { r1, r2 }
        SUB     r2, r2, r8
        CMP     r2, #1                                  ; Allow for the terminating zero
        MOVMI   r2, #1                                  ; Make a buffer that is too small
        MOVMI   r0, #100                                ; For this result
        BMI     FakeErrorCDATBufferOverflow
        ADD     r1, r1, r8
        ADD     r14, sp, #FormatFrameSize + 4           ; Exit R1
        STMIA   r14, { r1, r2 }
        ADD     r4, sp, r4                              ; Trailing zero of converted source string
        MOV     r2, #0                                  ; Current group size
        STRB    r0, [ r1 ], #-1                         ; Put the terminator on the destination
FormatCopyLoop
        LDRB    r14, [ r6 ]
        TEQ     r14, #0
        MOVNE   r2, r14                                 ; Use this grouping
        ADDNE   r6, r6, #1                              ; Ready for the next grouping
        TEQ     r2, #0                                  ; Don't do anything if format defective
        MOVEQ   r2, #-1
        MOV     r9, r2                                  ; R2 is the number of chars to copy across
CharacterCopyLoop
        LDRB    r14, [ r4, #-1 ]!
        STRB    r14, [ r1 ], #-1
        TEQ     r4, sp                                  ; Have we reached the last character?
        BEQ     FinishConvertFormatted
        SUBS    r9, r9, #1
        BNE     CharacterCopyLoop
        MOVS    r0, r7                                  ; Length of the separator
        BEQ     CharacterCopyLoop                       ; No separator to copy
SeparatorCopyLoop
        SUBS    r0, r0, #1
        LDRB    r14, [ r5, r0 ]
        STRB    r14, [ r1 ], #-1
        BNE     SeparatorCopyLoop
        B       FormatCopyLoop                          ; Start back again

FinishConvertFormatted
        CLRV
ExitConvertFormatted
        INC     sp, FormatFrameSize
        STRVS   r0, [ sp, #0 ]
        Pull    "r0, r1, r2, r4-r9, pc"


; >>>>>
; DoMap
; >>>>>

MC0     DCB     "MC0",0
MC1     DCB     "MC1",0
MC2     DCB     "MC2",0
MC3     DCB     "MC3",0
MC4     DCB     "MC4",0
MC5     DCB     "MC5",0
MC6     DCB     "MC6",0
        ALIGN

DoMap ROUT
        SemEntry   Flag,Dormant ;leaves SB,LR stacked
        Push    "R7-R11"
 [ BigDisc
        SUB     SP, SP, #40     ;string buffer
 |
        SUB     SP, SP, #20     ;string buffer
 ]
        CMPS    R1, #0          ;V=0
        BLEQ    IdentifyCurrentDisc     ;(->R0,R3,V)
        BVS     %FT95
        BEQ     %FT05
        MOV     R1, R0
        MOV     R2, #MustBeDisc
        BL      FullLookUp              ;(R1,R2->R0-R6,C,V)
        BVS     %FT95
05
        BL      DiscMustBeFileCore      ;(R3->V,R0)
        BVS     %FT95
        BL      DiscAddToRec            ;(R3->LR)
 [ BigDir
        LDR     r2, [LR, #DiscRecord_BigDir_DiscVersion]
        TEQS    r2, #0
        MOVEQ   r0, #0
        MOVNE   r0, #1          ; bit 0 is big dirs flag
        LDRB    r2, [LR, #DiscRecord_IdLen]
        CMPS    r2, #15
        ORRHI   r0, r0, #2
; r0 = 0 - not a new filecore disc
; r0 = 1 - big dirs, small map
; r0 = 2 - big map, small dirs, this case is only valid for IdLen=16
; r0 = 3 - bit map, big dirs
        TEQS    r0, #1
        baddr   r0, MC4, EQ     ;(   start,  length) new map, big directories
        BEQ     %FT07
        TEQS    r0, #2
        baddr   r0, MC5, EQ     ;(   start,  length) big map, new directories
        BEQ     %FT07
        TEQS    r0, #3
        baddr   r0, MC6, EQ     ;(   start,  length) big map, big directories
        BEQ     %FT07
 ]
        LDRB    r2, [LR, #DiscFlags]
        TSTS    r2, #OldMapFlag
        BNE     %FT06
        TST     r2, #OldDirFlag
        baddr   r0, MC3, EQ     ;(   start,  length) new map, new directories
        baddr   r0, MC2, NE     ;(   start,  length) new map, old directories
        B       %FT07
06
        TST     r2, #OldDirFlag
        baddr   r0, MC1, EQ     ;(   start,  length) old map, new directories
        baddr   r0, MC0, NE     ;(   start,  length) old map, old directories
07
        BL      message_gswrite0

 [ BigDisc
; find the disc record
        BL      DiscAddToRec            ; get the disc record
        LDR     R2, [LR, #DiscRecord_BigMap_DiscSize2]    ;
        MOVS    R2, R2
        MOVNE   R0, #28                 ;field width (Big Disc)
        MOVEQ   R0, #20                 ;field width (Small Disc)
        MOV     R2, #0
 |
        MOV     R2, #0
        MOV     R0, #20                 ;field width
 ]
        BL      ScreenFormat            ;(R0->R9-R11)
        ADD     R10,R10,#1              ;separating space
        BL      %FT99
        MOV     LR, #1
        STRB    LR, LastReEnter
10
        BL      BeforeReadFsMap         ;(R3->R0,V)
        BVS     %FT95
        LDRB    LR, LastReEnter
        TEQS    LR, #0
        MOVNE   LR, #0
        STRNEB  LR, LastReEnter
        BLNE    InitReadFs              ;(R3->R9-R11)
15
        BL      NextFs                  ;(R3,R9-R11->R7-R11,Z)
        BIC     R8, R8, #DiscBits
        BLEQ    UnlockMap
        BEQ     %FT20
        CMPS    R8, R2
        BLS     %BT15
        BL      UnlockMap
        MOV     R2, R8
        MOV     R1, R7
        MOV     R7, SP
        MOV     LR, #"("
        STRB    LR, [R7],#1
        MOV     R0, R8

 [ BigDisc ; SBP: Thu 15th December 1994 - walked thru, doesn't seem
; to support >4G discs.  Changed to support them.
        Push    "R1,R2"
        LDR     R1, [R10, #ZoneHead+DiscRecord_BigMap_DiscSize2]    ; discsize2 field
        MOVS    R1, R1
        BEQ     %FT18                   ; small disc
        LDRB    LR,[R10,#ZoneHead+DiscRecord_Log2SectorSize]   ; sector size
        MOV     R1, R0,LSL LR
        RSB     LR, LR, #32
        MOVS    R0, R0, LSR LR
        BEQ     %FT16                   ; don't print number - pad spaces
        BL      PutHex2
        MOV     R0, R1
        BL      PutHexWord
        B       %FT19

; here when low address on big disc - pad with 8 spaces
16
        MOV     R0, #8
        MOV     LR, #' '
17
        STRB    LR, [R7],#1
        SUBS    R0, R0, #1
        BNE     %BT17
        MOV     R0, R1
        BL      PutHex2
        B       %FT19

; here when small disc
18
        LDRB    LR,[R10,#ZoneHead+DiscRecord_Log2SectorSize]   ; sector size
        MOV     R0, R0, LSL LR
        BL      PutHex2
; here when finished address and want to do length
19
        Pull    "R1,R2"
 |
        BL      PutHex2         ;(R0,R7->R7)
 ]
        MOV     R0, #","
        STRB    R0, [R7],#1
        MOV     R0, R1
        BL      PutHex2         ;(R0,R7->R7)
        MOV     LR, #")"
        STRB    LR, [R7],#1
        MOV     LR, #0
        STRB    LR, [R7],#1
        MOV     R7 ,SP
        BL      WriteString     ;(R7->R0,V)
        BL      %FT99
        BLVC    NextField       ;(R9-R11->R0,R11,V)
        BL      %FT99
        BVS     %FT95
        B       %BT10

20
        CMPS    R4, R6          ;V=0
        BLNE    DoXOS_NewLine   ;(->R0,V)
95
        BLVS    FindErrBlock    ;(R0->R0,V)
 [ BigDisc
        ADD     SP, SP, #40
 |
        ADD     SP, SP, #20
 ]
        BL      FileCoreExit
        Pull    "R7-R11,SB,PC"

99                      ;swap R4-R6 with R9-R11
        Push    "R4-R6,R9-R11,LR"
        Pull    "R9-R11"
        Pull    "R4-R6,PC"


RootText
 =       RootChar,0
        ALIGN

; >>>>>>>
; DoMount
; >>>>>>>

DoMount ROUT
        Push    "lr"
        MOV     r6, sp          ; Initial SP
 [ DebugQ
        DREG    r6, "r6 start "
 ]

        ; Convert no parameter to *mount <drive>
        TEQ     r1, #0
        LDREQ   r12, [r12]
        LDREQB  lr, Drive
        ASSERT  ("0" :AND: 7)=0
        EOREQ   lr, lr, #"0" :EOR: 4
        Push    "lr", EQ
        MOVEQ   r0, sp

        MOV     r1, r0

        ; Calculate the length required
        MOV     r2, #?RootLibText + 1 + 3   ; includes 0 terminator, 1 for . and 3 for rounding up
        MOV     r0, r1
10
        LDRB    lr, [r0], #1
        CMP     lr, #" "
        TEQHI   lr, #DeleteChar
        ADDHI   r2, r2, #1
        BHI     %BT10

        ; Ensure enough space for the : prefix
        LDRB    lr, [r1]
        TEQ     lr, #":"
        ADDNE   r2, r2, #1

        BIC     r2, r2, #3
        SUB     sp, sp, r2

        MOV     r2, sp


        ; :<disc>

        MOVNE   lr, #":"
        STRNEB  lr, [r2], #1

        MOV     r0, r1
20
        LDRB    lr, [r0], #1
        CMP     lr, #" "
        TEQHI   lr, #DeleteChar
        STRHIB  lr, [r2], #1
        BHI     %BT20

        ; .
        MOV     lr, #"."
        STRB    lr, [r2], #1

        ; Save position for later
        MOV     r3, r2

        ; $\0
        MOV     lr, #"$"
        STRB    lr, [r2], #1
        MOV     lr, #0
        STRB    lr, [r2], #1

        ; *Dir :<disc>.$
        MOV     r0, #FSControl_Dir
        MOV     r1, sp
 [ DebugQ
        DSTRING r1, "*Dir "
 ]
        SWI     XOS_FSControl
        BVS     %FT90

        baddr   r0, RootLibText
        MOV     r2, r3
30
        LDRB    lr, [r0], #1
        STRB    lr, [r2], #1
        TEQ     lr, #0
        BNE     %BT30

        ; Junk errors returned from these calls...

        ; *Lib :<disc>.$
        MOV     r0, #FSControl_Lib
        MOV     r1, sp
 [ DebugQ
        DSTRING r1, "*Lib "
 ]
        SWI     XOS_FSControl

        ; *NoURD
        MOV     r0, #FSControl_NoURD
 [ DebugQ
        DLINE   "*NoURD"
 ]
        SWI     XOS_FSControl

        CLRV

90
 [ DebugQ
        DREG    r6, "r6 end "
 ]
        MOV     sp, r6
        Pull    "pc"


; >>>>>>>>>>
; DoNameDisc
; >>>>>>>>>>

DoNameDisc ROUT
DoNameDisk
        Push    "r0-r4,lr"

        MOV     r4, sp

        ; Find length of 1st string
        MOV     r3, #0
        MOV     r1, r0
10
        LDRB    lr, [r1], #1
        CMP     lr, #" "
        ADDHS   r3, r3, #1
        BHS     %BT10

        ; Skip to 1st non-space
20
        LDRB    lr, [r1], #1
        TEQ     lr, #" "
        BEQ     %BT20

        SUB     r1, r1, #1

        ; Add length of 2nd string
30
        LDRB    lr, [r1], #1
        CMP     lr, #" "
        ADDHI   r3, r3, #1
        BHI     %BT30

        ; Add enough for an extra :, \0 terminators and 3 for the round-up
        ADD     r3, r3, #1+1+1+3
        BIC     r3, r3, #3
        SUB     sp, sp, r3

        ; Copy the 1st string, adding a : if there isn't one already
        MOV     r2, sp
        LDRB    lr, [r0]
        TEQ     lr, #":"
        MOVNE   lr, #":"
        STRNEB  lr, [r2], #1

40
        LDRB    lr, [r0], #1
        CMP     lr, #" "
        STRHIB  lr, [r2], #1
        BHI     %BT40
        MOV     lr, #0
        STRB    lr, [r2], #1

        MOV     r1, r2

        ; Copy the 2nd string

        ; Skip to 1st non-space
60
        LDRB    lr, [r0], #1
        TEQ     lr, #" "
        BEQ     %BT60

        SUB     r0, r0, #1

70
        LDRB    lr, [r0], #1
        CMP     lr, #" "
        STRHIB  lr, [r1], #1
        BHI     %BT70
        MOV     lr, #0
        STRB    lr, [r1], #1


        ; Do the FSControl

        MOV     r0, #FSControl_NameDisc
        MOV     r1, sp

 [ DebugQ
        DREG    r0, "OS_FSControl(",cc
        DSTRING r1, ",",cc
        DSTRING r2, ",",cc
        DLINE   ")"
 ]
        SWI     XOS_FSControl

        MOV     sp, r4
        STRVS   r0, [sp]
        Pull    "r0-r4,pc"

VerifyStr
 = "VC0",0      ;Verifying ...
VerOk
 = "VC1",0      ;Verified OK
VerRetries
 = "VC2",0      ;Verified with retries
VerBad
 = "VC3",0      ;Verify failed
        ALIGN

; >>>>>>>>
; DoVerify
; >>>>>>>>

DoVerify ROUT
        SemEntry  Flag,Dormant          ;leaves SB,LR stacked
        Push    "R7-R11"

        TEQS    R1, #0
        BLEQ    IdentifyCurrentDisc     ;(->R0,R3,V)
        MOVNE   R2, #MustBeDisc
        MOVNE   R1, R0
        BLNE    FullLookUp      ;(R1,R2->R0-R6,V)
        baddr   R0, VerifyStr,VC
        BLVC    message_gswrite0
        MOVVC   R1, #DiscOp_Restore
        AND     R2, R3, #DiscBits
        BLVC    DoDiscOp        ;(R1-R4->R0,R2-R4,V)
        BVS     %FT85

        MOV     R1, #DiscOp_Verify
        MOV     R6, #0          ;clear error flag

;        BL      DiscMustBeFileCore      ; check if not filecore disc.

;        BVS     %FT70
        BL      TestMap         ;(R3->Z)
        BNE     %FT50

        ; Verify new map
        BL      BeforeReadFsMap ;(R3->R0,V)
        BVS     %FT85
        BL      CritInitReadNewFs       ;(->R10,R11)

        SUBS    R0, R0, R0      ;init zone = 0, Z=0, C=1, HS
        B       %FT30

10
        ADD     R11,R11,R7
20
        CMPS    R11,R5
        ADDHS   R0, R0, #1
30
 [ DebugQ
        DREG    R0, "Verify zone "
 ]
        BLHS    InitZoneObj     ;(R0,R10->R8,R9,R11,LR)
        MOVHS   R5, LR
 [ BigMaps
        LDRHSB  LR, [R10,#ZoneHead+DiscRecord_NZones]
        LDRHSB  R9, [R10,#ZoneHead+DiscRecord_BigMap_NZones2]
        ADDHS   LR, LR, R9, LSL #8
 |
        LDRHSB  LR, [R10,#ZoneHead+DiscRecord_NZones]
 ]
        CMPHSS  R0, LR
        MOVHS   R7, #0
        BHS     %FT40

; SBP: 06 Feb 1997: ***** Fix to prevent silly long transfers on large discs during *verify (-ve numbers problem!)
 [ {TRUE}
        MOV     r9,r0
        BL      MapPtrToDiscAdd
        SUB     r4,r0,r2
        LDR     lr,[r10,#ZoneHead+DiscRecord_Log2SectorSize]
        MOV     r4,r4,LSL lr
        MOV     r0,r9
        CMP     r4,#1024*1024*1024
        MOV     r7,#0
        MOV     r8,#1
        BHS     %ft40
 ]
; SBP: 06 Feb 1997: ***** End of fix

 [ BigMaps
        BL      FragRdLenLinkBits ;(R10,R11->R7,R8)     ***************** nasty - fix it properly later
 |
        BL      RdLenLinkBits   ;(R10,R11->R7,R8)
 ]
        TEQS    R8, #1
        BNE     %BT10           ; Not a bad block

40
        ; Verify to 'here'
        MOV     R9, R0
        BL      MapPtrToDiscAdd ;(R3,R10,R11->R0)
        ADD     R11,R11,R7

 [ BigDisc ;SBP: Thu 15th December 1994 - walked thru, looks OK
        SUBS    R4, R0, R2      ;length to verify
        LDRB    LR, [R10, #ZoneHead+DiscRecord_Log2SectorSize]
        MOV     R4, R4, LSL LR  ;get transfer length in bytes
        BLHI    %FT90           ;verify this chunk
 |
        SUBS    R4, R0, R2      ;length to verify
        BLHI    %FT90           ;verify this chunk
 ]

        ; If bad block is last in zone skip ZoneSpare into next zone
        CMP     R11, R5
        ASSERT  DiscRecord_ZoneSpare :MOD: 4 = 2
        LDRHS   LR, [R10, #ZoneHead + DiscRecord_ZoneSpare]
        ADDHS   R11, R5, LR, LSR #16            ; Offset from R5 just in case R11 overhangs into the ZoneSpare

        BL      MapPtrToDiscAdd ;(R3,R10,R11->R0) disc add after defect
        MOV     R2, R0
        MOV     R0, R9
        TEQS    R8, #1
        BEQ     %BT20

        CMPS    R6, #1
        baddr   R0, VerOk, LO
        baddr   R0, VerRetries, EQ
        baddr   R0, VerBad, HI
        BL      message_gswrite0
        ALIGN

        B       %FT80

50
        BL      SizeLessDefects         ;(R3->LR)
        MOV     R4, LR
        BL      %FT90
60
        CMPS    R6, #1
        baddr   R0, VerOk, LO
        baddr   R0, VerRetries, EQ
        baddr   R0, VerBad, HI
        BL      message_gswrite0
        ALIGN

        B       %FT85

80
;        DLINE   "DoVerify: Calling UnlockMap"
        BL      UnlockMap
;        DLINE   "DoVerify: Done UnlockMap"
85
        BLVS    FindErrBlock            ;(R0->R0,V)
        BL      FileCoreExit
        Pull    "R7-R11,SB,PC"

90
        Push    "R0,R3,R5,R7-R9,LR"
 [ DebugQ
        DREG    R1, "Verify chunk:",cc
        DREG    R2, ",",cc
        DREG    R4, ","
 ]
        BL      DiscAddToRec    ;(R3->LR)
        MOV     R5, LR
        MOV     R9, #-1
92
        MOV     R3, #0
        BL      DoDiscOp        ;(R1-R4->R0-R4,V)
        BVC     %FT99
 [ NewErrors
        BICS    LR, R0, #&FF
        TSTNES  R0, #NewDiscErrorBit
        BEQ     %FT99
 |
        TSTS    R0, #DiscErrorBit
        BEQ     %FT99
 ]
        Push    "R1,R2"
        MOV     R1, #DiscOp_Restore
        BL      DoDiscOp        ;(R1-R4->R0-R4,V)
        Pull    "R1,R2"
        BVS     %FT99
        TEQS    R2, R9
        BEQ     %FT96

        ORR     R6, R6, #1
        MOV     R8, #VerifyRetries
        MOV     R9, R2
        BL      DoXOS_NewLine   ;(->R0,V)
        BICVC   R0, R2, #DiscBits
 [ BigDisc ;SBP: Thu 15th December 1994 Walked thru, push/pull of R1 not
; needed - use R3 instead
        LDRVCB  LR, [R5,#DiscRecord_Log2SectorSize]
        MOVVC   R3, R0, LSL LR  ;bottom 32 bits of addr
        RSBVC   LR, LR, #32     ;shift to get top 32 bits of addr
        MOVVC   R0, R0, LSR LR  ;top 32 bits
        BLVC    WrHex           ;(R0->R0,V) - do top 32 bits of number
        MOVVC   R0,R3           ; - do bottom 32 bits
 ]
        BLVC    WrHex           ;(R0->R0,V)
        BLVC    DoSpace         ;(->R0,V)
94
        MOVVC   R0, #"?"
        BLVC    DoXOS_WriteC    ;(R0->R0,V)
        BVS     %FT99
        B       %BT92

96
        SUBS    R8, R8, #1
        BNE     %BT94           ;V=0

        ORR     R6, R6, #2
        MOV     R8, #VerifyRetries
        BL      FindErrBlock    ;(R0->R0,V)
        ADD     R7, R0, #4      ;also set error flag
        MOV     R0, #CR
        BL      DoXOS_WriteC    ;(R0->R0,V)
        BLVC    WriteString     ;(R7->R0,V)
        BVS     %FT99

        LDRB    LR, [R5,#DiscRecord_Log2SectorSize]
        MOV     R0, #1
 [ BigDisc ; SBP: Thu 15th December 1994 - walked thru, looks OK
        ADD     R2, R2, R0
        SUBS    R4, R4, R0, LSL LR      ;also V=0
 |
        ADD     R2, R2, R0, LSL LR
        SUBS    R4, R4, R0, LSL LR      ;also V=0
 ]
        BGT     %BT92
99
        Pull    "R0,R3,R5,R7-R9,PC",VC
        ADD     SP, SP, #7*4
        B       %BT80

        LTORG
        END