;MiscOp Entry
;------------
;Entry:
; R0 = reason
; R1 = drive


.Entry_MiscOp
 STMFD   R13!, {R1, R2, R12, R14}
 ADR     R12, VARS
 #if compile_RO < 450
   MOV     R2, PC
 #else
   MRS     R2, CPSR
 #endif
 CMP     R1, #4				;is it an HD?
 BHS     _Pass_to_ADFS2			;YES
 TEQ     R1, #0				;is it drive 0?
 BNE     _Pass_to_ADFS			;NO, forward request to ADFS

;@@@ for debugging purposes to record MiscOps
#if Save_MiscOpValues == 1
 STMFD   R13!, {R2}			;preserve R2
 LDR     R2, [R12, #recording_addr - VARS]
 TEQ     R2, #0				;are we recording?
 ADDNE   R2, R12, #recordStack - VARS	;YES
 MVNNE   R1, #0
 STRNE   R0, [R2, #0*4]
 STRNE   R1, [R2, #1*4]
 STRNE   R1, [R2, #2*4]
 STRNE   R1, [R2, #3*4]
 STRNE   R1, [R2, #4*4]
 BLNE    record_discop			;write the DiscOp info to file
 LDMFD   R13!, {R2}			;preserve R2, R14
#endif

 LDRB    R1, [R12, #Type_Mounted - VARS]
 TEQ     R1, #Type_None			;do we have a floppy mounted
 BNE     _Capture_MiscOp		;YES, we need to handle the request
					;fall through with R1 = 0
 LDRB    R1, [R12, #No_Floppy_Support - VARS]
 TEQ     R1, #0				;does ADFS support floppies?
 ADRNE   R0, Error_Disc_Empty		;NO
 BNE     _no_floppy_support

 ._Pass_to_ADFS
 LDRB    R1, [R12, #No_Floppy_Support - VARS]
 TEQ     R1, #0				;does ADFS support floppies?
 ADRNE   R0, Error_Disc_Drive_Not_Known	;NO
 BNE     _no_floppy_support

 ._Pass_to_ADFS2
 #if compile_RO < 450
   TEQP    R2, #0
 #else
   MSR     CPSR_f, R2
 #endif
 LDMFD   R13!, {R1, R2, R12, R14}
 B       pass_to_os_ADFS_MiscOp		;forward request to ADFS

._no_floppy_support
 MOV     R1, #1<<31
 ORR     R0, R0, #%01 << 30		;R0 is an error pointer
 ADDS    R1, R1, R1			;set V

 STMFD   R13!, {R0}
 BL      MiscOp_restorePSRVS
LDMFD   R13!, {R0, R1, R2, R12, PC}


 ._Capture_MiscOp
 STR     R2, MiscOp_CPSR
 LDMFD   R13!, {R1, R2}

 CMP     R0, #1
 BEQ     MiscOp_PollChanged		;1 = poll changed
 BLO     MiscOp_Mount			;0 = mount

 CMP     R0, #3
 BEQ     MiscOp_UnlockDrive		;3 = unlock drive
 BLO     MiscOp_LockDrive		;2 = lock drive

 CMP     R0, #5
 BEQ     MiscOp_Eject			;5 = eject disc
 BLO     MiscOp_PollPeriod		;4 = poll period

 CMP     R0, #7
 MOVEQ   R2, #0				;7 = drive status PRM5a-177
;### needs coding
; BLO     MiscOp_ModuleDesc		;6 = FileCore module descriptor block PRM5a-176

 STMFD   R13!, {R0}
 BL      MiscOp_restorePSR
LDMFD   R13!, {R0, R12, PC}




;MiscOp 0
;--------
;Entry:
; R1 = drive
; R2 = disc address to read
; R3 = pointer to buffer
; R4 = length to read into buffer
; R5 = pointer to disc record to fill in
; R8 = pointer to FileCore instance private word
; STACK: R12, R14

.MiscOp_Mount
 STMFD   R13!, {R1-R3, R9-R10}

 LDRB    R0, [R12, #Format_In_Progress - VARS]		;are we about to format ADFFS
 TEQ     R0, #0				;if non-zero, it's a disc rec pointer
 BNE     _copy_discrec			;YES, copy the disc rec we're formatting as

 BL      Identify_Disc			;check the disc type
					;R0=pointer to a blank disc sig
 TEQ     R1, #ASC("F")			;Is it a new format disc?
 TEQNE   R1, #ASC("E")
 BEQ     _identified			;yes, we can skip the disc size copy

 TEQ     R1, #ASC("?")
 BNE     _copy_discrec

 ._error
 BL      MiscOp_restorePSRVS
 MOV     R0, #&D7			;report not understood error
LDMFD   R13!, {R1-R3, R9-R10, R12, PC}	;Exit


 ._identified
 STMFD   R13!, {R4-R6}
 LDR     R4, [R0, #O_DiscSigAddr]	;the disc sig address on disc
 MOV     R5, R0
 BL      read_signature			;R6=pointer to discsig on disc
 MOV     R0, R6
 LDMFD   R13!, {R4-R6}
 BVS     _error				;exit on error

 ._copy_discrec
 LDR     R9, [R0, #DR_log2secsize]	;log2secsize / secspertrack / ..
 STR     R9, [R5, #DR_log2secsize]	;.. heads / secspertrack
 LDRB    R9, [R0, #DR_lowsector]
 STRB    R9, [R5, #DR_lowsector]

 TEQ     R4, #0				;do we need to copy data
 BEQ     _exit				;NO

 LDRB    R9, [R0, #DR_log2secsize]
 MOV     R1, R3				;dest
 MOV     R10, R2			;address is always byte offset
 ._need_more
   BL      Convert_Sector_to_Addr_Read_Suspend	;R2=size of sector
   BVS     _error			;exit on error
   MOV     R0, R14			;source
   CMP     R4, R2			;is it less than one sector?
   MOVLO   R2, R4			;less
   SUB     R4, R4, R2
   ADD     R10, R10, R2
   BL      MemCopy			;Copy the requested data
   CMP     R4, #0
 BGT     _need_more

 ._exit
 BL      MiscOp_restorePSR
 MOV     R0, #0				;no error
LDMFD   R13!, {R1-R3, R9-R10, R12, PC}




;MiscOp 1
;--------
;Entry:
; R1 = drive
; R2 = sequence no.
; R8 = pointer to FileCore instance private word
; STACK: R12, R14
;
;Exit:
; R2 = sequence no.
; R3 = result flag -       11-31 reserved
;                  +----------10 ready works
;                  |+--------- 9 density sensing works
;                  ||+-------- 8 disc in drive is high density
;                  |||+------- 7 changed works
;                  ||||+------ 6 empty works
;                  |||||+----- 5 drive is 40 track
;                  ||||||+---- 4 ready
;                  |||||||+--- 3 empty
;                  ||||||||+-- 2 changed
;                  |||||||||+- 1 maybe changed
;                  ||||||||||+ 0 not changed
;                  |||||||||||

.MiscOp_PollChanged
 LDRB    R3, [R12, #Floppy_Changed - VARS]	;R3=changed status
 LDR     R14, [R12, #miscop_sequence_no - VARS]	;R1=sequence number
;**** fix for bug introduced in RISC OS 3.60
 SUBS    R3, R3, #1				;has the disc changed?
 STRPLB  R3, [R12, #Floppy_Changed - VARS]	; |   reset disc change status
 MOVMI   R3,     #%11011010001			;NO, ready/density sensing/changed/empty works, ready, not changed
 MOVPL   R3,     #%11011010010			; |+  ready/density sensing/changed/empty works, ready, maybe changed
 CMP     R3, #8
 MOVEQ   R3,     #%11011010100			; |+  ready/density sensing/changed/empty works, ready, changed
;****

 MOVNE   R14, #0				;YES,
 MOVNE   R2, #0					; |   reset sequence number
 STRNE   R2, [R12, #miscop_sequence_no - VARS]	; |   update sequence no

 TEQ     R14, R2				;has the sequence number changed?
 BICNE   R3, R3, #%00000000111			; | YES,
 ORRNE   R3, R3, #%00000000010			; | maybe changed
 STRNE   R2, [R12, #miscop_sequence_no - VARS]	;update sequence no

 TST     R3,     #%00000000110			;has the disc changed?
 LDREQB  R1, [R12, #miscop_floppy_HD - VARS]	;NO
 BLNE    _identify_disc				;YES
 ORR     R3, R3, R1
 BL      MiscOp_restorePSR
 MOV     R0, #1
 MOV     R1, #0
LDMFD   R13!, {R12, PC}


 ._identify_disc
 STMFD   R13!, {R0, R14}
 BL      Identify_Disc
 TEQ     R1, #ASC("F")
 TEQNE   R1, #ASC("0")			;1440K DOS
 MOVEQ   R1,     #%00100000000		;HD floppy
 MOVNE   R1, #0
 STRB    R1, [R12, #miscop_floppy_HD - VARS]
 LDMFD   R13!, {R0, PC}




;MiscOp 2 and 3
;--------------
;Entry:
; R1 = drive
; R8 = pointer to FileCore instance private word
; STACK: R12, R14

.MiscOp_LockDrive
 BL      MiscOp_restorePSR
 MOV     R0, #2
LDMFD   R13!, {R12, PC}

.MiscOp_UnlockDrive
 BL      MiscOp_restorePSR
 MOV     R0, #3
LDMFD   R13!, {R12, PC}




;MiscOp 4
;--------
;Entry:
; R1 = drive
; R8 = pointer to FileCore instance private word
; STACK: R12, R14
;Exit:
; R5 = minimum poll period in ms
; R6 = media type string

.MiscOp_media_type	DCB "disc", 0
			ALIGN

.MiscOp_PollPeriod
 MOV     R5, #100			;100cs poll period
 ADR     R6, MiscOp_media_type		;media type string

 BL      MiscOp_restorePSR
 MOV     R0, #4
LDMFD   R13!, {R12, PC}




;MiscOp 5
;--------
; STACK: R12, R14

.MiscOp_Eject
 BL      Entry_ADFEject			;called with R0=5

 BL      MiscOp_restorePSR
 MOV     R0, #5
LDMFD   R13!, {R12, PC}




;MiscOp_restorePSRVS
;-------------------
;restores flags with V set
;
;Exit:
;R0 - corrupt

.MiscOp_restorePSRVS
 LDR     R0, [R12, #MiscOp_CPSR - VARS]		;restore flags
 ORR     R0, R0, #1 << 28			;set V
B       MiscOp_restorePSR_exit




;MiscOp_restorePSR
;-----------------
;restores flags with V clear
;
;Exit:
;R0 - corrupt

.MiscOp_restorePSR
 LDR     R0, [R12, #MiscOp_CPSR - VARS]		;restore flags
 BIC     R0, R0, #1 << 28		;clear V
 .MiscOp_restorePSR_exit
 #if compile_RO < 450
   TEQP    R0, #0
 #else
   MSR     CPSR_f, R0
 #endif
MOV     PC, R14




;Identify_Disc
;-------------
;Exit:
; R0 = Pointer to template disc sig
; R1 = ASC("<disc type>")
;	L/D/E/F - Acorn format
;	0 - 1440K MS-DOS
;	1 - 720K MS-DOS
;	7 - 720K Atari/M

.Identify_Disc
 STMFD   R13!, {R2-R6, R9-R10, R14}

 ADR     R5, Disc_Record_F		;check F format
 LDR     R4, [R5, #O_DiscSigAddr]	;the disc sig address on disc
 BL      read_signature
 MOVVC   R0, #ASC("F")
 BLVC    _check_signature_F_E

 ADR     R5, Disc_Record_E		;check E format
 LDR     R4, [R5, #O_DiscSigAddr]	;the disc sig address on disc
 BL      read_signature
 MOVVC   R0, #ASC("E")
 BLVC    _check_signature_F_E
					;special case disc records
					;.. that aren't easily detected
 ADR     R5, Disc_Record_D		;check D format
 LDR     R4, [R5, #O_WhereToCheck]	;location to check for Hugo / Nick
 BL      read_signature
 MOVVC   R0, #ASC("D")
 BLVC    _check_signature_D_L

 ADR     R5, Disc_Record_L		;check L format
 LDR     R4, [R5, #O_WhereToCheck]	;location to check for Hugo / Nick
 BL      read_signature
 MOVVC   R0, #ASC("L")
 BLVC    _check_signature_D_L

 ADR     R5, Disc_Record_DOS_1440
 LDR     R4, [R5, #O_DiscSigAddr]
 BL      read_signature
 MOVVC   R0, #ASC("0")
 BLVC    _check_signature_FAT

 ADR     R5, Disc_Record_DOS_720
 LDR     R4, [R5, #O_DiscSigAddr]
 BL      read_signature
 MOVVC   R0, #ASC("1")
 BLVC    _check_signature_FAT

 ADR     R5, Disc_Record_Atari_720
 LDR     R4, [R5, #O_DiscSigAddr]
 BL      read_signature
 MOVVC   R0, #ASC("7")
 BLVC    _check_signature_FAT

 ADR     R5, Disc_Record_Atari_360
 LDR     R4, [R5, #O_DiscSigAddr]
 BL      read_signature
 MOVVC   R0, #ASC("8")
 BLVC    _check_signature_FAT

 MOV     R1, #ASC("?")			;unknown disc type
 ADR     R0, Disc_Record_L		;return any record
 ._exit
LDMFD   R13!, {R2-R6, R9-R10, PC}


 ._ID_Hugo	DCB "oguH"
 ._ID_Nick	DCB "kciN"


._check_signature_F_E
 LDR     R2, [R5]			;get the first word of the template sig
 LDR     R3, [R6]			;identify using first four bytes of disc rec
 TEQ     R3, R2				;do sector size, sectors, heads and density match

 ._check_signature_exit
 MOVEQ   R1, R0
 MOVEQ   R0, R5
 BEQ     _exit				;exit on match
MOV    PC, R14


._check_signature_FAT			;Ref: support.microsoft.com/en-us/kb/140418
 STR     R14, [R13, #-4]!
 LDRB    R3, [R6, #&18]
 LDRB    R4, [R5, #DR_secspertrack]
 TEQ     R3, R4				;sectors/track
 LDREQB  R3, [R6, #&1A]
 LDREQB  R4, [R5, #DR_heads]
 TEQEQ   R3, R4				;heads
 LDREQB  R3, [R6, #&0B]
 LDREQB  R4, [R6, #&0C]
 ORREQ   R3, R3, R4, LSL #8		;R3=sector size in bytes
 LDREQB  R14, [R5, #DR_log2secsize]
 MOVEQ   R4, #1
 MOVEQ   R4, R4, LSL R14
 TEQEQ   R3, R4				;sector size
 LDREQB  R3, [R6, #&13]
 LDREQB  R4, [R6, #&14]
 ORREQ   R3, R3, R4, LSL #8		;sector count
 MOVEQ   R3, R3, LSL R14		;size of disc
 LDREQ   R4, [R5, #DR_discsize]
 TEQEQ   R3, R4				;disc size match?
 LDR     R14, [R13], #4
 BEQ     _check_signature_exit
MOV     PC, R14


._check_signature_D_L
 LDRB    R4, [R6, #0]			;load 1 byte at a time
 LDRB    R3, [R6, #1]
 ORR     R4, R3, R4, LSL #8
 LDRB    R3, [R6, #2]
 ORR     R4, R3, R4, LSL #8
 LDRB    R3, [R6, #3]
 ORR     R4, R3, R4, LSL #8

 LDR     R3, _ID_Hugo			;check for "Hugo"
 TEQ     R3, R4
 LDRNE   R3, _ID_Nick			;check for "Nick"
 TEQNE   R3, R4
B       _check_signature_exit




;read_signature
;--------------
;returns the address of the discsig
;
;Entry:
; R4 - disc address
; R5 - pointer to template discsig
;
;Exit:
; R0, R4, R10 - corrupted
; R6 - pointer to discsig
; R9 - log2secsize

.read_signature
 STR     R14, [R13, #-4]!

 LDRB    R9, [R5, #DR_log2secsize]
 RSB     R0, R9, #32
 MOV     R10, R4, LSR R9		;sector align address
 MOV     R10, R10, LSL R9
 MOV     R4, R4, LSL R0			;preserve offset within sector
 BL      Convert_Sector_to_Addr_Read_Suspend	;get memory address .. in R14
 ADD     R6, R14, R4, LSR R0		;add offset within sector
LDR     PC, [R13], #4
