;This provides all code to redirect RISCOS and the VDU framebuffer to DA2
;Frames are then blitted to the GPU's framebuffer on the GPU's VSync,
;at the FPS of the game...or 50FPS if the FPS isn't known.

STRUCT shape_t
{
  .width	DCB 0
  .height	DCB 0
		DCB 0
		DCB 0
  .buffLA	DCD 0
}

#set rt_stack_size = 4 * 1024

 ALIGN   32, 4
 .HAL_vidc_list		DBB 252		;VIDC type 3 list for current MODE
			DCD -1		;terminator
 .GPU_palette		DBD 256		;GPU palette
 .Cursor_palette	DBD 4		;hardware cursor palette
 .GPU_screen_start	DCD 0		;start address in GPU for blitter
 .Cursor_state		DCD 0		;0=pointer off, 1..4=pointer on
 .hwd_graphicsV8_values	DCD 0, 0, 0	;return values for GraphicsV 8
 .current_GPU_bank	DCD 0		;alternates between 0 and 1 on each blit
 .logical_VDU_start	DBD 4		;framestore start/end addresses
 .hwd_framestore	DBD 3		;physical address of hardware framestore
 .HAL_vidc_list_dirty	DCD 0		;set to 1 if MODE update required
 .current_display_driver	DCD 0	;driver ID we're taking over





;entry_GraphicsV
;---------------
;Entry point for GraphicsV vector

;Entry:
; R0-R3 - dependent on <reason>
; R4    - <driver> in bits 24..31, <reason> in bits 0..7
; R12   - pointer to VARS

.entry_GraphicsV
 STMFD   R13!, {R1, R4, R14}

 LDR     R1, current_display_driver
 AND     R14, R4, #&FF			;R14=reason code
 MOV     R4, R4, LSR #24		;R4=driver ID
 TEQ     R4, R1
 TEQEQ   R14, #1			;VSync for GPU?
 BEQ     _GPU_VSync			;YES

 LDR     R1, [R12, #our_GraphicsV_ID - VARS]
 TEQ     R4, R1				;event for us?
 LDMNEFD R13!, {R1, R4, PC}		;NO

 TEQ     R14, #5			;pointer?
 TEQNE   R14, #7			;vet MODE?
 TEQNE   R14, #8			;features?
 TEQNE   R14, #10			;palette entry?
 TEQNE   R14, #11			;palette entries?
 TEQNE   R14, #19			;vet MODE 2?
 BEQ     _retain			;retain them

 LDR     R1, [R12, #closest_screen_mode - VARS]
 CMN     R1, #1				;are we going to support this MODE?
 LDMEQFD R13!, {R1, R4}
 BEQ     _pass_to_GPU			;NO

 ._retain
 LDMFD   R13!, {R1, R4}
 CMP     R14, #19
 ADDLS   PC, PC, R14, LSL #2		;use jump table below
LDR     PC, [R13], #4			;pass call on

 LDR     PC, [R13], #4			;0 - Vector claimed
 LDR     PC, [R13], #4			;1 - VSync
 B       _set_MODE			;2 - Set Mode
 LDR     PC, [R13], #4			;3 - Set interlace
 B       _pass_to_GPU			;4 - Set blank
 B       _update_pointer		;5 - Update Pointer
 B       _set_DAG			;6 - Set DAG
 B       _vet_MODE			;7 - Vet Mode
 B       _features			;8 - Supported features
 LDR     PC, [R13], #4			;9 - Framestore information
 B       _write_palette_entry		;10 - Write single palette entry
 B       _write_palette_entries		;11 - Write multiple palette entries
 LDR     PC, [R13], #4			;12 - Read palette entry
 LDR     PC, [R13], #4			;13 - Render
 LDR     PC, [R13], #4			;14 - IIC op
 LDR     PC, [R13], #4			;15 - Select head
 LDR     PC, [R13], #4			;16 - Select Startup mode
 B       _pass_to_GPU			;17 - List pixel formats
 B       _pass_to_GPU			;18 - Read Info
 B       _vet_MODE			;19 - Vet mode 2


._GPU_VSync
 LDMFD   R13!, {R1, R4}
 #if compile_GPU == 1 AND JIT_debug == 1
   LDR     R14, vsync_counter
   ADD     R14, R14, #1			;count actual VSyncs
   STR     R14, vsync_counter
 #endif

 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 BEQ     _raise_as_us			;NO

;The following section fixes Hero Quest locking, but adds microstutter
 LDR     R14, [R12, #vsync_unthreaded - VARS]
 TEQ     R14, #0			;do we need to raise VSync unthreaded?
 BNE     _exit_claimed			;YES
;*********************************************************************

 LDR     R14, vsync_triggered		;YES
 TEQ     R14, #0			;50hz ticked?
 BEQ     _exit_claimed			;NO, ignore VSync
 MOV     R14, #0
 STR     R14, vsync_triggered		;mark as actioned
 BL      blit_DA2_frame
 ._raise_as_us				;re-raise VSync as us
 STMFD   R13!, {R4, R8, R9}
 LDR     R8, [R12, #our_GraphicsV_ID - VARS]
 MOV     R9, #Vector_GraphicsV
 BIC     R4, R4, #&FF << 24
 ORR     R4, R4, R8, LSL #24
 SWI     XOS_CallAVector		;PRM1-70
LDMFD   R13!, {R4, R8, R9, PC}


;Entry: R14 stacked

._pass_palette_to_GPU
 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 BNE     _exit_claimed			;NO, don't pass to GPU
					;YES, fallthru to _pass_to_GPU
._pass_to_GPU
 STMFD   R13!, {R4, R8, R9}
 LDR     R8, current_display_driver
 MVN     R9, #0
 STR     R9, current_display_driver	;prevent re-entrancy
 MOV     R9, #Vector_GraphicsV
 BIC     R4, R4, #&FF << 24
 ORR     R4, R4, R8, LSL #24
 SWI     XOS_CallAVector		;PRM1-70
 STR     R8, current_display_driver
 TEQ     R4, #0				;claimed?
 ADDEQ   R13, R13, #4			;drop R4
 LDMEQFD R13!, {R8, R9, R14, PC}
LDMFD   R13!, {R4, R8, R9, PC}



._update_pointer
 AND     R14, R0, #1
 STR     R14, Cursor_state		;store cursor state
 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 BEQ     _pass_to_GPU			;NO

 TST     R0, #1				;is the cursor enabled?
 BEQ     _exit_claimed			;NO, exit
 
 STMFD   R13!, {R0-R3, R5-R6}
 ADD     R4, R12, #vidc20_regs - (VIDC20_HCR << 2) - VARS
 LDR     R5, [R4, #VIDC20_HDSR << 2]
 LDR     R6, [R4, #VIDC20_HBSR << 2]
 CMP     R5, R6				;is HDSR<HBSR?
 ADDLO   R1, R1, R6			;YES, correct X using HBSR
 ADDHS   R1, R1, R5			;NO, correct X using HDSR
 LDR     R5, [R4, #VIDC20_VDSR << 2]
 LDR     R6, [R4, #VIDC20_VBSR << 2]
 CMP     R5, R6				;is VDSR<VBSR?
 ADDLO   R2, R2, R6			;YES, correct Y using VBSR
 ADDHS   R2, R2, R5			;NO, correct Y using VDSR

 ADD     R1, R1, #18 - 17		;correct HCSR
 STR     R1, [R4, #VIDC20_HCSR << 2]
 LDR     R5, [R4, #VIDC20_VCER << 2]
 LDR     R6, [R4, #VIDC20_VCSR << 2]
 SUB     R5, R5, R6			;R5=current cursor height
 STR     R2, [R4, #VIDC20_VCSR << 2]

 TST     R0, #%10			;is the cursor being updated?
 LDRNEB  R1, [R3, #shape_t.height]	;YES, get height
 ADDNE   R2, R2, R1			; |+  work out VCER
 ADDEQ   R2, R2, R5			;NO, use existing height
 STR     R2, [R4, #VIDC20_VCER << 2]
 BEQ     _update_pointer_exit		;NO, exit
 MOV     R0, R1				;R0=height

 LDR     R1, [R3, #shape_t.buffLA]	;get address of pixel data
 MOV     R4, R1

 STR     R4, [R12, #MEMC_CinitOS - VARS]	;store OS's pointer address
 BL      check_JIT_and_taskID		;is the task supported?
 MOVEQ   R4, #RO3xx_Pointer_Buffer	;YES, legacy RO cursor buffer
 STR     R4, [R12, #MEMC_Cinit - VARS]	;set MEMC Cinit
 BNE     _update_pointer_exit		;NO

 ._update_pointer_L1
   SUBS    R0, R0, #1			;last row?
   LDMPLIA R1!, {R2-R3}			;get one row
   STMPLIA R4!, {R2-R3}
 BPL      _update_pointer_L1		;NO

 ._update_pointer_exit
 LDMFD   R13!, {R0-R3, R5-R6}
B       _exit_claimed




._features
 ADR     R4, hwd_graphicsV8_values
 LDMIA   R4, {R0-R2}
 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 ORRNE   R0, R0, #1			;allow hardware scrolling
 ORRNE   R0, R0, #1 << 1		;and hardware pointer
 BICNE   R0, R0, #1 << 3		;no framestore
 BICNE   R0, R0, #1 << 4		;allow VSync
 BICNE   R0, R0, #1 << 6		;prevent rectangle copies
 ORR     R0, R0, #1 << 5		;framestore changes on mode change
 ORR     R1, R1, #%1111			;allow 1/2/4/8 bpp MODE's
B       _exit_claimed




;Entry:
; R0 -	0 - Normal Entry
;	1 - Border
;	2 - Pointer
; R1 - colour
; R2 - palette index

._write_palette_entry
 TEQ     R0, #2				;cursor palette entry?
 BEQ     _write_palette_entry_cursor	;YES

 TEQ     R0, #0				;normal palette entry?
 BNE     _pass_palette_to_GPU		;NO

 CMP     R2, #255			;is it a valid palette entry?
 BHI     _pass_palette_to_GPU		;NO

 ADR     R0, GPU_palette
 MOV     R1, R1, ROR #8			;SSBBGGRR
 STR     R1, [R0, R2, LSL #2]		;store the palette entry
 MOV     R1, R1, ROR #24		;set registers back to entry state
 MOV     R0, #0
B       _pass_palette_to_GPU


._write_palette_entry_cursor
 CMP     R2, #3				;is it a valid cursor palette entry?
 BHI     _pass_palette_to_GPU		;NO

 ADR     R0, Cursor_palette
 MOV     R1, R1, ROR #8			;SSBBGGRR
 STR     R1, [R0, R2, LSL #2]		;store the palette entry
 MOV     R1, R1, ROR #24		;set registers back to entry state
 MOV     R0, #2
B       _pass_palette_to_GPU




;Entry:
; R0 -	0 - Normal Entry
;	1 - Border
;	2 - Pointer
; R1 - pointer to array of colours
; R2 - first palette index
; R3 - number of entries

._write_palette_entries
 STMFD   R13!, {R0-R4}
 TEQ     R0, #0				;normal palette entry?
 BEQ     _write_palette_entries_normal

 TEQ     R0, #2				;cursor palette entry?
 BNE     _exit				;NO

 ADD     R0, R2, R3			;R0=max palette entry
 CMP     R0, #4				;more than max allowed
 RSBHI   R3, R2, #4			;YES, cap it
 ADR     R4, Cursor_palette
 B       _write_palette_entries_L1

 ._write_palette_entries_normal
 LDR     R0, [R12, #current_mode_bitdepth - VARS]
 MOV     R4, #1
 MOV     R0, R4, LSL R0			;R0=bpp
 MOV     R4, R4, LSL R0			;R4=max palette entries
 CMP     R4, #256
 MOVHI   R4, #256			;cap to 256 entries
 CMP     R2, R4				;is index valid?
 BHS     _exit				;NO

 ADD     R0, R2, R3
 CMP     R0, R4				;more than allowed?
 RSBHI   R3, R2, R4			;YES, cap it
 ADR     R4, GPU_palette

 ._write_palette_entries_L1
   LDR     R0, [R1], #4
   MOV     R0, R0, ROR #8		;SSBBGGRR
   STR     R0, [R4, R2, LSL #2]		;store the palette entry
   ADD     R2, R2, #1			;go to next palette entry
   SUBS    R3, R3, #1			;more palette entries?
 BNE     _write_palette_entries_L1	;YES, loop

 ._exit
 LDMFD   R13!, {R0-R4}
B       _pass_palette_to_GPU




._set_MODE
 STMFD   R13!, {R0-R9}

 ADR     R8, HAL_vidc_list		;copy the VIDC list for updates
 ADD     R1, R8, #256 - 4		;ensure we don't overrun
 MOV     R9, R8
 ._set_MODE_L1
   LDR     R4, [R0], #4
   STR     R4, [R9], #4
   SUBS    R1, R1, #4
   CMNNE   R4, #1			;is it the last VIDC list entry?
 BNE     _set_MODE_L1			;NO, loop

 LDR     R4, [R8, #VIDC_Type3.log2bpp]
 STR     R4, [R12, #current_mode_bitdepth - VARS]	;store it for the VIDC code
 MOV     R1, #5				;32 bpp
 STR     R1, [R8, #VIDC_Type3.log2bpp]
 LDR     R5, [R8, #VIDC_Type3.hdisp]
 STR     R5, [R12, #VIDC20_width - VARS]
 LDR     R6, [R8, #VIDC_Type3.vdisp]
 STR     R6, [R12, #VIDC20_height - VARS]
 STR     R13, [R8, #VIDC_Type3.hdisp]	;force a GraphicsV 2 thru to GPU
 STR     R13, [R12, #old_VIDC20_HCR - VARS]
 STR     R13, HAL_vidc_list_dirty

 LDMFD   R13!, {R0-R9}			;clear down stack
B       _exit_claimed




._set_DAG
 TEQ     R0, #0				;set video display?
 BNE     _exit_claimed			;NO

 #if compile_GPU == 1 AND JIT_debug == 1
   LDR     R14, blitter_counter		;increase the FPS counter
   ADD     R14, R14, #1
   STR     R14, blitter_counter
 #endif

 STR     R0, [R13, #-4]!		;preserve R0
 BL      get_screen_start
 LDR     R0, [R13], #4
B       _exit_claimed




._vet_MODE
 TEQ     R14, #19			;vetMODE 2?
 MOVNE   R0, #0				;NO, allow all MODEs
 BNE     _exit_claimed
 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 BEQ     _pass_to_GPU			;NO

 MOV     R0, #1				;use DA2
 MOV     R1, #0				;start alignment
 MOV     R2, #0				;no padding
					;fall-thru to exit_claimed


._exit_claimed
 MOV     R4, #0				;claim GraphicsV
LDMFD   R13!, {R14, PC}			;exit




 .blit_in_progress	DCD -1
#if compile_GPU == 1 AND JIT_debug == 1
 .vsync_counter		DCD 0		;50hz counter
#endif




;blit_update_VIDC
;----------------
;VIDC parameters have changed, force the GPU to update the geometry
;
;Exit:
; R0-R9 - corrupt

.blit_update_VIDC
 STMFD   R13!, {R12, R14}
 ADR     R12, VARS

 ADR     R8, HAL_vidc_list
 ADR     R9, vidc20_regs - (VIDC20_HCR << 2)

					;***********************************
 LDR     R4, [R12, #VIDC1_CR - VARS]	;calculate T1 ticks per raster,
 ANDS    R4, R4, #%11			; based on HCR and pixelrate
 LDR     R0, [R9, #VIDC20_HCR << 2]	;get current HCR
 ADD     R0, R0, #8			;correct for N-8
 MOVEQ   R0, R0, LSR #2			;R0=HCR/4 (8000)
 BEQ     _P1
 CMP     R4, #%10			;16000?
 ADDNE   R0, R0, R0, LSR #2		;NO, HCR + HCR>>2
 ADDNE   R0, R0, R0, LSR #4		; |+ +HCR>>4 + HCR>>6

 MOVLS   R0, R0, LSR #3			;R0=HCR/6 (12000) or HCR/8 (16000)
 MOVHI   R0, R0, LSR #4			;R0=HCR/12 (24000)

 ._P1					;R0=ticks per scanline
 LDR     R4, [R9, #VIDC20_VCR << 2]	;R4=VIDC VCR
 ADD     R4, R4, #2 - 1			;correct for N-2 (-1 as we add at VSync)

 LDR     R7, [R12, #VIDC20_width - VARS]	;R7=display width
 LDR     R6, [R12, #VIDC20_height - VARS]	;R6=display height

 SUBS    R3, R4, R6
 MULGT   R5, R3, R0			;R5=T1 ticks at end of frame
 MOVMI   R5, #0
 TEMP    R1
 STR     R5, IOC_ticks_flyback
 STR     R0, IOC_ticks_scanline
 LOCK    R1
; LDR     R7, [R9, #VIDC20_HCR << 2]	;show borders
; LDR     R3, [R9, #VIDC20_HSWR << 2]
; SUB     R7, R7, R3
; LDR     R3, [R9, #VIDC20_VSWR << 2]
; SUB     R6, R4, R3
					;*********** ASPECT RATIO *************
 MOV     R5, #0				;default to 5:4 eg MODE 13 320x256
 STR     R5, HAL_vidc_list_dirty

 LDR     R3, [R12, #cpu_id - VARS]
 EORS    R3, R3, #CPUID_Iyonix		;running on an Iyonix?
 BEQ     _leave_to_gpu			;YES, leave the GPU to handle

 LDR     R3, [R9, #VIDC20_HCR << 2]	;R3=VIDC HCR
 ADD     R3, R3, #8			;correct value
 CMP     R4, R3
 MOVHI   R5, #1				;2:3 eg MODE 48 320x480
 MOVHI   R7, R7, LSL #1			;double the width to 640x480
 CMP     R3, R4, LSL #1
 MOVHI   R5, #2				;10:4 eg MODE 15 640x256
 MOVHI   R6, R6, LSL #1			;double the height to 640x512
 STR     R5, [R12, #VIDC20_aspect_ratio - VARS]
 ._leave_to_gpu
					;***********************************
 ADD     R7, R7, #32 - 1		;ensure width is DIV 32
 BIC     R7, R7, #32 - 1
 CMP     R7, #32
 MOVLT   R7, #32			;ensure width is min 32 pixels
 CMP     R6, #32
 MOVLT   R6, #32			;ensure height is min 32 pixels
 LDR     R1, [R8, #VIDC_Type3.hdisp]	;get current values for comparison
 LDR     R2, [R8, #VIDC_Type3.vdisp]
 TEQ     R1, R7				;width changed?
 TEQEQ   R2, R6				;NO, height changed?
 LDMEQFD R13!, {R12, PC}		;NO, don't raise a GraphicsV 2
 MLA     R5, R6, R7, R7			;R5=pixels per frame
					;..with an extra line for blitter overrun

 LDR     R3, [R9, #VIDC20_HCR << 2]	;R3=VIDC HCR
 LDR     R4, [R9, #VIDC20_VCR << 2]	;R4=VIDC VCR
 LDR     R0, [R12, #old_VIDC20_HCR - VARS]
 LDR     R2, [R12, #old_VIDC20_VCR - VARS]

 TEQ     R0, R3				;has the overall width changed?
 TEQEQ   R2, R4				;NO, overall height?
 BNE     _keep				;YES
 ;size has changed, has it encroached into the border?
 LDR     R0, [R9, #VIDC20_HDSR << 2]
 LDR     R1, [R9, #VIDC20_HBSR << 2]
 CMP     R0, R1
 BLO     _keep
 LDR     R0, [R9, #VIDC20_VDSR << 2]
 LDR     R1, [R9, #VIDC20_VBSR << 2]
 CMP     R0, R1
 BLO     _keep
 LDR     R0, [R9, #VIDC20_HBER << 2]
 LDR     R1, [R9, #VIDC20_HDER << 2]
 CMP     R0, R1
 BLO     _keep
 LDR     R0, [R9, #VIDC20_VBER << 2]
 LDR     R1, [R9, #VIDC20_VDER << 2]
 CMP     R0, R1
 LDMHSFD R13!, {R12, PC}
 
 ._keep
 LDR     R0, [R9, #VIDC20_VBSR << 2]
 LDR     R1, [R9, #VIDC20_VDER << 2]
 CMP     R0, R1
 LDMHSFD R13!, {R12, PC}

 STR     R3, [R12, #old_VIDC20_HCR - VARS]
 STR     R4, [R12, #old_VIDC20_VCR - VARS]

 STR     R7, [R8, #VIDC_Type3.hdisp]
 STR     R6, [R8, #VIDC_Type3.vdisp]

 LDR     R6, current_display_driver
 MOV     R0, R8
 MOV     R6, R6, LSL #24		;R6=driver ID
 ORR     R4, R6, #2			;set MODE
 MOV     R9, #Vector_GraphicsV
 SWI     XOS_CallAVector		;PRM1-70
 TEMP    R0
 MOV     R4, #0
 STR     R4, current_GPU_bank		;need to reset screen bank
 LOCK    R0

 ORR     R4, R6, #9			;get DAG
 SWI     XOS_CallAVector		;PRM1-70

 ADR     R9, hwd_framestore
 STR     R0, [R9, #0]			;R0=physical address of framestore
 ADD     R2, R0, R5, LSL #2
 STR     R2, [R9, #4]			;set 2nd GPU frame buffer
 ADD     R2, R2, R5, LSL #2
 STR     R2, [R9, #8]			;set 3rd GPU frame buffer
 MOV     R2, R1				;2.74h - map all of the framestore
 MOV     R1, R0
 MOV     R0, #13
; ORR     R0, R0, #1 << 17		;give everyone access to the memory
 ORR     R0, R0, #%11 << 8		;cacheable and bufferable
 SWI     XOS_Memory			;map in IO memory
 ADR     R9, logical_VDU_start
 STR     R3, [R9, #0]			;store the new logical address
 ADD     R3, R3, R5, LSL #2		;add offset to 2nd buffer
 STR     R3, [R9, #4]			;2nd GPU frame buffer
 ADD     R3, R3, R5, LSL #2		;add offset to 2nd buffer
 STR     R3, [R9, #8]			;end of 2nd GPU frame buffer
 ADD     R3, R3, R5, LSL #2		;add offset to 2nd buffer
 STR     R3, [R9, #12]			;end of 3rd GPU frame buffer
LDMFD   R13!, {R12, PC}




;stop_fifty_hz_ticker
;--------------------
;Stops generating a 50hz ticker

.stop_fifty_hz_ticker
 STMFD   R13!, {R0-R1, R14}
 LDR     R1, vsync_trigger_enabled
 TEQ     R1, #0				;is it enabled?
 MOVNE   R0, #0				;YES,
 STRNE   R0, vsync_trigger_enabled	; |
 SWINE   XRT_Deregister			; |+  turn off
 LDR     R1, vsync_trigger_enabled + 4
 TEQ     R1, #0				;is it enabled?
 MOVNE   R0, #0				;YES,
 STRNE   R0, vsync_trigger_enabled + 4	; |
 SWINE   XRT_Deregister			; |+  turn off

 MOV     R0, #Module_Free
 LDR     R2, rt_stack
 SWI     XOS_Module
 MOV     R0, #Module_Free
 LDR     R2, rt_stack2
 SWI     XOS_Module
LDMFD   R13!, {R0-R1, PC}




;start_fifty_hz_ticker
;---------------------
;Generates a 50Hz counter for the PLL

.start_fifty_hz_ticker
 STMFD   R13!, {R0-R7, R14}
 MOV     R3, #0				;handle to pass in R12
 STR     R3, fifty_hz			;reset counters
 #if compile_GPU == 1 AND JIT_debug == 1
   STR     R3, blitter_counter
   STR     R3, vsync_counter
 #endif

 LDR     R1, vsync_trigger_enabled
 TEQ     R1, #0				;is it enabled?
 LDMNEFD R13!, {R0-R7, PC}		;YES, exit

 MOV     R0, #Module_Claim		;claim stack space
 MOV     R3, #rt_stack_size
 SWI     XOS_Module
 STRVC   R2, rt_stack
 MOVVC   R0, #Module_Claim
 MOVVC   R3, #rt_stack_size
 SWIVC   XOS_Module
 ADDVS   R13, R13, #4
 LDMVSFD R13!, {R1-R7, PC}
 STR     R2, rt_stack2

 MOV     R0, #19
 SWI     XOS_Byte			;wait for VSync
 SWI     XOS_ReadMonotonicTime		;add 1cs to time, so we trigger out
 ADD     R5, R0, #2
 STR     R0, rt_vflybk_time		;time to clear VFLYBK
 ADD     R5, R0, #1			;..of sync with a 50hz monitor (entry R10)
 ADR     R1, _fifty_hz_ticker		;entry point
 ADR     R3, VARS			;entry R12
 ADR     R2, fifty_hz			;entry R0
 ADR     R4, _rt_pollword		;pointer to pollword
 LDR     R6, rt_stack			;SYS stack
 ADD     R6, R6, #rt_stack_size
 MOV     R7, #64			;priority
 MOV     R0, #0				;flags (must be 0)
 STR     R4, [R4, #0]			;ensure pollword is non-zero
 SWI     XRT_Register
 STR     R0, vsync_trigger_enabled

 ADR     R1, _IOC_VFLYBK		;entry point
 LDR     R6, rt_stack2			;SYS stack
 ADD     R6, R6, #rt_stack_size
 ADR     R4, _rt_vflybk_pollword	;pointer to pollword
 ADR     R3, IOC_registers		;entry R12
 MOV     R2, #0				;entry R0
 STR     R4, [R4, #0]			;ensure pollword is non-zero
 MOV     R7, #192			;priority
 MOV     R0, #0				;flags (must be 0)
 SWI     XRT_Register
 STR     R0, vsync_trigger_enabled + 4
LDMFD   R13!, {R0-R7, PC}



;Entry:
; R0  = pointer to fifty_hz
; R12 = pointer to VARS
; R1, R3-R9, R11, R12 - may be corrupted

._fifty_hz_ticker
 MOV     R8, R14

 #if compile_GPU == 1 AND JIT_debug == 1
   LDR     R1, [R0, #0]
   ADD     R1, R1, #1			;increase counter
   TEQ     R1, #50
   MOVEQ   R1, #0
   STR     R1, [R0, #0]
   LDREQ   R3, blitter_counter
   STREQ   R3, blitter_FPS
   LDREQ   R3, vsync_counter
   STREQ   R3, monitor_hz
   STREQ   R1, blitter_counter
   STREQ   R1, vsync_counter
 #endif

 MOV     R0, #0
 ADD     R10, R10, #1
 STR     R10, rt_vflybk_time		;time to clear VFLYBK
 ADD     R10, R10, #1
 STR     R0, _rt_pollword		;don't call until timeout
 MOV     R2, R10
 MOV     R0, #%100			;R2 contains time to call us

 LDR     R4, [R12, #closest_screen_mode - VARS]
 CMN     R4, #1				;are we going to support this MODE?
 MOVEQ   PC, R8				;NO

 STR     R13, vsync_triggered
 #if compile_GPU == 1 AND Emulate_RTFM_Joystick == 1
   LDR     R14, [R12, #(Joystick_Keymapping-4) - VARS]
   TEQ     R14, #0			;are we mapping keys?
   BLEQ    Joystick_handler		;NO, call for RTFM support
 #endif
;The following section fixes Hero Quest locking, but adds microstutter
 LDR     R4, [R12, #vsync_unthreaded - VARS]
 TEQ     R4, #0				;do we need to raise VSync unthreaded?
 MOVEQ   PC, R8				;NO

 BL      blit_DA2_frame
 STMFD   R13!, {R0-R2}
 LDR     R4, [R12, #our_GraphicsV_ID - VARS]
 MOV     R9, #Vector_GraphicsV
 MOV     R4, R4, LSL #24
 ORR     R4, R4, #1			;trigger a VSync
 SWI     XOS_CallAVector		;PRM1-70
 LDMFD   R13!, {R0-R2}
;*********************************************************************
MOV     PC, R8


 ._rt_pollword		DCD 0		;VSync RTSupport pollword

;Entry:
; R0  = 0
; R12 = pointer to IOC_registers
; R1, R3-R9, R11, R12 - may be corrupted

._IOC_VFLYBK
 LDRB    R1, [R12, #IOC_CR >> 2]
 BIC     R1, R1, #IOC_IR_pin		;clear IR pin
 STRB    R1, [R12, #IOC_CR >> 2]
 STR     R0, _rt_vflybk_pollword	;suspend us
 LDR     R2, rt_vflybk_time
 MOV     R0, #%100			;R2 contains time to call us
MOV     PC, R14



 ._rt_vflybk_pollword	DCD 0		;VFLYBK RTSupport pollword
 .rt_vflybk_time	DCD 0		;time to call RTSupport
 .rt_stack	DCD 0
 .rt_stack2	DCD 0
#if compile_GPU == 1 AND JIT_debug == 1
 .monitor_hz		DCD 0		;refresh rate the monitor is at
 .blitter_counter	DCD 0		;blitter blit counter
 .blitter_FPS		DCD 0		;refresh rate of the blitter
#endif
 .vsync_trigger_enabled	DCD 0, 0	;<>0 if we're raising 50Hz VSyncs
 .fifty_hz		DCD 0		;50hz ticker for PLL
 .vsync_triggered	DCD 0		;<>0 if VSync occurred




;claim_GraphicsV_Vector
;----------------------
;Claim the GraphicsV vector, redirecting the framebuffer and RO to DA2

.claim_GraphicsV_Vector
 STMFD   R13!, {R0-R9, R12, R14}
 ADR     R12, VARS

 LDR     R0, [R12, #our_GraphicsV_ID - VARS]
 TEQ     R0, #0				;is GraphicsV claimed?
 LDMNEFD R13!, {R0-R9, R12, PC}		;YES, exit

 MOV     R0, #11
 MOV     R1, #-1
 SWI     XOS_ScreenMode
 STR     R1, current_display_driver

 MOV     R4, #8				;Supported features
 ORR     R4, R4, R1, LSL #24
 MOV     R9, #Vector_GraphicsV
 SWI     XOS_CallAVector		;PRM1-70
					;R0 is feature flags
					;R1 is supported formats
					;R2 is display buffer alignment
 ADR     R3, hwd_graphicsV8_values
 STMIA   R3, {R0-R2}

 MOV     R0, #64
 MOV     R1, #0
 ADR     R2, ADFFS_Title
 SWI     XOS_ScreenMode			;register GraphicsV driver
 STR     R0, [R12, #our_GraphicsV_ID - VARS]
 MOV     R8, R0				;R8=our driver ID

 MOV     R0, #Vector_GraphicsV
 ADR     R1, entry_GraphicsV		;address of our GraphicsV code
 MOV     R2, R12
 SWI     XOS_Claim			;PRM1-66

 BL      start_fifty_hz_ticker		;ensure blitter is running

 ADR     R1, HAL_vidc_list
 MOV     R0, #0
 STR     R0, current_GPU_bank		;set starting blitter frame
 MOV     R0, #32
 STR     R0, [R1, #VIDC_Type3.hdisp]	;reset GPU size

 MOV     R0, #65
 MOV     R1, R8
 SWI     XOS_ScreenMode			;init GraphicsV driver

 MVN     R0, #0
 SWI     XWimp_CommandWindow

 MOV     R0, #11
 MOV     R1, R8
 SWI     XOS_ScreenMode			;set our driver active
LDMFD   R13!, {R0-R9, R12, PC}




;release_GraphicsV_Vector
;------------------------
;Release the GraphicsV vector, redirecting the framebuffer to the GPU

.release_GraphicsV_Vector
 STMFD   R13!, {R0-R2, R10-R12, R14}

 ADR     R12, VARS			;value passed in R12
 LDR     R1, [R12, #our_GraphicsV_ID - VARS]
 TEQ     R1, #0				;is GraphicsV claimed?
 LDMEQFD R13!, {R0-R2, R10-R12, PC}		;NO, exit

 MOV     R0, #106
 MOV     R1, #0
 SWI     XOS_Byte
 MOV     R10, R1			;R10=current pointer state

 MOV     R0, #1
 SWI     XOS_ScreenMode
 MOV     R11, R1			;R11=current screen mode

 MOV     R0, #66
 SWI     XOS_ScreenMode			;GraphicsV driver is stopping

 BL      stop_fifty_hz_ticker

 MOV     R0, #Vector_GraphicsV
 ADR     R1, entry_GraphicsV		;address of our GraphicsV code
 MOV     R2, R12
 SWI     XOS_Release			;PRM1-68

 MOV     R0, #67
 LDR     R1, [R12, #our_GraphicsV_ID - VARS]
 SWI     XOS_ScreenMode			;Deregister GraphicsV driver

 MOV     R0, #0
 STR     R0, [R12, #our_GraphicsV_ID - VARS]

 MOV     R0, #11
 LDR     R1, current_display_driver
 SWI     XOS_ScreenMode			;set back to GPU driver

 MOV     R0, #0
 MOV     R1, R11
 SWI     XOS_ScreenMode			;set back to original mode

 MOV     R0, #-1
 MOV     R1, #0
 MOV     R2, #0
 MOV     R3, #65536
 MOV     R4, #65536
 SWI     XWimp_ForceRedraw		;force a desktop redraw

 MOV     R0, #106
 MOV     R1, R10
 SWI     XOS_Byte			;reinstate pointer
LDMFD   R13!, {R0-R2, R10-R12, PC}
