;AutoVIDC module source - Clock Detection
;----------------------------------------
;
;VIDC Enhancer software control
;For software controlled VIDC Enhancers
;
;Author: Paul Vernon (Retro-Kit) (2012-2013)
;   www: http://www.retro-kit.co.uk

; ---------------------------------------------------------------------------
; Test presence and read speed of VIDC enhancers
; This function is adapted with permission from the
; BASIC Assembly version v0.01 (c) S.Harrison 2013
;
; Adapted version works for all VIDC clock hardware

; A side effect of the code is that the user will see
; the screen blanking briefly during the test before
; it is restored as the clocks are selected in turn

;Entry:
; R8 = internal_flags
; R9 = WatfordType if we know it exists, otherwise NativeType and we need to check Native and AuxIO control
;
;Exit:
; R8 = updated internal_flags
; EnhancerType updated

.checkEnhancerClocks
	STMFD	R13!,{R0-R7,R9-R10,R14}

	MOV	R0,#1			; 1 channel audio
	MOV	R1,#0
	MOV	R2,#0
	MOV	R3,#0
	MOV	R4,#0
	STRB	R1,OCdisable		; enable the over clocking capabilities
	SWI	XSound_Configure
	STMFD	R13!,{R0-R4}		;stack the old values

	MOV	R0,#9			;Sound System Buffer Change
	ADR	R1,handleraddr
	MOV	R2,R12
	SWI     XOS_ClaimDeviceVector

; we're going to check for enhancers in the following order:
; Watford (if detected), Native OS, AuxIS
; the first one detected becomes the one we'll use
	SUB	R13,R13,#20		;create space on the stack
	MOV	R7,R13			;use the stack for the results
	ADR	R6,detectedClockSpeeds

	TEQ	R9,#WatfordType		;do we think a Watford enhancer exists?
	BNE	_noWatford

	#if Debug==1
	SWI	XOS_WriteS
	DCB	"Testing Watford Enhancer",10,13,0
	ALIGN
	#endif

	STRB	R9,EnhancerType
	BL	_tryClockControl
	BL	_didTheClocksChange	;NE if clocks changed
	BNE	_weHaveClockControl

;see if Native OS control changes the VIDC clock, plus grab the current clock at R13+16
._noWatford
	#if Debug==1
	SWI	XOS_WriteS
	DCB	"Testing Acorn Native",10,13,0
	ALIGN
	#endif

	MOV	R0,#NativeType
	STRB	R0,EnhancerType
	BL	_tryClockControl
	BL	_didTheClocksChange	;NE if clocks changed
	BICNE	R8,R8,#ROcanControl OR ROinControl OR EnhancerNeeded	;RO can control the clocks
	MOVNE	R0,#0			;disable AutoVIDC
	BNE	_nativeClockControl
;._noNative
;try AuXIO
	#if Debug==1
	SWI	XOS_WriteS
	DCB	"Testing AuxIO",10,13,0
	ALIGN
	#endif

	MOV	R0,#AuxIOType
	STRB	R0,EnhancerType
	BL	_tryClockControl
	BL	_didTheClocksChange	;NE if clocks changed
	MOVEQ	R0,#0			;we couldn't change the clock

._weHaveClockControl
	MOVNE	R0,#1
._nativeClockControl
	LDMIA	R13!,{R1-R5}
	STMIA	R6,{R1-R5}		;store the detected clocks
	ORRNE	R8,R8,#EnhancerDetected
	STRB	R0,VIDCEnable

	MOV	R0,#9			;Sound System Buffer Change
	ADR	R1,handleraddr
	MOV	R2,R12
	SWI     XOS_ReleaseDeviceVector

	LDMIA	R13,{R0-R4}		;get the original values from the stack
	MOV	R0,#1
	SWI	XSound_Configure	;covers a RO2 bug - only sets sample speed if < chans*15.75!
	LDMIA	R13!,{R0-R4}
	SWI	XSound_Configure	;restores dma size and uS (and channels on RO2)

;reset everything to the defaults..
	MOV	R0,#1
	STRB	R0,OCdisable

	LDMFD	R13!,{R0-R7,R9-R10,PC}

; ---------------------------------------------------------------------------

;Entry:
; R0=number of clocks to convert
; R7=pointer to 4 clocks to check
;Exit:
; R1-R5 - corrupt
; NE if the clock changed, EQ otherwise

._didTheClocksChange
	STMFD	R13!,{R14}
	BL	_convertClockSpeeds	; convert the clock speeds to kHz...
	LDMIA	R7,{R1-R5}
	TEQ	R1,R5			;see if the clock changed
	TEQEQ	R2,R5
	TEQEQ	R3,R5
	TEQEQ	R4,R5
	LDMFD	R13!,{PC}


;Entry:
; R7=pointer to buffer to store 
;
;Exit:
; R0=number of clocks we tested

._tryClockControl
	MOV	R0,#4			;clock 4 becomes the clock before we changed it, so we can set it back
	STMFD	R13!,{R0,R9,R14}
	MOV	R9,R0

;	STMFD	R13!,{R0-R12}
	MOV	R0,#0
	SWI     XHostFS_GetDeviceName	;are we running under Red Squirrel?
					;will corrupt R0,R2
;	LDMFD	R13!,{R0-R12}
	LDMVCFD	R13!,{R0,R9,PC}		;YES, exit as it doesn't emulate timers correctly	

._testloop
	MOV	R0,R9
 	BL	AutoVIDC_SetClockSWI

; we're going to ignore the first and second audio buffers as we can't be sure they're ours
	MVN	R0,#1			; initialise counters
	STR	R0,T1ticks
	STR	R0,T1ticks+4
	STR	R0,dmacount
._countloop				; wait for the timing to complete
	LDR	R0,T1ticks
	CMN	R0,#2
	BEQ	_countloop
	STR	R0,[R7,R9,LSL#2]	; store no. of ints while at this setting and increment the pointer by 4

;BL convertCardinal2
;BL writeStringNewLine
	SUBS	R9,R9,#1		; decrease the loop counter.
	BPL	_testloop		; loop if >0
	LDMFD	R13!,{R0,R9,PC}

; Interrupt number conversion code...

;Entry:
; R0 = number of interrupt counts to convert to KHz (always 4)
; R7 = pointer to clocks to convert

._convertClockSpeeds
	STMFD	R13!,{R0-R7,R14}
	ADD	R0,R0,#1
._checkClocks
	#if Debug==1
	SWI	XOS_NewLine
	#endif

	SUBS	R0,R0,#1		;is it the last clock
	BMI	_de_dupe

	LDR	R4,[R7],#4		;grab the next interrupt count
;Set up pointers to the clock speeds and interrupt ranges
	ADR	R6,possibleClockSpeeds
._checkRange
	LDMIA	R6!,{R3,R14}		;grab the range and KHz
	STR	R3,[R7,#-4]		;assume this clock is correct / fall-back to 24MHz
	CMN	R14,#1			;is it the last range to check?
	BEQ	_checkClocks		;YES

;break up the range into two values, R1 amd R2
	MOV	R1,R14,LSR#16		;R1=lowest value
	EOR	R2,R14,R1,LSL#16	;R2=highest value

	#if Debug==1
	STMFD	R13!,{R0}
	MOV	R0,R1
	BL	convertCardinal2
	SWI	XOS_Write0
	SWI	XOS_WriteI+ASC("|<")
	MOV	R0,R4
	BL	convertCardinal2
	SWI	XOS_Write0
	SWI	XOS_WriteI+ASC("|<")
	MOV	R0,R2
	BL	convertCardinal2
	BL	writeStringNewLine
	LDMFD	R13!,{R0}
	#endif

;if it is a range then check to see if our interrupts are within the range
	CMP	R4,R1			;is R1<R8<R2
	CMPGT	R2,R4
	BLT	_checkRange		;NO, try the next range

	#if Debug==1
	STMFD	R13!,{R0}
	MOV	R0,R3
	SWI	XOS_WriteS
	DCB	"  detected ",0
	ALIGN
	BL	convertCardinal2
	BL	writeStringNewLine
	LDMFD	R13!,{R0}
	#endif

	B	_checkClocks

._de_dupe
	LDMDB	R7!,{R0-R3,R14}		;grab the four clocks we into R0-R3
	LDMIB	R7,{R4-R6}
	TEQ	R1,R0			;remove non-existant clocks
	MOVEQ	R4,#0
	TEQ	R2,R1
	TEQNE	R2,R0
	MOVEQ	R5,#0
	TEQ	R3,R2
	TEQNE	R3,R1
	TEQNE	R3,R0
	MOVEQ	R6,#0
	STMIB	R7,{R4-R6}
	LDMFD	R13!,{R0-R7,PC}


; ---------------------------------------------------------------------------
; Interrupt handler code.
; It increments the dmacounter every time it is called.

;Entry:
; R3=IOC base address
; R12->VARS
;Exit:
; R0,R2 must be preserved (for checkEnhancerClocks above)
; R1,R3 can be corrupted

.handleraddr				;R1-R3 and R12 may be corrupted
	STRB	R0,[R3,#&5C]		;latch T1
	LDR	R1,dmacount
	ADDS	R1,R1,#1		;have we been entered at least once?
	STR	R1,dmacount
	MOVMI	R0,#&FF			;reset T1
	STRMIB	R0,[R3,#&50]		;T1 low
	STRMIB	R0,[R3,#&54]		;T1 high
	STRMIB	R0,[R3,#&58]		;T1 go

	CMP	R1,#1			;have we received counted 1 buffer?
	LDRB	R0,[R3,#&50]		;read T1 low
	LDRB	R1,[R3,#&54]		;read T1 high
	ORR	R0,R0,R1,LSL#8
	RSB	R0,R0,#&10000		;R0 now has the number of 2nS ticks taken
	LDRGE	R3,lastT1ticks
	STR	R0,lastT1ticks
	SUBGE	R0,R0,R3		;YES, R0=time for last buffer to play
	STREQ	R0,T1ticks+4		;store T1 ticks taken
	BLE	_need_another_count

	LDR	R3,T1ticks+4		;we've timed two audio buffers
	CMP	R0,R3			;is the 1st count lower?
	MOVGT	R0,R3			;YES
	STR	R0,T1ticks		;lowest timing, in case an IRQ occurred
._need_another_count
	MOV	R1,#MEMCbase + &80000	;MEMC Sstart
	ORR	R3,R1,#&1F80000>>8	;RISC OS sound buffer 1 physical address
	ORR	R1,R3,#((DMAbufferSize-16)>>4)<<2
	ORR	R1,R1,#&A0000		;MEMC SendN
	STR	R1,[R1,#0]		;write SendN
	STR	R3,[R3,#0]		;write Sstart to clear the IRQ

; Override RISC OS resetting the VIDC control register
	MOV	R1,#VIDCbase		;R1 = VIDC base
	MOV	R3,#&C0000000 OR (uS_vidc-2)	;VIDC manual is incorrect, write N-2
	ORR	R3,R3,#&00000100
	STR	R3,[R1,#0]
	MOV	PC,R14			;pass the IRQ on to the OS

; ---------------------------------------------------------------------------
;A list of possible clock speeds in kHz from 24000 to 60000kHz including most of the common clocks that would be used to overclock the VIDC chip.

MACRO Clock kHz:I, low:I, high:I
{
	DCD kHz
	DCW (VIDCclkMIN*bufferLengthT1)/low
	DCW (VIDCclkMIN*bufferLengthT1)/high
}

;Clock speed and the detected interrupt range that covers it
.possibleClockSpeeds
Clock	24000, 23500,24500		;24MHz
Clock	25175, 24500,25500		;25.175MHz
Clock	31500, 31000,31750		;31.5MHz
Clock	32000, 31750,32500 		;32MHz
Clock	36000, 35500,36500		;36MHz
Clock	40000, 39500,40500		;40MHz
Clock	48000, 47500,48500		;48MHz		- This is most likely the limit to which the VIDC can be accellerated reliably.
Clock	50000, 49500,50500		;50MHz 		- VIDC *MUST* be cooled
Clock	60000, 59500,60500		;60MHz 		- VIDC *MUST* be cooled
DCD	VIDCclkMIN, -1			;24MHz fall-back value
