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

; Implementation of the SWI calls.

; A SWI call allowing a calling application to set the oscillator to one of 3 values, 0 - 2 thereby setting the VIDC clock speed.

;Entry:
; R0 - -1 to return current clock, otherwise VIDC clock speed (0-3)
;
;Exit:
; R0 - clock speed (0-3) if R0=-1 on entry

.AutoVIDC_SetClockSWI
	STMFD	R13!,{R1-R10,R14}

	BL	AutoVIDC_ClockAvailableSWI
	MOV	R5,R1				; R5=clock speed if known, 0 if not

	MOV	R9,PC
	ORR	R8,R9,#%10<<26
	TEQP	R8,#0				; disable IRQ

; Is the VIDC Enhancer required?
	LDR	R10,_LatchBase

	LDRB	R1,EnhancerType			; Yes? What type of enhancer?
	TEQ	R1,#NativeType
	BEQ	_useNativeEnhancer
	TEQ	R1,#AuxIOType			; Is is the Aux IO type?
	BNE	_useWEVIDCEnhancer		; No? Use the WE code
	BEQ	_useAuxVIDCEnhancer		; Yes? Use the Aux IO code

; If we're here, we need to set the clock using the VIDC registers, not AuxIO.
._useNativeEnhancer
	CMP	R0,#2				; 0-2 are valid values
	BHI	_returnNativeClock		; will also trap R0 < 0

	MOV	R4,R0				; Save R0 in R4
	BL	getMonitorTypeAndMode
	BL	AutoVIDC_ModeSyncPolaritySWI	; Pass Mode (R0) into AutoVIDC_GetSyncPolarity

; R0 - Sync. Polarity
; R4 - VIDC clock speed setting
; R5 - VIDC clock speed in kHz

; IOEB Video control latch @ &3350048:
; bit    3    2    1    0
;      <SP1><SP0><VC1><VC0>
; where:
; SP = Sync Polarity:
;       SP1  SP0   V    H
;        0    0   +ve  +ve
;        0    1   +ve  -ve
;        1    0   -ve  +ve
;        1    1   -ve  -ve
;
; VC=VIDC Clock (0=24MHz, 1=25.175MHz, 2=36MHz, 3=Reserved (24MHz))
	ORR	R0,R4,R0,LSL#2
	STRB	R0,[R10,#VIDCLatch]		; Write it to the VIDC register

	MOVS	R0,R5				; R5=0 if we've not yet worked out the clocks
	BLNE	storeVIDCclockSpeed

; Finally, we need to return the clock setting based on what we just stuck into
; the VIDCclk registers
._returnNativeClock
	BL	getClockSpeedInkHz		; R3=VIDC Clock speed in kHz
	TEQ	R3,#0				; If we're on RO2, we get a value of 0 back
						; we'll return clock 0 if that's the case
	MOV	R0,R3
	BL	AutoVIDC_ClockAvailableSWI
	B	_setClockCleanUp

._useAuxVIDCEnhancer
	MOV	R1,#PageZero			; Page Zero

	CMP	R0,#1				; Check the user input
	MOVLO	R4,#0				; Is it 0? If so, set the AuxIO bit to 0
	MOVEQ	R4,#Aux2Bitmask			; If it's 1, set Aux 2 bit
	BLS     _SetAuxClock_SWI

	CMP	R0,#3
	BHI	_returnAuxClock			; Any other user input value, just return the current value.
	MOVLO	R4,#Aux1Bitmask			; If it's 2, set Aux 1 bit
	LDREQB	R2,OCdisable			; If it's 3, check if over-clocking is enabled
	TEQEQ	R2,#0
	MOVEQ	R4,#Aux12Bitmask		; If it's enabled. Set both Aux 1 and 2

._SetAuxClock_SWI
	LDRB	R3,[R1,#LatBShadow]		; Load Aux-Shadow
	BIC	R3,R3,#Aux12Bitmask		; clear both Aux 1 and Aux 2
	ORR	R3,R3,R4			; set Aux-Bit.
	STRB	R3,[R1,#LatBShadow]		; set Aux-Shadow
	STRB	R3,[R10,#LatchB]		; set Aux-Hardware

	MOVS	R0,R5				; R5=0 if we've not yet worked out the clocks
	BLNE	storeVIDCclockSpeed

._returnAuxClock
; Finally, we want to output the current VIDC clock based on the AuxIO line values

	LDRB	R3,[R1,#LatBShadow]		; Load Aux-Shadow
	AND	R2,R3,#Aux12Bitmask		; apply a bitmask to the content Aux-Shadow

; Map the actual hardware to an abstracted value of 0, 1, 2 or 3 for 24, 25.175, 36 and overclocked clocks
; Aux bits map %00=0  %01=2  %10=1  %11=3
	MOV	R0,R2,LSR#5			; %1100000 now %11
	TEQ	R2,#Aux2Bitmask			; Is Aux 2 active?
	MOVEQ	R0,#1
	TEQ	R2,#Aux1Bitmask			; Is Aux 1 active?
	MOVEQ	R0,#2

	B	_setClockCleanUp

._useWEVIDCEnhancer
; Watford Super VIDC Enhancer
; bits are:
; 76543210
; ||||||||
; ||||||++- VIDC clock
; |||||+--- H Sync
; ||||+---- V Sync (also Vsync invert check by writing 0)
; |||+----- Inverse sync required (read only)
; ||+------ 25MHz clock available (read only)
; |+------- if bit 4=1 when read, this bit is set to the inverse of bit 2
; +-------- if bit 4=1 when read, this bit is set to the inverse of bit 3
	CMP	R0,#2				; are we asking for the current clock?
	BHI	_returnWEClock			; YES

; construct value to write to the WE in R3
	MOVS	R3,R0				;24MHz?
	ADDNE	R3,R3,#1			;NO, R3 now 0 (24MHz), 2 (25.175MHz), 3 (36MHz)
	BL	getMonitorTypeAndMode		;R0=current mode
	BL	AutoVIDC_ModeSyncPolaritySWI	;R0=Sync bits

	LDRB	R4,internal_flags
	TST	R4,#IIC25MHzFitted		;EQ if 25.175MHz not available
	TEQEQ	R3,#2				;are we asking for 25Mhz when it's not available?
	MOVEQ	R3,R0,LSL#2			;YES, switch to 24MHz and put sync bits into bits 2/3
	ORRNE	R3,R3,R0,LSL#2			;NO, leave and put sync bits into bits 2/3
	TST	R4,#InvertSync			;does P6/P7 need to be the inverse of the sync
	EORNE	R0,R0,#%11			;YES
	ORRNE	R3,R3,R0,LSL#6

	EOR	R3,R3,#&FF			;invert the value for WE
	BL	iic_control_write		;Write to the WE VIDC Enhancer

	MOVS	R0,R5				;R5=0 if we've not yet worked out the clocks
	BLNE	storeVIDCclockSpeed

._returnWEClock
	BL	iic_control_read		;R0=inverse of value
	AND	R0,R0,#%11			;0=36MHz, 1=25Mhz, 2=0Mhz, 3=24Mhz
	RSBS	R0,R0,#2
	MOVMI	R0,#0				;R0 now 0=24MHz, 1=25Mhz, 2=36MHz

._setClockCleanUp
	TEQP	R9, #0				; enable IRQ

	LDMFD	R13!,{R1-R10, PC}


._LatchBase	DCD LatchBase

; ---------------------------------------------------------------------------
; a small function to remove replicated code pass R0 in representing the clock speed in kHz to be stored in ZP.
.storeVIDCclockSpeed
	STMFD	R13!,{R1,R14}
	LDRB	R1,internal_flags		; If we're >= RO3...
	TST	R1,#RISCOS3
	MOVEQ	R1,#PageZero1000
	STREQ	R0,[R1,#VIDCclockspeed]		; ..store the VIDC clock speed in the correct zero page location
	LDMFD	R13!,{R1,PC}

; ---------------------------------------------------------------------------
; A SWI to effectively enable and disable AutoVIDC from manipulating the VIDC Enhancers thereby delegating control to the calling program.

.AutoVIDC_DelegateControlSWI
	CMP	R0,#1
	MOVHI	PC,R14				; If the user input isn't 0 or 1, ignore the call
	STMFD	R13!,{R0-R1,R14}

	LDRB	R1,EnhancerType
	TEQ	R1,#NativeType
	EORNE	R0,R0,#1			; invert R0 to set VIDCEnable
	STRNEB	R0,VIDCEnable
	BLNE	AutoVIDC_ReInitSWI
	LDMFD	R13!,{R0-R1, PC}

; ---------------------------------------------------------------------------
; Check to see if AutoVIDC is in control or not?

.AutoVIDC_IsDelegatedSWI
	LDRB	R0,VIDCEnable			; Grab the current state
	B	invertValue

; ---------------------------------------------------------------------------
; Check to see if over-clocking is enabled or not?

.AutoVIDC_IsOverClockingEnabledSWI
	LDRB	R0,OCdisable			; Grab the current state
.invertValue
	EOR	R0,R0,#1			; invert value
	MOV	PC,R14

; ---------------------------------------------------------------------------
; Return or derive the Sync. Polarity for the mode passed in.
; Entry:
;  R0 = Mode to check
;  R1 = Monitor type
; Exit:
;  R0 = Sync (0-3)
.AutoVIDC_ModeSyncPolaritySWI
	STMFD	R13!,{R1-R7,R14}

	MOV	R6, R0
	MOV	R7, R1

	LDRB	R1,internal_flags		; If we're < RO3...
	TST	R1,#RISCOS3
	MOVNE	R0,#0				; Always return a sync polarity value of 0 in RO2.
	BNE	_exitModeSyncPolaritySWI

	CMN	R6, #1				; If we're RO3 and we want to know the current mode...
	BEQ	_getCurrentSync
	CMP	R6, #47				; If we're RO3 and a pre-configured RO mode
	BLE	_selectSync			; we need to move on to derive the Sync polarity

;If we're here then we're asking about a custom mode so we can use Service_ModeExtension (&50) via OS_ServiceCall

	MOV	R1, #Service_ModeExtension	; Set up the service call reason
	MOV	R2, R6				; Set up the mode to ask about
	MOV	R3, R7				; Set up the monitor type to ask about.
	SWI	XOS_ServiceCall			; Get the VIDC mode list for the mode

	TEQ	R1,#0     			; If the service call doesn't respond
	BNE	_selectSync			; we're in a native mode so skip the processing...
	LDMIA	R3,{R0,R6}			; R0=VIDC List type, R6=Base mode for the current mode
	TEQ	R0, #1
	BNE	_selectSync			; If the list type isn't "Type 1" we need to skip processing it.

; If we're here then we have a valid VIDC list of Type 1 and we need to process it.
; By processing it, we need to see if there are Sync. Polarity details in the extended part of the list as per PRM 1-621 - 1-623.

	MVN	R0, #0				; Set up the R0 register
	MOV	R1, #0				; Set up the counter
	MOV	R2, #1				; Set up the part indicator
._loopVIDCList

        LDR	R4, [R3, R1]			; What is the content of the VIDC list at this point?
	TEQ	R2,#2   			; Are we in the second section?
	BNE	_nextItem                        ; No, OK, skip to the next item.

; If we're here then we're in the "extended parameters" and we need to look for the Sync. Polarity.

	MOV	R5, R4, LSR #24			; Right shift 24-bits
	TEQ	R5, #1				; Is the remaining data 1?
	ANDEQ	R0, R4, #3			; Yes, so return the data
	BEQ	_exitModeSyncPolaritySWI	; Exit with the Sync. Polarity in R0.

._nextItem
	CMN	R4, #1
	ADDNE	R1, R1, #4
        BNE	_loopVIDCList

        ADD	R2,R2,#1
        CMP	R2, #3
        ADDLT	R1,R1,#4
        BLT	_loopVIDCList

;If we're here, we have a base mode but no Sync.Polarity set in the Extended parameters so process the base mode to derive the Sync.Polarity

._selectSync
;
; Returns R0 with with the Sync.Polarity set. (call for new hardware only)
;
; The following code was provided by Steve Harrison
; as part of the solution to determine a mode's Sync Polarity.

	MOV	R0,R6				; Move the base mode into R0

;first check the modes which *always* use a fixed sync setting whatever the monitortype
	CMP	R0, #25
	RSBGTS	R14, R0, #28			;if mode >25 is 28-mode >=0?
	MOVGE	R0, #3 				;sync 3 is always used for VGA modes 25-28
	BGE	_exitModeSyncPolaritySWI

	CMP	R0,#29
	RSBGTS	R14,R0,#32			;if mode >29 is 32-mode >=0? (allows for Mode32 in future ;-)
	MOVGE	R0,#0				;sync 0 is always used for SVGA modes 29-31
	BGE	_exitModeSyncPolaritySWI

	CMP	R0,#37
	RSBGTS	R14,R0,#43			;if mode >37 is 43-mode >=0?
	MOVGE	R0, #2				;sync 2 is always used for modes 37-43
	BGE	_exitModeSyncPolaritySWI

	MOV	R0, R7				;now check the monitortypes and select the required default sync setting for these (other than in the above modes)
	TEQ	R0, #3
	TEQNE	R0, #4
	MOVEQ	R0, #2				;return 2 for remaining modes on monitortypes 3 and 4
	MOVNE	R0, #0				;otherwise return 0 for remaining modes on monitortypes 0,1,2 and 5 (LCD) and any other types...
	B	_exitModeSyncPolaritySWI

._getCurrentSync
	MOV	R1,#PageZero1000		; Take a look at ZP &1584
	LDR	R0,[R1,#VIDCControlCopy]	; Grab the contents of &1584
	MOV	R0,R0,LSR#11			; Shift right 11 bits
	AND 	R0,R0,#3                        ; return the bitmasked data

._exitModeSyncPolaritySWI
	LDMFD	R13!,{R1-R7, PC}

; ---------------------------------------------------------------------------
; Check to see if the clock is available.
;
; Entry:
;  R0 = clock to test (0-3) or speed in kHz
;
; Exit:
;  R0 = clock index (-1 if not detected or assumed)
;  R1 = clock speed in kHz

.AutoVIDC_ClockAvailableSWI
	STMFD	R13!,{R2-R3,R14}

	ADR	R2,detectedClockSpeeds		; R2=list of available clock speeds
	CMP	R0,#3				;are we being asked for a specific clock?
	LDRLS	R1,[R2,R0,LSL#2]
	LDMLSFD	R13!,{R2-R3,PC}

	MOV	R3,#0				; R3=counter
._isClockPresent
	LDR	R1,[R2,R3,LSL#2]
	TEQ	R0,R1				; Match the clock speed....
	BEQ	_clockPresent			; Matches? finish!

	ADD	R3,R3,#1			; next clock speed
	CMP	R3,#4				;have we checked all the clocks?
	BNE	_isClockPresent			; Doesn't match? Move to the next...
	B	_clockNotAvailable

._checkCounter
	TEQ	R1,#0				; Yes? Is the current entry 0?
	BNE	_clockPresent			; No? We have the clock speed available

._clockNotAvailable
	MVN	R0,#0				; Clock not known
	MVN	R1,#0
	LDMFD	R13!,{R2-R3,PC}

._clockPresent
	MOV	R0,R3			; If the clock is present, return the current entry position (0-3)
	LDMFD	R13!,{R2-R3,PC}

; ---------------------------------------------------------------------------
; Returns whether or not an enhancer has been detected by the AutoVIDC module

.AutoVIDC_EnhancerPresentSWI
	LDRB	R0,internal_flags
	MOV	R0,R0,LSR#7			;EnhancerDetected is in bit 7
	MOV	PC,R14

; ---------------------------------------------------------------------------
; Returns the type of enhancer that has been detected by the AutoVIDC module

; 0 - Native Acorn		(A4, A540, A3010, A3020, A4000, A5000)
; 1 - AUX I/O controlled	(A3xx, A4x0[/1], A3000) (AtomWide, Beebug, Andreas Barth, Retro-Kit)
; 2 - I2C controlled		(A3xx, A4x0[/1], A3000) (Watford Electronics)

.AutoVIDC_EnhancerTypeSWI
	LDRB	R0,EnhancerType
	MOV	PC,R14
