;Low Level Disc Operations
;-------------------------
;Handles read/write/verify disc operations
;
;Entry:
; R1 = bits 0 - 3 reason code
;           4 - 7 option bits
; R2 = disc address, top 3 bits drive
; R3 = pointer to buffer
; R4 = length in bytes
; R5 = pointer to alternative disc record
; R6 = pointer to defect list?
; R8 = pointer to FileCore instance private word
;
;Important registers:
; R9  = size of sector log2
; R10 = R2 on entry, is incremented on each read/write and passed back
;       as R2 on exit
; R11 = R1 on entry

#set debug_Delta = 0

.Entry_DiscOp
 STMFD   R13!, {R0, R8}
 #if compile_RO < 450
   MOV     R8, PC
 #else
   MRS     R8, CPSR
 #endif
 TST     R2, #&E0000000			;is it drive 0?
 BNE     Check_Floppy_Support		;NO, forward request to ADFS
 TEMP    R0
 STR     R8, DiscOp_CPSR		;store CPSR for exit
 LOCK    R0

 LDR     R0, recording_addr
 TEQ     R0, #0				;are we recording?
 BNE     capture_DiscOp_read		;capture this DiscOp
 .Pass_DiscOp_to_ADFS
 LDRB    R0, Type_Mounted
 TEQ     R0, #Type_None			;do we have a floppy mounted
 BNE     Capture_DiscOp			;YES, we need to handle the request
					;fall through with R0=0
 LDRB    R0, No_Floppy_Support
 TEQ     R0, #0				;does ADFS support floppies?
 BNE     Check_Floppy_Support_drive_empty	;NO

 .Pass_DiscOp_to_ADFS2
 #if compile_RO < 450
   TEQP    R8, #0
 #else
   MSR     CPSR_f, R8
 #endif
 LDMFD   R13!, {R0, R8}
;@@@ commented lines are to capture return of ADFS_DiscOp
;STR     R14, [R13, #-4]!
;ADR     R14, _capture_return
B       pass_to_os_ADFS_DiscOp
;._capture_return
;LDR     R14, [R13], #4
;@@@

.Check_Floppy_Support
 TST     R2, #4 << 29			;is it an HD?
 BNE     Pass_DiscOp_to_ADFS2		;YES
 LDRB    R0, No_Floppy_Support
 TEQ     R0, #0				;does ADFS support floppies?
 BEQ     Pass_DiscOp_to_ADFS2		;YES

 ADR     R0, Error_Disc_Drive_Not_Known

.Check_Floppy_Support_error
 MOV     R8, #1<<31
 ORR     R0, R0, #%01 << 30		;R0 is an error pointer
 ADDS    R8, R8, R8			;set V
 ADD     R13, R13, #4			;drop R0
 LDMFD   R13!, {R8}
MOV     PC, R14

.Check_Floppy_Support_drive_empty
 ADR     R0, Error_Disc_Empty
 B       Check_Floppy_Support_error


 .Capture_DiscOp
 LDMFD   R13!, {R0, R8}			;clear stack
 STMFD   R13!, {R1, R9-R12, R14}

 BL      Prevent_ScatterList_Adds	;prevent scatterlist additions

 ;------------------------------------- Move head based on time
 LDRB    R0, Type_Mounted
 TEQ     R0, #Type_ADF			;ADF mounted?
 LDRNE   R11, head_track                ;NO,
 CMNNE   R11, #1			; |+ is the head parked?
 BEQ     Entry_DiscOp_head_parked	;YES, skip head moving code

 AND     R1, R1, #%1111
 CMP     R1, #3				;are we performing a read/write sector
 BHS     Entry_DiscOp_head_parked	;NO, skip head code

 SWI     XOS_ReadMonotonicTime		;PRM1-446
 ADR     R9, time_last
 LDR     R1, [R9]
 STR     R0, [R9]			;update time

 SUBS    R9, R0, R1			;R9 = time in centiseconds
 BMI     Entry_DiscOp_head_parked	;skip head code if negative

 LDR     R12, head_position		;current head position
 LDR     R1, [R12, #0]
 CMN     R1, #1				;end of track?
 LDRNEB  R1, [R12, #O_time]		;time offset of current sector
 TEQ     R1, #&FF			;does it have a time?
 BEQ     Entry_DiscOp_head_parked	;NO, skip moving
 ADDNE   R9, R9, R1			;NO, advance by current head time
 MOV     R1, #Track_Length_MS
 TEMP    R14				;R9=R9 MOD R1
 oDIV    R0, R9, R1			;R9 = time MOD track length in ms
 LOCK    R14

 LDRB    R14, [R5, #DR_density]		;density we're looking for
 ._L1					;now need to sector with time>R9
   LDRB    R0, [R11, #O_time]		;sector time
   TEQ     R0, #&FF			;are we at end of track?
   BEQ     _L1_exit			;YES, exit

   CMP     R0, R9			;is this sector the one we want?
   LDRHSB  R0, [R11, #O_density]	;YES, load the density
   ANDHS   R0, R0, #bits_density
   TEQHS   R0, R14			;is it the one we're looking for?

   ADDNE   R11, R11, #2*4		;NO, go to the next sector
 BNE     _L1

 ._L1_exit
 TEMP    R1
 STR     R11, head_position		;store the new head position
 LOCK    R1
 ;-------------------------------------

 .Entry_DiscOp_head_parked
 LDR     R1, [R13]			;get R1 from stack, as its now corrupt

 MOV     R10, #0
 TEMP    R9
 STR     R10, DiscOp_return_error	;clear return error
 LOCK    R9

 LDRB    R9, [R5, #DR_log2secsize]	;R9 = size of sector log2
 LDR     R0, ADFS_FS_Create_Block	;is ADFS running in sector mode?
 TST     R0, #ADFS_Sector_Aligned
 MOVEQ   R10, R2			;NO, R10 will track original disc address
 MOVNE   R10, R2, LSL R9		;YES, change to byte offset
 MOV     R11, R1			;preserve R1 in R11 for later use

 AND     R0, R1, #%1111			;what are we being called for?
 CMP     R0, #2
 BEQ     DiscOp_Write			;2 = write
 BLO     DiscOp_Read			;0 = verify / 1 = read

 CMP     R0, #4
 BLO     DiscOp_ReadTrack		;3 = read track

.Entry_DiscOp_Exit			;exit for all others restoring flags
 LDR     R1, DiscOp_CPSR
 BIC     R1, R1, #1 << 28		;clear V
 #if compile_RO < 450
   TEQP    R1, #0
 #else
   MSR     CPSR_f, R1
 #endif
LDMFD   R13!, {R1, R9-R12, PC}




;Convert_Sector_to_Addr
;----------------------
;Converts a sector offset into a byte offset
;based on current disc sig
;
;Entry:
; R5  - pointer to discrec
; R9  - log2secsize
; R10 - disc address
;        bit 0 = 0 - read / 1 - write
; R11 - DiscOp as passed by FileCore
;
;Exit:
; R2  - size of sector
; R14 - memory address of data
; V   - set on unrecoverable error

.Convert_Sector_to_Addr_Write
 STMFD   R13!, {R10, R14}
 ORR     R10, R10, #1
 BL      Convert_Sector_to_Addr
LDMFD   R13!, {R10, PC}

.Convert_Sector_to_Addr_Read_Suspend
 STMFD   R13!, {R11, R14}
 LDRB    R14, [R12, #suspend_adding_sectors - VARS]
 STRB    R11, [R12, #suspend_adding_sectors - VARS]
 STMFD   R13!, {R14}
 BL      Convert_Sector_to_Addr_Read
 LDMFD   R13!, {R11}
 STRB    R11, [R12, #suspend_adding_sectors - VARS]
LDMFD   R13!, {R11, PC}

.Convert_Sector_to_Addr_Read
 STMFD   R13!, {R10, R14}
 BIC     R10, R10, #1
 BL      Convert_Sector_to_Addr
LDMFD   R13!, {R10, PC}

.Convert_Sector_to_Addr
 STMFD   R13!, {R0, R3-R4, R11-R12, R14}	;NOTE R11 is pulled in _check_for_add_sector

;@@@@@@ fix, needs to convert disc size before checking
; LDR     R12, [R5, #DR_discsize]	;get disc size from disc rec
; CMP     R10, R12			;is the disc address within the disc
; MOVHS   R0, #Error_Bad_Parameters	;NO?
; BHS     Convert_Sec_to_Adr_exit	;|+ exit with error

 MOV     R0, R10, LSR R9		;R0 = disc address in sectors
 LDRB    R12, [R5, #DR_secspertrack]	;R12 = sectors per track
 M_DIV   R0, R12, R11			;R0 (Track) = R0/R12  R11=remainder (Sector)
 
 LDRB    R12, Type_Mounted
 TEQ     R12, #Type_JFD			;JFD?
 BEQ     _ignore_sequencing		;YES, we can ignore sequencing
 LDRB    R2, [R5, #DR_lowsector]
 TST     R2, #1<<6			;sequenced?
 BEQ     _not_sequenced			;NO
 MOV     R0, R0, LSL #1			;YES, double track to compensate
 TST     R2, #1<<7
 MOVNE   R14, #(40*2)-1			;40 tracks
 MOVEQ   R14, #(80*2)-1			;80 tracks
 CMP     R0, R14			;side 1
 SUBHI   R0, R0, R14			;flip to interleave
 ._not_sequenced
 TEQ     R12, #Type_ADF			;ADF?
 BEQ     Convert_Sector_to_ADF_Addr	;YES
 ._ignore_sequencing

 CMP     R0, #Max_Track_Count		;check if the track is beyond our max
 MOVHS   R0, #Error_Bad_Parameters	;YES
 BHS     Convert_Sec_to_Adr_exit	;|+ exit with error

 LDR     R12, JFD_Tracks		;track info pointer
 MOV     R4, R0				;R4=track no
 LDR     R12, [R12, R0, LSL #2]		;R12 = offset in sector info table
 CMN     R12, #1			;is the track pointer -1
 BEQ     _check_for_add_sector		;YES

					;now, find the sector
 LDR     R0, JFD_Sectors		;sector info pointer
 ADD     R12, R0, R12			;R12 = address of sector table
 MOV     R3, R12			;store beginning of track for head positioning
 LDR     R0, head_track			;now position head
 TEQ     R12, R0			;...if we're on the same track
 LDREQ   R12, head_position		;...position, the head
 TEMP    R14
 STRNE   R12, head_track		;otherwise begin at start of track
 STRNE   R12, head_position
 LOCK    R14
 MOV     R2, R12			;R2=original head position
 LDRB    R14, [R5, #DR_density]		;density we're looking for
 ._L1
   LDR     R0, [R12, #0]		;load the sector ID
   CMN     R0, #1			;is it -1
   MOVEQ   R12, R3			;YES, loop to start of track
   TEQEQ   R12, R2			;processed whole track?
   BEQ     _check_for_add_sector	;YES

   LDRB    R0, [R12, #O_sector_no]	;load the sector no
   TEQ     R0, R11			;is it the one we're looking for?
   LDREQB  R0, [R12, #O_density]	;load the density
   ANDEQ   R0, R0, #bits_density	;mask the density bits
   TEQEQ   R0, R14			;is it the one we're looking for?
   BEQ     _L1_exit			;YES, exit
   ADD     R12, R12, #8			;go to the next sector

   TEQ     R12, R2			;have we stepped through the whole track?
 BNE     _L1				;NO

 ._check_for_add_sector
 ADR     R12, VARS

 LDRB    R0, [R12, #Type_Mounted - VARS]
 TEQ     R0, #Type_JFD			;JFD?
 LDREQB  R14, [R12, #suspend_adding_sectors - VARS]	;YES
 TEQEQ   R14, #0			;have we suspended adding sectors?
 BNE     _not_a_JFD			; YES

 LDR     R14, [R12, #JFD_tmp - VARS + JFD_MinVersion]
 SUBS    R14, R14, #JFD_Supports_Autoflush	;does it support adding sectors?
 BMI     _not_a_JFD			;NO
 
 MOV     R2, #1
 MOV     R2, R2, LSL R9			;R2=sector size
 LDR     R14, [R13, #3 * 4]		;get R11 from stack
 ANDS    R14, R14, #%1111		;Verify?
 MOVEQ   R0, #0				;YES, no error
 BEQ     Convert_Sec_to_Adr_exit	; |+  exit

 CMP     R14, #2			;Read/Write sectors?
 BLS     Add_a_blank_Sector		;YES

 ._not_a_JFD
 LDRB    R0, FDC_Error_Sector_Not_Found	;fall-back to Sector Not Found
 MOV     R14, #0
 B       Convert_Sec_to_Adr_exit

 ._L1_exit
 ADD     R2, R12, #8			;advance the disc head
 TEMP    R11
 STR     R2, head_position
 LOCK    R11
 LDR     R11, [R12, #0]			;load the sector ID
 TST     R10, #1			;write?
 ORRNE   R11, R11, #bit_delta_write	;YES, note sector is now a delta write
 STRNE   R11, [R12, #0]

 AND     R14, R11, #bits_sector_length	;restrict sector length
 ADD     R14, R14, #7			;and convert back to 128 / 256 / 512 / 1024
 MOV     R2, #1
 MOV     R2, R2, LSL R14		;R2 = sector size in bytes

 MOV     R0, #0				;assume no error
 TST     R11, #bit_not_found		;Sector Not Found?
 LDRNEB  R0, FDC_Error_Sector_Not_Found
 TST     R11, #bit_ID_CRC		;ID CRC okay?
 MOVNE   R0, #Error_ID_CRC
 TST     R11, #bit_sector_CRC		;Data CRC okay?
 LDRNEB  R0, FDC_Error_Sector_CRC
 TST     R11, #bit_slow_sector		;is this a slow sector?
 BLNE    read_slow_sector		;YES, pause
 TST     R11, #bit_DO3_data		;DiscOp 3 track data?
 MOVNE   R2, #bit_DO3_data		;YES

 LDR     R14, [R12, #4]			;load the disc byte offset of requested sector

 .Convert_Sec_to_Adr_exit
 LDR     R3, ADF_Buffer_Address		;source
 ADD     R14, R14, R3			;return the address of the requested sector

 ADDS    R3, R0, R0			;clear V and test if R0 is zero
 LDMEQFD R13!, {R0, R3-R4, R11-R12, PC}	;no error, exit

 TEMP    R3
 STR     R0, DiscOp_return_error	;store error
 LOCK    R3
 LDRB    R3, FDC_Error_Sector_CRC
 TEQ     R0, R3				;is it recoverable
 MOVNE   R3, #1<<31			;NO
 ADDNES  R3, R3, R3			;..set V
LDMFD   R13!, {R0, R3-R4, R11-R12, PC}


.Convert_Sector_to_ADF_Addr
 LDRB    R12, [R5, #DR_secspertrack]	;R12 = sectors per track
 MLA     R14, R0, R12, R11
 ADD     R2, R14, #1			;next sector
 MOV     R14, R14, LSL R9		;R14=start address
 MOV     R2, R2, LSL R9		;R10=end address

 LDR     R3, ADF_Buffer_Length
 MOV     R0, #0				;assume no error
 CMP     R2, R3			;is the sector outside the disk?
 LDRHIB  R0, FDC_Error_Sector_Not_Found	;YES, exit with error
 MOV     R2, #1
 MOV     R2, R2, LSL R9			;R2=sector size
B       Convert_Sec_to_Adr_exit




;read_slow_sector
;---------------
;pause as if reading from an actual floppy, 200cs / track
;
;Entry:
; R12 - pointer to sector ID

.read_slow_sector
 STMFD   R13!, {R0-R3, R14}

 MOV     R3, #1				;R3=retry count + 1
 LDR     R0, [R12]			;are there any errors?
 ANDS    R0, R0, #bit_ID_CRC + bit_sector_CRC
 BEQ     _no_error

 MOV     R0, #0				;get number of retries
 MOV     R1, #0
 SWI     XADFS_Retries			;PRM2-288

 MOV     R3, R2, LSR #8
 AND     R3, R3, #&FF			;R3=retry count
 ADD     R3, R3, #1			;need to add one for initial delay

 ._no_error
 LDRB    R1, [R12, #O_sector_length]	;load the sector length
 ADD     R1, R1, #7			;sector length = 128 / 256 / 512 / 1024
 MOV     R2, #1
 MOV     R2, R2, LSL R1			;R2 = sector size in bytes

 LDRB    R0, [R12, #O_density]		;calculate time delay based on density

 TEQ     R0, #1				;SD
 MOVEQ   R1, #160			;|  125kbit/sec SD (160 bytes / cs)
 ADDEQ   R2, R2, #62+57			;|+ add header overhead bytes PRM2-270
 TEQ     R0, #2				;DD
 MOVEQ   R1, #320			;|  250kbit/sec DD (320 bytes / cs)
 ADDEQ   R2, R2, #62+90			;|+ add header overhead bytes PRM2-270
 TEQ     R0, #4				;QD
 MOVEQ   R1, #640			;|  500kbit/sec DD (640 bytes / cs)
 ADDEQ   R2, R2, #62+90			;|+ add header overhead bytes PRM2-271

 MOV     R14, #14			;seek time in cs
 MLA     R0, R1, R14, R2		;R0 = effective bytes we're reading
 MUL     R2, R0, R3			;..then multiply by the retry count

 M_DIV   R2, R1, R0			;calculate cs, R2 = R2 / R1
					;ie bytes to read/bytes per cs
 #if compile_RO < 450			;ensure IRQ are enabled
   MOV     R1, PC
   BIC     R3, R1, #%11 << 26
   TEQP    R3, #0
 #else
   MRS     R1, CPSR
   BIC     R3, R1, #%11 << 6
   MSR     CPSR_c, R3
 #endif
 SWI     XOS_ReadMonotonicTime		;read the current time
 ADD     R2, R0, R2			;R2 is time to wait until
 ._L1
   SWI     XOS_ReadMonotonicTime
   CMP     R0, R2
 BLO     _L1
 #if compile_RO < 450
   TEQP    R1, #0
 #else
   MSR     CPSR_c, R1			;restore IRQ state
 #endif

LDMFD   R13!, {R0-R3, PC}




;DiscOp 1
;--------
;Entry:
; R0 = 0 - verify / 1 - read
; R3 = pointer to buffer
; R4 = length in bytes
; R5 = pointer to disc record
; R9 = log2secsize
;R10 = disc address in bytes
;R11 = R1 as passed by FileCore

;Exit:
; R4  - bytes remaining on error, zero if successful
; R10 - updated

.DiscOp_Read
 TST     R11, #ScatterList_Op
 BNE     _Read_ScatterList		;deal with scatterlist

 TEQ     R4, #0				;if no bytes requested, exit immediately
 BEQ     Entry_DiscOp_Exit

 ADR     R14, non_scatter_original
 STMIA   R14, {R3, R4, R10}		;if we get a CRC error, exit with these
 ADR     R14, DiscOp_local_scatter
 STMIA   R14, {R3, R4}			;use scatterlist to perform read
 ADD     R3, R3, R4			;set R3 to final address
 TEMP    R1
 STR     R3, non_scatter_R3_return	;..and save so we can restore on exit from
 LOCK    R1
 MOV     R3, R14			;..the scatterlist code

;fall through to DiscOp_Read_ScatterList

;Entry:
; R11 - R1 as passed by FileCore

 ._Read_ScatterList
   LDMIA   R3, {R1, R12}		;read the address and length

   CMN     R1, #Scatter_List_End	;if <= -65536 the scatter list looped
   #if compile_RO == 311
     ADDCS   R3, R3, R1			;...go back to the start of the list
     LDMCSIA R3, {R1, R12}		;...and load the next pair
   #else
     TEQCS   R12, #0			;...and R12=0
     ADDEQ   R3, R3, R1			;...go back to the start of the list
     LDMEQIA R3, {R1, R12}		;...and load the next pair
   #endif
   TEQ     R12, #0			;are we at the end of the list?
   BEQ     DiscOp_ScatterList_exit

   BL      Convert_Sector_to_Addr_Read	;convert it to our internal address
   BVS     DiscOp_ScatterList_exit	;exit on critical error
   MOV     R0, R14

   CMP     R12, R2			;is this scatter entry more than one sector?
   MOVLS   R2, R12			;NO, we can read in one go

   TST     R11, #Background_Op		;are we running a background transfer?
   BNE     _P1				;YES, skip foreground limit check
   CMP     R2, R4
   MOVHI   R2, R4			;cap transfer to foreground limit
   ._P1

   ANDS    R14, R11, #%1111		;are we performing a Verify?
   ADDEQ   R1, R1, R2			;YES, increase the address
   BLNE    MemCopy			;NO
   ADD     R10, R10, R2			;next disc address
					;now update the address / bytes remaining
   SUBS    R12, R12, R2			;reduce remainder
   STMNEIA R3, {R1, R12}		;NO, we need to stay on it then
   STMEQIA R3!, {R1, R12}		;YES, go to next

   SUBS    R4, R4, R2			;reduce foreground bytes remaining
   TSTEQ   R11, #Background_Op		;are we performing a background transfer?
 BNE     _Read_ScatterList		;loop while bytes still remaining


;Entry
; R3  - scatter list pointer
; R4  - bytes remaining, if any
; R10 - final disc address
; R11 - R1 as passed by FileCore

.DiscOp_ScatterList_exit
 LDR     R0, ADFS_FS_Create_Block	;do we need to release an FIQ?
 TST     R0, #Floppy_FIQ
 LDRNE   R12, ADFS_FileCore_Private	;YES
 ADRNE   R0, FileCore_Release_FIQ	;|
 MOVNE   R14, PC			;|
 LDRNE   PC, [R0, #0]			;|+ notify FileCore to release

 TST     R11, #ScatterList_Op		;was this a scatter list?
 BNE     DiscOp_ReadWrite_Exit		;YES, don't check R3 return value
 LDR     R0, DiscOp_return_error	;error to return, if any
 LDRB    R14, FDC_Error_Sector_CRC
 TEQ     R0, R14			;CRC error?
 TEQNE   R0, #Error_ID_CRC		;ID CRC error?
 ADREQ   R14, non_scatter_original
 LDMEQIA R14, {R3, R4, R10}		;YES, restore R3, R4 to the original values
 BEQ     DiscOp_ReadWrite_Exit
 LDR     R3, non_scatter_R3_return	;NO,  restore R3 to the updated address

 TST     R11, #Background_Op		;background discop?
 SUBEQ   R3, R3, R4			;NO, correct address in case R4 isn't zero

.DiscOp_ReadWrite_Exit
 LDR     R0, ADFS_FS_Create_Block
 TST     R0, #ADFS_Sector_Aligned	;is ADFS running in sector mode?
 MOVEQ   R2, R10			;NO, leave as bytes
 MOVNE   R2, R10, LSR R9		;YES, convert back to sectors

 LDR     R0, DiscOp_return_error	;error to return, if any
 TEQ     R0, #0				;NO, other error?
 BNE     DiscOp_Error_Exit		;...exit with error

 MOV     R4, #0				;no bytes left to copy

 TST     R11, #Background_Op		;are we running a background transfer?
 BEQ     _not_background_op		;NO
 BL      Find_ScatterList_Start		;YES, find start of scatterlist
 STR     R0, [R14, #-4]			;clear process active bit PRM2-604
 LDR     R12, ADFS_FileCore_Private	;notify FileCore transfer is complete
 ADR     R1, FileCore_Notify_Background
 MOV     R14, PC
 LDR     PC, [R1, #0]

 ._not_background_op
 LDR     R1, DiscOp_CPSR		;restore flags
 BIC     R1, R1, #1 << 28		;clear V
 #if compile_RO < 450
   TEQP    R1, #0
 #else
   MSR     CPSR_f, R1
 #endif
LDMFD   R13!, {R1, R9-R12, PC}




;DiscOp_Error_Exit
;-----------------
;Exits with a disc error, or pointer to error block for
;background transfers
;
;Entry:
;R0  = error
;R1  = pointer to start of scatterlist
;R3  = scatterlist that errored
;R4  = bytes remaining, negative if error occurred during background part
;R10 = disc address in bytes
;R11 = DiscOp R1

;References:
; www.riscosopen.org/wiki/documentation/show/FileCore Error Numbers
; PRM2-604 - scatterlist structure / returning errors during background transfer

.DiscOp_Error_Exit
 CMP     R0, #0				;0 means no error
 CMPNE   R0, #&80			;FileCore errors?
 BHS     _exit				;YES, exit with error in R0

 LDR     R9, ADFS_FS_Create_Block	;build error code into R0
 TST     R9, #New_Style_Errors		;is ADFS using new style errors?
 MOVEQ   R0, R0, LSL #24		;NO
 ORREQ   R0, R0, #%10<<30		; |   set error flag
 ORREQ   R0, R0, R10, LSR #8		; |+  disc byte address/256
 ADRNE   R12, new_style_error_block	;YES
 STRNEB  R0, [R12, #1]			; |   store error code
 STRNE   R10, [R12, #4]			; |   disc address
 ORRNE   R0, R12, #%01			; |+  set error flag

 TST     R11, #Background_Op		;are we processing a background transfer?
 BEQ     _exit				;NO, exit
 BL      Find_ScatterList_Start		;YES, find start of scatterlist

 CMP     R4, #0				;are we in the background part?
 MOVMI   R4, #0				;YES, clear foreground bytes remaining

 TST     R9, #New_Style_Errors		;is ADFS using new style errors?
 MOVNE   R10, R3, LSR #2		;YES, get bits 2-31 of scatter list
 STMNEDB R14, {R0, R10}			;|+   store error and scatter entry PRM2-604
 STMEQDB R14, {R0, R3}			;NO,  store error and scatter entry PRM2-604

 LDR     R12, ADFS_FileCore_Private
 ADR     R1, FileCore_Notify_Background
 MOV     R14, PC
 LDR     PC, [R1, #0]			;notify FileCore transfer is complete

 ._exit
 LDR     R1, DiscOp_CPSR		;restore flags
 ORR     R1, R1, #1 << 28		;set V
 #if compile_RO < 450
   TEQP    R1, #0
 #else
   MSR     CPSR_f, R1
 #endif
LDMFD   R13!, {R1, R9-R12, PC}




;DiscOp_Error
;------------
;Stores the error in R0 and exists via the correct method
;
;Entry:
; R0 = error number
;R11 = R1 as passed by FileCore

.DiscOp_Error
 TEMP    R2
 STR     R0, DiscOp_return_error
 LOCK    R2
 TST     R11, #ScatterList_Op
 BNE     DiscOp_ScatterList_exit
B       DiscOp_ReadWrite_Exit




;DiscOp 2
;--------
;Entry:
; R3 = pointer to buffer
; R4 = length in bytes
; R5 = pointer to disc record
; R9 = log2secsize
;R10 = disc address in bytes
;R11 = R1 as passed by FileCore

;Exit:
; R4  - bytes remaining on error, zero if successful
; R10 - updated

.DiscOp_Write
 ADR     R14, VARS
 LDR     R14, [R14, #JFD_tmp - VARS + JFD_Bit_Flags]
 TST     R14, #JFD_BF_WriteProtect	;is the disc write protected?
 MOVNE   R0, #Error_Write_Protected	;YES, return error
 BNE     DiscOp_Error

 TST     R11, #ScatterList_Op		;are we processing a scatterlist?
 BNE     _Write_ScatterList		;YES

 TEQ     R4, #0				;if no bytes requested, exit immediately
 BEQ     Entry_DiscOp_Exit

 ADR     R14, non_scatter_original
 STMIA   R14, {R3, R4}			;if we get a CRC, we exit with these in R3, R4
 ADR     R14, DiscOp_local_scatter
 STMIA   R14, {R3, R4}			;use scatterlist to perform read
 ADD     R3, R3, R4			;set R3 to final address
 TEMP    R0
 STR     R3, non_scatter_R3_return	;..and save so we can restore on exit from
 LOCK    R0
 MOV     R3, R14			;..the scatterlist code

;fall through to DiscOp_Write_ScatterList

;Entry:
; R11 - R1 as passed by FileCore

 ._Write_ScatterList
   LDMIA   R3, {R0, R12}		;read the address and length

   CMN     R0, #Scatter_List_End	;if <= -65536 the scatter list looped
   #if compile_RO == 311
     ADDCS   R3, R3, R0			;...go back to the start of the list
     LDMCSIA R3, {R0, R12}		;...and load the next pair
   #else
     TEQCS   R12, #0			;...and R12=0
     ADDEQ   R3, R3, R0			;...go back to the start of the list
     LDMEQIA R3, {R0, R12}		;...and load the next pair
   #endif

   TEQ     R12, #0			;are we at the end of the list?
   BEQ     _write_callback

   BL      Convert_Sector_to_Addr_Write	;convert discaddr to our internal disc address
   BVS     DiscOp_ScatterList_exit	;exit on unrecoverable error
   MOV     R1, R14			;memory address

   CMP     R12, R2			;is this scatter entry more than one sector?
   MOVLS   R2, R12			;NO, we can read in one go

   TST     R11, #Background_Op		;are we running a background transfer?
   BNE     _P1				;YES, skip foreground limit check
     CMP     R2, R4
     MOVHI   R2, R4			;cap transfer to foreground limit
   ._P1

   BL      MemCopy
   ADD     R10, R10, R2			;next disc sector
					;now update the address / bytes remaining
   SUBS    R12, R12, R2			;reduce remainder, have we finished scatter?
   STMNEIA R3, {R0, R12}		;NO, we need to stay on it
   STMEQIA R3!, {R0, R12}		;YES, go to next

   SUBS    R4, R4, R2			;reduce foreground remainder
   TSTEQ   R11, #Background_Op		;are we running a background transfer?
 BNE     _Write_ScatterList		;loop while bytes still remaining

 ._write_callback
 ADR     R12, VARS
 BL      Schedule_a_delta_write
 B       DiscOp_ScatterList_exit




;Schedule_a_delta_write
;----------------------
; Schedules a flush back to the floppy image in 3 seconds time
; if a JFD is mounted and it's flagged to auto-flush

;Entry:
; R12 - pointer to VARS

.Schedule_a_delta_write
 STMFD   R13!, {R0-R2, R14}

 LDRB    R1, [R12, #Type_Mounted - VARS]
 STRB    R1, [R12, #floppy_content_changed - VARS]	;floppy contents have changed
 TEQ     R1, #Type_ADF			;is in an ADF?
 BEQ     _retain			;YES
 TEQ     R1, #Type_JFD			;is it a JFD
 LDMNEFD R13!, {R0-R2, PC}		;NO

 LDR     R0, [R12, #JFD_tmp - VARS + JFD_Bit_Flags]
 TST     R0, #JFD_BF_AutoFlush		;is the image flagged to flush?
 LDMEQFD R13!, {R0-R2, PC}		;NO

 ._retain
 ADR     R0, Delta_write_callback	;clear any existing callback
 MOV     R1, R12
 SWI     XOS_RemoveTickerEvent
 MOV     R0, #300			;flush after 3 seconds
 ADR     R1, Delta_write_callback
 MOV     R2, R12
 SWI     XOS_CallAfter			;call us back
 LDMFD   R13!, {R0-R2, PC}

.Delta_write_callback
 STMFD   R13!, {R0-R1, R14}
 ADR     R0, Entry_Auto_Flush
 MOV     R1, R12
 SWI     XOS_AddCallBack		;*ADFFlush via callback
 LDMFD   R13!, {R0-R1, PC}




;DiscOp 3
;--------
;Entry:
; R1 = R1 as passed by FileCore
; R3 = pointer to buffer
; R5 = pointer to disc record
; R9 = log2secsize
;R10 = disc address in bytes
;R11 = R1 as passed by FileCore

;Exit:
;R0 - 0 on success, otherwise FDC error
;R2, R3, R8 - preserved

.DiscOp_ReadTrack
 STMFD   R13!, {R2, R3, R8}

; BIC     R11, R11, #Background_Op	;ensure its foreground
 LDRB    R12, [R5, #DR_secspertrack]	;R12 = sectors per track
 MOV     R8, R10, LSR R9		;R8 = disc address in sectors
 M_DIV   R8, R12, R0			;R8 (Track) = R8/R12  R0=remainder (Sector)

 LDR     R12, JFD_Tracks		;track info pointer
 LDR     R12, [R12, R8, LSL #2]		;R12 = offset in sector info table
 CMN     R12, #1			;is the track pointer -1
 LDREQ   R0, FDC_Error_Mark_Not_Found	;YES?
 BEQ     _Track_data_exit		;|+ unformatted, exit with error

 BL      Convert_Sector_to_Addr_Read_Suspend	;check track exists
 BVS     _Track_data_exit		;exit if it doesn't
					;R2 - bit_DO3_data set if it containts
					;DiscOp 3 data
					;R14 = pointer to data

 TST     R1, #DefectList_Op		;are we being asked for an ID list?
 LDREQB  R0, emulate_1772		;NO
 TEQEQ   R0, #1				; |  are we emulating a 1772
 BEQ     _Track_data			;YES, return the whole track

 AND     R9, R8, #1			;R9 = head
 MOV     R10, R8, LSR #1		;R1 = cylinder

 LDRB    R0, Type_Mounted
 TEQ     R0, #Type_ADF			;ADF mounted?
 BEQ     _ADF				;YES, need to handle seperately

					;now, read the sector ID's
 LDR     R0, JFD_Sectors		;sector info pointer
 ADD     R12, R0, R12			;R12 = address of sector table
 ._L1
   LDR     R0, [R12]			;load the sector ID
   CMN     R0, #1			;is it -1
   BEQ     _L1_exit			;YES, exit loop

   STRB    R10, [R3], #1		;cylinder
   STRB    R9, [R3], #1			;head
   LDRB    R1, [R12, #O_sector_no]	;load the sector no
   STRB    R1, [R3], #1
   LDRB    R1, [R12, #O_sector_length]	;load the sector length
   AND     R1, R1, #bits_sector_length	;mask it
   STRB    R1, [R3], #1

   TST     R0, #bit_slow_sector		;is this a slow sector?
   BLNE    read_slow_sector		;delay for sector read
   ADD     R12, R12, #8			;go to the next sector
 B     _L1

 ._L1_exit
; ADFS fails to update the discsize correctly on RO3.5+
 BL      Identify_Disc
 LDR     R1, [R0, #DR_log2secsize]	;get disc record
 STR     R1, [R5, #DR_log2secsize]	;update disc record
 LDR     R1, os_version
 CMP     R1, #&350 << 8			;RO3.5+
 LDRLO   R1, [R0, #DR_discsize]
 STRLO   R1, [R5, #DR_discsize]
 B       _Track_exit_no_error


;make up sectors if we have an ADF file mounted

 ._ADF
 MOV     R12, #0
 LDRB    R10, [R5, #DR_log2secsize]
 SUB     R10, R10, #7			;R10 = sector size in 1772 format

 LDRB    R0, [R5, #DR_secspertrack]	;R0 = sectors per track
 ._L2
   STRB    R1, [R3], #1			;cylinder
   STRB    R9, [R3], #1			;head
   STRB    R12, [R3], #1		;sector no
   STRB    R10, [R3], #1		;sector size

   ADD     R12, R12, #1			;next sector
   SUBS    R0, R0, #1			;until we run out of sectors
 BNE     _L2
 B       _Track_exit_no_error


;_Track_data
;-----------
;return the whole track

;Entry:
; R2 - Convert_Sector_to_Addr_Read response
._Track_data
 TEQ     R2, #bit_DO3_data		;did we get a DiscOp 3 response?
 LDRNEB  R0, FDC_Error_Mark_Not_Found	;NO,
 BNE     _Track_data_exit		; |+ exit

 LDR     R2, [R14]			;get size of track
 MOV     R1, R3				;address to copy to
 ADD     R0, R14, #4			;address of data
 BL      MemCopy

 ._Track_exit_no_error
 SUBS    R0, R0, R0			;R0=0 and clear V

 ._Track_data_exit
 LDRVSB  R0, FDC_Error_Mark_Not_Found
 LDMFD   R13!, {R2, R3, R8}
B       DiscOp_Error_Exit




;Prevent_ScatterList_Adds
;------------------------
;Prevents additions to a background transfer list
;
;Entry:
; R1 = reason
;
;Exit:
; R0 - corrupt

.Prevent_ScatterList_Adds
 TST     R1, #Background_Op		;is it a background op?
 MOVEQ   PC, R14			;NO, exit

 STR     R14, [R13, #-4]!
 SWI     XOS_IntOff
 BL      Find_ScatterList_Start		;YES, find the start
 LDR     R0, [R14, #-4]			;load status word PRM2-604
 TST     R0, #1<<30			;are extensions allowed?
 BICNE   R0, R0, #1<<30			;YES, clear "process can be extended" flag
 STRNE   R0, [R14, #-4]
 SWI     XOS_IntOn
LDR     PC, [R13], #4




;Find_ScatterList_Start
;----------------------
;Returns the address of the first entry in a scatterlist
;
;Entry:
; R3 = scatterlist pointer
;
;Exit:
; R14 - pointer to start of scatterlist

.Find_ScatterList_Start
 STMFD   R13!, {R2-R4, R14}

 ._L1					;find the start of the scatter list
   LDMIA   R3!, {R2, R4}
   CMN     R2, #Scatter_List_End	;if address <= -65536
   #if compile_RO == 311
     BCC     _L1
   #else
     TEQCS   R4, #0			;.. and length 0
     BNE     _L1			;.. we've hit the end
   #endif

 ADD     R3, R3, R2
 SUB     R14, R3, #8			;R14 is now start of list
LDMFD   R13!, {R2-R4, PC}




;Sector wasn't imaged by ADFFS, so add a blank sector
;
;Entry:
; R2  - sector size in bytes
; R4  - track no
; R5  - pointer to discrec
; R9  - log2secsize
; R11 - sector number
; R12 - pointer to VARS
; if JFD_Data-JFD_Track_Table>=[JFD_TrackSector_size] = size of Track+Sector tables
;   then shift the data up by JFD_Sector_Expansion

;Exit:
; R0  - 0 if no error occured, otherwise FDC_Error_Sector_Not_Found
; R14 - Offset of newly created sector in Data
; R2, R5, R9  - preserved
; R3, R4 - corrupt

.Add_a_blank_Sector
 STMFD   R13!, {R1, R8, R10}

#if debug_Delta == 1	;Creating sector
SWI XOS_WriteS
DCB "Creating ", 0
ALIGN
MOV R1, R4
BL outputDecimal
SWI &100+ASC(".")
MOV R1, R11
BL outputDecimal
SWI XOS_NewLine
SWI XOS_WriteS
DCB "TrackSector_size=",0
ALIGN
LDR     R1, [R12, #JFD_TrackSector_size - VARS]
BL outputHexTruncated
SWI XOS_NewLine
#endif

 LDR     R8, [R12, #JFD_Tracks - VARS]	;R8=track table pointer
 LDR     R0, [R8, R4, LSL #2]		;track offset in sector table
 CMN     R0, #1				;does the track exist?
 MOVNE   R10, #8			;R10=bytes to shift Sector table data up by
 MOVEQ   R10, #8+4			;NO, we need a sector list terminator

 LDR     R3, [R12, #JFD_TrackSector_size - VARS]
 LDREQ   R0, [R12, #JFD_Sectors - VARS]
 SUBEQ   R0, R0, R8			;R0=Track table size
 SUBEQ   R0, R3, R0			;R0=Offset in sector table
 STREQ   R0, [R8, R4, LSL #2]		;create Track->Sector list offset
#if debug_Delta == 1	;Track->Sector
SWI XOS_WriteS
DCB "Track->Sector: ", 0
ALIGN
MOV R1, R0
BL outputHexTruncated
SWI XOS_NewLine
#endif
 ADD     R3, R3, R10			;R3=required size of Track+Sector table
 MOV     R8, R0				;R8=offset in sector table

 LDR     R0, [R12, #ADF_Buffer_Address - VARS]	;start of Data
 LDR     R4, [R12, #JFD_Tracks - VARS]
 SUB     R4, R0, R4			;R4=space between Track table
					;   and Data
 CMP     R3, R4				;LS if we have space to add a sector?
#if debug_Delta == 1	;No space
BLS _skip1
SWI XOS_WriteS
DCB "No space for sector, moving Data table", 0
ALIGN
SWI XOS_NewLine
._skip1
CMP     R3, R4
#endif
 LDR     R4, [R12, #JFD_delta_offset - VARS]
 LDR     R14, [R12, #JFD_Tracks - VARS]
 LDR     R0, [R12, #ADF_Buffer_Address - VARS]
 SUB     R0, R0, R14			;area before data
 ADD     R4, R4, R0
 ADD     R4, R4, R2			;add sector size
 ADDLS   R4, R4, R10			;add the sector table entry
 ADDHI   R4, R4, #JFD_Sector_Expansion

 LDR     R0, [R12, #DA_size - VARS]	;total DA size
 CMP     R4, R0				;do we need to increase the DA size?
#if debug_Delta == 1	;DA needs extending
BLS _skip2
SWI XOS_WriteS
DCB "DA needs extending from ",0
ALIGN
MOV R1, R0
BL outputHexTruncated
SWI XOS_WriteS
DCB " to ", 0
ALIGN
MOV R1, R4
BL outputHexTruncated
SWI XOS_NewLine
._skip2
CMP     R4, R0
#endif
 BLHI    MemAlloc			;YES
#if debug_Delta == 1	;DA allocate failed
BVC _skip7
SWI XOS_WriteS
DCB "DA allocate failed: ",0
ALIGN
ADD R0, R0, #4
SWI XOS_Write0
SWI XOS_NewLine
MOV R1, #1<<31
ADDS R1, R1, R1
._skip7
#endif
 LDMVSFD R13!, {R1, R8, R10}
 LDRVSB  R0, [R12, #FDC_Error_Sector_Not_Found - VARS]	;failed to add the sector
 BVS     Convert_Sec_to_Adr_exit

; memory now allocated
 STR     R3, [R12, #JFD_TrackSector_size - VARS]	;increase Track+Sector size
#if debug_Delta == 1	;TrackSector_size
SWI XOS_WriteS
DCB "TrackSector_size=",0
ALIGN
MOV R1, R3
BL outputHexTruncated
SWI XOS_NewLine
#endif

; add the sector table entry
 LDR     R14, [R12, #ADF_Buffer_Address - VARS]
 LDR     R0, [R12, #JFD_Tracks - VARS]
 SUB     R0, R14, R0			;R0=space allocated for sectors
 CMP     R0, R3				;do we need to expand the Sector table?
 BHI     _spare_in_sector_table

; need to add some space for adding sectors
 LDR     R0, [R12, #DA_address - VARS]
 LDR     R2, [R12, #DA_size - VARS]
 ADD     R2, R2, R0				;R2=end of DA
 LDR     R0, [R12, #ADF_Buffer_Address - VARS]
#if debug_Delta == 1	;Moved Data
SWI XOS_WriteS
DCB "Moved Data from ", 0
ALIGN
MOV R1, R0
BL outputHexTruncated
SWI XOS_WriteS
DCB " to ", 0
ALIGN
#endif
 ADD     R1, R0, #JFD_Sector_Expansion
#if debug_Delta == 1
BL outputHexTruncated
SWI XOS_NewLine
#endif
 SUB     R2, R2, R1				;R2=size
 STR     R1, [R12, #ADF_Buffer_Address - VARS]	;new address of sector Data
 BL      MemMove				;shift it up

 ._spare_in_sector_table
 CMP     R10, #8+4				;are we create a new sector list?
#if debug_Delta == 1	;Creating/inserting track
BNE _skip3
SWI XOS_WriteS
DCB "Creating a new track", 0
ALIGN
B _skip4
._skip3
SWI XOS_WriteS
DCB "Inserting into existing track, R10=", 0
ALIGN
MOV R1, R10
BL  outputHexTruncated
LDR   R2, [R12, #JFD_TrackSector_size - VARS]	;NO, need to make some room
LDR   R1, [R12, #JFD_Sectors - VARS]
LDR   R14, [R12, #JFD_Tracks - VARS]
SUB   R1, R1, R14			;R1=size of track table
SUB   R2, R2, R1
SUB   R2, R2, R8			;R2-=offset in sector table
SUB   R2, R2, R10			;reduce by the amount we're shifting up by
SWI XOS_WriteS
DCB " R2=",0
ALIGN
MOV R1, R2
BL  outputHexTruncated
 LDR   R0, [R12, #JFD_Sectors - VARS]	;NO, need to move sector table entries up
 ADD   R0, R0, R8			;start of this track's sector table
 ADD   R1, R0, R10			;move up
SWI XOS_WriteS
DCB " R1=",0
ALIGN
BL  outputHexTruncated
SWI XOS_WriteS
DCB " R0=",0
ALIGN
MOV R1, R0
BL  outputHexTruncated
._skip4
SWI XOS_NewLine
CMP     R10, #8+4
#endif
 LDRNE   R2, [R12, #JFD_TrackSector_size - VARS]	;NO, need to make some room
 SUBNE   R2, R2, R8			;R2-=offset in sector table
 SUBNE   R2, R2, R10			;reduce by the amount we're shifting up by
 LDRNE   R1, [R12, #JFD_Sectors - VARS]
 LDRNE   R14, [R12, #JFD_Tracks - VARS]
 SUBNE   R1, R1, R14			;R1=size of track table
 SUBNE   R2, R2, R1			;reduce by size of track table
 LDRNE   R0, [R12, #JFD_Sectors - VARS]	;NO, need to move sector table entries up
 ADDNE   R0, R0, R8			;start of this track's sector table
 ADDNE   R1, R0, R10			;move up
 BLNE    MemMove			;make room for new sector
 ;space now allocated for sector table entry

 LDR     R4, [R12, #JFD_Sectors - VARS]
 CMP     R10, #8+4			;are we create a new sector list?
 BEQ     _skip_track_pointers		;YES

 LDR     R2, [R12, #JFD_Tracks - VARS]
#if debug_Delta == 1	;Adjusting Track pointers
SWI XOS_WriteS
DCB "Adjusting Tracks @ ", 0
ALIGN
MOV R1, R2
BL outputHexTruncated
SWI &100+ASC(":")
SWI &100+ASC(" ")
#endif
 ._update_track_pointers
   LDR     R0, [R2], #4			;R0=sector list pointer
;   CMN     R0, #1			;does track exist?
;   BEQ     _missing_track
   CMP     R0, R8			;do we need to adjust the sector table offset?
#if debug_Delta == 1
BLE _skip5
LDR R14, [R12, #JFD_Tracks - VARS]
SUB R1, R2, R14
MOV R1, R1, LSR #2
SUB R1, R1, #1
BL outputDecimal
SWI &100+ASC(" ")
CMP     R0, R8
._skip5
#endif
   ADDGT   R0, R0, R10			;YES
   STRGT   R0, [R2, #-4]
;   ._missing_track
   CMP     R2, R4			;last track
 BNE     _update_track_pointers
#if debug_Delta == 1
SWI XOS_NewLine
#endif
; we've moved the track offsets up by 8 bytes

 ._skip_track_pointers
; create the Sector->Data pointer
 ADD     R4, R4, R8			;R4=sector table entry pointer
 CMP     R10, #8+4			;are we create a new sector list?
 MVNEQ   R0, #0				;YES, terminate it
 STREQ   R0, [R4, #8]

 LDRB    R14, [R5, #DR_density]		;sector density
 SUB     R0, R9, #7			;_______s - sector size
 ORR     R0, R0, #bit_delta_write	;__B____s - delta change
 ORR     R0, R0, R11, LSL #8		;__B_nn_s - sector number
 ORR     R0, R0, R14, LSL #16		;__Bdnn_s - density
 STR     R0, [R4, #0]			;store sector header

; now work out the data offset
 LDR     R1, [R12, #ADF_Buffer_Address - VARS]
 LDR     R8, [R12, #JFD_delta_offset - VARS]
 MOV     R2, #1
 MOV     R2, R2, LSL R9			;R2=sector size in bytes
 ADD     R14, R8, R2
 STR     R8, [R4, #4]			;R8=sector offset in data
 STR     R14, [R12, #JFD_delta_offset - VARS]

#if debug_Delta == 1
STMFD   R13!, {R1}
SWI     XOS_WriteS
DCB     "Data @ ", 0
ALIGN
ADD     R1, R1, R8			;start
BL      outputHexTruncated
SWI     XOS_WriteS
DCB     " Sector size="
MOV     R1, R2
BL      outputDecimal
SWI     XOS_NewLine
LDMFD   R13!, {R1}
#endif
 ADD     R0, R1, R8			;start
 ADD     R1, R0, R2			;end
 BL      ZeroBlock			;zero the newly created sector

 BL      Schedule_a_delta_write

 MOV     R14, R8			;exit with offset in R14
 MOV     R0, #0				;no error
 LDMFD   R13!, {R1, R8, R10}
;LDRB R0, FDC_Error_Sector_CRC
B       Convert_Sec_to_Adr_exit




;capture_DiscOp_read
;-------------------
;Records DiscOp actions
;
;Entry:
; R1-R5 - as passed by FileCore
; {stack+8} - R1

;Exit:
; R0 - corrupt

.capture_DiscOp_read
 STMFD   R13!, {R1-R12, R14}

 BL      Prevent_ScatterList_Adds	;prevent scatterlist adds

 LDRB    R9, [R5, #DR_log2secsize]	;R9 = size of sector log2
 LDR     R0, ADFS_FS_Create_Block	;is ADFS running in sector mode?
 TST     R0, #ADFS_Sector_Aligned
 MOVNE   R2, R2, LSL R9			;YES, change R2 to bytes

 ADR     R8, recordStack
 STR     R1, [R8, #0*4]			;record R1 - reason
 STR     R2, [R8, #1*4]			;record R2 - disc address
 LDR     R10, [R5]			;record [R5, #0]
 STR     R10, [R8, #3*4]		;...log2secsize, secspertrack, heads, density
 LDR     R10, [R5, #DR_discsize]	;record [R5, #DR_discsize]
 STR     R10, [R8, #4*4]		;...disc size

 TST     R1, #ScatterList_Op		;is this a scatterlist?
 TSTNE   R1, #Background_Op		;YES, is it a background transfer?
 BEQ     _exit				;NO, R4 is already correct

 MOV     R4, #0				;reset byte counter
 ._L1
   LDMIA   R3, {R1, R12}		;read the address and length

   CMN     R1, #Scatter_List_End	;if <= -65536 the scatter list looped
   #if compile_RO == 311
     ADDCS   R3, R3, R1			;...go back to the start of the list
     LDMCSIA R3, {R1, R12}		;...and load the next pair
   #else
     TEQCS   R12, #0			;...and R12=0
     ADDEQ   R3, R3, R1			;...go back to the start of the list
     LDMEQIA R3, {R1, R12}		;...and load the next pair
   #endif
   ADD     R3, R3, #8

   ADD     R4, R4, R12			;increase bytes read
   TEQ     R12, #0			;are we at the end of the list?
 BNE     _L1

 ._exit
 STR     R4, [R8, #2*4]			;record R4 - length

 MOV     R2, R8
 BL      record_discop			;write the DiscOp info to file

 LDMFD   R13!, {R1-R12, R14}
B       Pass_DiscOp_to_ADFS




;record_discop
;-------------
;Writes a disc op to the record file and flushes it to disc
;
;Entry:
; R2 - address of buffer containing 5 registers
;
;Exit:
; R2 - corrupted

.record_discop
 STMFD   R13!, {R0-R1, R3-R5, R12, R14}
 ADR     R12, VARS

 #if Recording_immediate == 0
   LDR     R0, [R12, #recording_ptr - VARS]	;load current pointer
   LDR     R3, [R12, #recording_addr - VARS]	;..and start of buffer
   SUB     R1, R0, R3
   TEQ     R1, #Max_Recording_Size		;is buffer full?
   LDMEQFD R13!, {R0-R1, R3-R5, R12, PC}	;YES, exit

   LDMIA   R2, {R1, R3, R4, R5, R14}
   STMIA   R0!, {R1, R3, R4, R5, R14}		;20 bytes per entry
   STR     R0, [R12, #recording_ptr - VARS]
 #endif

;@@@ next section for debugging purposes (displays DiscOp values)
#if Display_DiscOpValues == 1
 MOV     R4, R2
 LDR     R1, [R4, #0]
 writeB  R1
 writeS  ":"
 LDR     R1, [R4, #1*4]
 writeD  R1
 writeS  " "
 LDR     R1, [R4, #2*4]
 writeD  R1
 writeS  " "
 LDR     R1, [R4, #3*4]
 writeD  R1
 writeS  " "
 LDR     R1, [R4, #4*4]
 writeD  R1
 writeS  ""
 MOV     R2, R4
#endif
;@@@

;@@@ next section for debugging purposes (saves immediately)
#if Recording_immediate == 1
 BL      resetVectors			;reset key vectors
 LDR     R1, [R12, #record_filename - VARS]		;filename
 MOV     R0, #&CF			;open existing file
 SWI     XOS_Find			;PRM2-78
 MOVVS   R0, #&8F			;create new file
 SWIVS   XOS_Find			;PRM2-78
 BVS     _exit2				;exit on error
 MOV     R1, R0				;file handle

 MOV     R3, #5 * 4			;length to save
 MOV     R0, #1				;specified file pointer
 LDR     R4, [R12, #recording_ptr - VARS]	;load current pointer
 SWI     XOS_GBPB			;PRM2-68
 STRVC   R4, [R12, #recording_ptr - VARS]	;update pointer
 MOV     R0, #0				;close file
 SWI     XOS_Find			;PRM2-77
 ._exit2
 BL      resetVectors			;reset key vectors
#endif
;@@@

;@@@ next section for debugging purposes (slows if CTRL pressed)
#if CTRL_PauseDebug == 1
 MOV     R0, #202
 MOV     R1, #0
 MOV     R2, #&FF			;Read keyboard status
 SWI     XOS_Byte			;PRM1-914
 TST     R1, #1<<6			;was CTRL key pressed?
 LDMEQFD R13!, {R0-R1, R3-R5, R12, PC}	;NO, exit

 MOV     R0, #176
 MOV     R1, #0
 MOV     R2, #255
 SWI     XOS_Byte
 ADD     R3, R1, #1
 TEQ     R3, #50
 MOVEQ   R3, #0
 ._L_pause
     MOV     R0, #176			;pause for 50ms
     MOV     R1, #0
     MOV     R2, #255
     SWI     XOS_Byte
     TEQ     R1, R3
   BNE     _L_pause

   MOV     R0, #202			;pause whilst CTRL down
   MOV     R1, #0
   MOV     R2, #&FF			;Read keyboard status
   SWI     XOS_Byte			;PRM1-914
   TST     R1, #1<<6			;was CTRL key pressed?
 BNE     _L_pause			;YES, stay paused
#endif
;@@@
LDMFD   R13!, {R0-R1, R3-R5, R12, PC}
