;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
}



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

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

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

 STMFD   R13!, {R2-R9}
 MOV     R4, #8				;Supported features
 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
 ORR     R0, R0, #%1			;allow hardware scrolling
 ORR     R1, R1, #%1111			;allow 1/2/4/8 bpp MODE's
 STMIA   R3, {R0-R2}
 LDMFD   R13!, {R2-R9}

 STR     R2, [R2, #graphicsV_claimed - VARS]
 MOV     R0, #Vector_GraphicsV
 ADR     R1, entry_GraphicsV		;address of our GraphicsV code
 SWI     XOS_Claim			;PRM1-66

 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
 STR     R0, [R1, #VIDC_Type3.vdisp]
LDMFD   R13!, {R0-R2, PC}




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

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

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

 MOV     R0, #0
 STR     R0, [R2, #graphicsV_claimed - VARS]
 TEMP    R1
 STR     R13, blit_in_progress		;pause the blitter
 LOCK    R1

 BL      stop_fifty_hz_ticker

 MOV     R0, #Vector_GraphicsV
 ADR     R1, entry_GraphicsV		;address of our GraphicsV code
 SWI     XOS_Release			;PRM1-68
LDMFD   R13!, {R0-R2, PC}




 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
#if PLL_vsync == 0 AND No_Frame_Pacing == 0
 .force_dirty_at	DCD 0		;time to force a blit
#endif
 .HAL_vidc_list_dirty	DCD 0		;set to 1 if MODE update required
 .pass_mode_change_to_GPU DCD 0




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

.entry_GraphicsV
 TST     R4, #&FF << 24			;default display?
 MOVNE   PC, R14			;NO, pass on request

 STR     R14, [R13, #-4]!
 LDR     R14, existing_DataAbort
 TEQ     R14, #0			;have we claimed the Data Abort vector?
 LDREQ   PC, [R13], #4			;NO, exit
 AND     R14, R4, #&FF			;mask to reason code
 TEQ     R14, #8			;features?
 BEQ     _features			;YES, keep it
 LDR     R14, [R12, #closest_screen_mode - VARS]
 CMN     R14, #1			;are we going to support this MODE?
 LDREQ   PC, [R13], #4			;NO

 AND     R14, R4, #&FF			;mask to reason code
 CMP     R14, #17
 ADDLS   PC, PC, R14, LSL #2		;use jump table below
LDR     PC, [R13], #4			;pass call on

 LDR     PC, [R13], #4			;0 - Vector claimed
 B       _vsync_occurred		;1 - VSync
 B       _set_MODE			;2 - Set Mode
 LDR     PC, [R13], #4			;3 - Set interlace
 LDR     PC, [R13], #4			;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
 B       _framestore_info		;9 - Framestore information
 B       _write_palette_entry		;10 - Write single palette entry
 B       _write_palette_entries		;11 - Write multiple palette entries
 B       _exit_unclaimed		;12 - Read palette entry
 B       _exit_unclaimed		;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       _exit_unclaimed		;17 - List pixel formats



._update_pointer
 ANDS    R4, R0, #1			;is the cursor being defined?
 STR     R4, Cursor_state		;store cursor state
 BEQ     _exit_claimed			;NO, exit

 STMFD   R13!, {R0-R3, R5-R6}
 ADR     R4, vidc20_regs - (VIDC20_HCR << 2)
 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]
 STR     R2, [R4, #VIDC20_VCSR << 2]

 LDRB    R1, [R3, #shape_t.height]	;get height
 ADD     R2, R2, R1			;work out VCER
 STR     R2, [R4, #VIDC20_VCER << 2]

 TST     R0, #%10			;is the cursor being updated?
 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
 #if compile_GPU == 1 AND JIT_WIMP_support == 1
   LDR     R3, jit_code
   TEQ     R3, #0			;is the JIT running?
   BEQ     _no_legacy_cursor		;NO
   BL      check_task_ID		;is the task supported?
   MOVEQ   R4, #&1F03800		;YES, legacy RO cursor buffer
   ._no_legacy_cursor
 #endif

 STR     R4, [R12, #MEMC_Cinit - VARS]	;set MEMC Cinit

 TEQ     R1, R4				;do we need to copy the cursor?
 BEQ     _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}
B       _exit_claimed




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

 TEQ     R0, #0				;normal palette entry?
 LDRNE   PC, [R13], #4			;NO, pass it on

 CMP     R2, #255			;is it a valid cursor palette entry?
 BHI     _exit_unclaimed		;NO, pass to RISCOS

 ADR     R4, GPU_palette
 MOV     R1, R1, ROR #8			;SSBBGGRR
 STR     R1, [R4, R2, LSL #2]		;store the palette entry
 MOV     R1, R1, ROR #24		;BBGGRRSS

 MOV     R4, #10
 B       _exit_unclaimed			;pass to RISCOS


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

 ADR     R4, Cursor_palette
 MOV     R1, R1, ROR #8			;SSBBGGRR
 STR     R1, [R4, R2, LSL #2]		;store the palette entry
 MOV     R1, R1, ROR #24		;BBGGRRSS

 MOV     R4, #10
B       _exit_unclaimed			;pass to RISCOS




._write_palette_entries
 TEQ     R0, #0				;normal palette entry?
 BEQ     _write_palette_entries_normal

 TEQ     R0, #2				;cursor palette entry?
 LDRNE   PC, [R13], #4			;NO, pass it on

 CMP     R2, #3				;is index valid?
 BHI     _exit_unclaimed		;NO, pass call on

 STMFD   R13!, {R0-R3}
 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     R14, [R12, #current_mode_bitdepth - VARS]
 MOV     R4, #1
 MOV     R14, R4, LSL R14		;R14=bpp
 MOV     R4, R4, LSL R14		;R4=max palette entries
 CMP     R2, R4				;is index valid?
 MOVHS   R4, #11
 BHS     _exit_unclaimed		;NO, pass call on

 STMFD   R13!, {R0-R3}
 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
 LDMFD   R13!, {R0-R3}

 MOV     R4, #11
B       _exit_unclaimed			;pass call on




._set_MODE
 LDR     R14, pass_mode_change_to_GPU
 TEQ     R14, #0
 LDRNE   PC, [R13], #4			;pass to GPU

 STMFD   R13!, {R0-R9}
 MOV     R14, R0
 ADR     R0, HAL_vidc_list		;copy the VIDC list for updates
 TEQ     R0, R14			;are we updating VIDC?
 BEQ     _set_MODE_P1			;YES, skip copy

 ADD     R1, R0, #256 - 4		;ensure we don't overrun
 MOV     R9, R0
 ._set_MODE_L1
   LDR     R4, [R14], #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

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

 BL      blit_update_VIDC

 LDMFD   R13!, {R0-R9}			;clear down stack
B       _exit_unclaimed			;pass to OS




._set_DAG
 LDR     R14, set_DAG_swap_frame
 TEQ     R14, #0			;are we swapping the GPU?
 MOVNE   R14, #0			;YES,
 STRNE   R14, set_DAG_swap_frame	; |   mark as completed
 TEQEQ   R0, #0				;NO, set current video display?
 LDRNE   PC, [R13], #4			; |+  pass on to GPU

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




._framestore_info
 LDR     R14, hwd_framestore
 TEQ     R14, #9			;do we need the hardware info?
 LDREQ   PC, [R13], #4			;YES, pass on call
B       _exit_unclaimed




._vet_MODE
 MOV     R0, #0				;allow all MODEs
					;fall-thru to be claimed



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




;_vsync_occurred
;--------------
;VSync occurred
;If we're generating a 50Hz VSync, allow ours through and claim the GPU's
;
;Exit:
; R0-R3 corrupt if claimed, preserved otherwise

._vsync_occurred
 #if compile_GPU == 1 AND No_Frame_Pacing == 0
   #if PLL_vsync == 1 AND No_Frame_Pacing == 0 AND JIT_debug == 1
     LDR     R14, vsync_counter
     ADD     R14, R14, #1		;count actual VSyncs
     STR     R14, vsync_counter
   #endif
   LDR     R14, vsync_being_triggered
   TEQ     R14, #0			;is it our 50Hz VSync?
   MOVNE   R14, #0			;YES,
   STRNE   R14, vsync_being_triggered	; |   mark actioned
   #if PLL_vsync == 0 AND No_Frame_Pacing == 0
     LDRNE   PC, [R13], #4		; |+  pass on
   #endif
   #if PLL_vsync == 1 AND No_Frame_Pacing == 0
     BEQ     _exit_claimed		;claim if not us
   #endif

   #if PLL_vsync == 0 AND No_Frame_Pacing == 0
     LDR     R14, vsync_trigger_enabled
     TEQ     R14, #0			;are we raising VSyncs?
     LDRNE   R14, vsync_counter		;YES,
     ADDNE   R14, R14, #1		; |   count actual VSyncs
     STRNE   R14, vsync_counter		; |
     BNE     _exit_claimed		; |+  exit claimed
   #endif
 #endif

 #if compile_GPU == 1 AND No_Frame_Pacing == 1
   LDR     R14, vsync_counter
   ADD     R14, R14, #1			;count VSyncs
   STR     R14, vsync_counter
 #endif

 BL      blit_DA2_frame
LDR     PC, [R13], #4			;pass on



 .set_DAG_swap_frame	DCD 0
 .blit_in_progress	DCD 0
#if JIT_debug == 1 OR PLL_vsync == 0
 .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

 MOV     R4, #0
 STR     R4, HAL_vidc_list_dirty

 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     R7, [R12, #VIDC20_width - VARS]	;R7=display width
 LDR     R6, [R12, #VIDC20_height - VARS]	;R6=display height

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

 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
					;*********** ASPECT RATIO *************
 MOV     R5, #0				;default to 5:4 eg MODE 13 320x256

 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?
 LDMEQFD R13!, {R12, PC}		;NO, don't raise a GraphicsV 2
 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]

 TEMP    R9
 STR     R13, pass_mode_change_to_GPU
 MOV     R0, R8
 MOV     R4, #2				;set MODE
 MOV     R9, #Vector_GraphicsV
 SWI     XOS_CallAVector		;PRM1-70
 MOV     R4, #0
 STR     R4, pass_mode_change_to_GPU
 STR     R4, current_GPU_bank		;need to reset screen bank

 MOV     R4, #9				;get DAG
 STR     R4, hwd_framestore		;pass to GPU
 LOCK    R9
 MOV     R9, #Vector_GraphicsV
 SWI     XOS_CallAVector

 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     R1, R0
 MOV     R2, R5, LSL #2			;R2=1 frame in bytes
 ADD     R2, R2, R2, LSL #1		;need three screen buffers
 MOV     R0, #13
; ORR     R0, R0, #1 << 17		;give everyone access to the memory
 #if compile_GPU == 1 AND Cached_DA2 == 1
   ORR     R0, R0, #%11 << 8		;cacheable and bufferable
 #endif
 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
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

 #if PLL_vsync == 0
   ADR     R0, _Read_50HzVal		;read ADFFS$50Hz
   MOV     R2, #1 << 31			;check if variable exists
   MOV     R3, #0
   MOV     R4, #0
   SWI     XOS_ReadVarVal		;PRM1-314
   ADDS    R2, R2, R2			;clear V, does ADFFS$50Hz exist?
   #if PLL_vsync == 0 AND JIT_debug == 1
     MOVNE   R0, #50			;YES,
     STRNE   R0, blitter_FPS		; |   blit every frame and
     STRNE   R0, monitor_hz		; |   assume monitor is 50Hz
   #endif
   LDMNEFD R13!, {R0-R7, PC}		;YES, exit
 #endif

 MOV     R0, #19
 SWI     XOS_Byte			;wait for VSync
 SWI     XOS_ReadMonotonicTime		;add 1cs to time, so we trigger out
 ADD     R5, R0, #1			;..of sync with a 50hz monitor (entry R10)
 ADR     R1, _fifty_hz_ticker		;entry point
 ADR     R2, fifty_hz			;handle to pass in R0
 ADR     R4, _rt_pollword		;pointer to pollword
 ADR     R6, _rt_stack			;SYS stack
 ADR     R7, _rt_priority		;priority
 MOV     R0, #0				;flags (must be 0)
 STR     R4, _rt_pollword		;ensure it's non-zero
 SWI     XRT_Register
 STR     R0, vsync_trigger_enabled

 ADR     R1, _IOC_VFLYBK		;entry point
 ADR     R6, _rt_stack2			;SYS stack
 ADR     R4, rt_pollword2		;pointer to pollword
 MOV     R2, #0				;value for R0
 MOV     R0, #0				;flags (must be 0)
 SWI     XRT_Register
 STR     R0, vsync_trigger_enabled + 4
LDMFD   R13!, {R0-R7, PC}

 #if PLL_vsync == 0
 ._Read_50HzVal	DCB "ADFFS$50Hz", 0
		ALIGN
 #endif
 ._rt_pollword	DCD 2
 ._rt_priority	DCB "VideoPaint", 0
		ALIGN
 DBB 512
 ._rt_stack
 DBB 128
 ._rt_stack2



; R0=0 on entry
._IOC_VFLYBK
 STMFD   R13!, {R14}
 BL      IOC_VFLYBK_low			;bring IR pin low
 STR     R0, rt_pollword2		;suspend us
 LDMFD   R13!, {PC}



; R11, R12 - may be corrupted
._fifty_hz_ticker
 STMFD   R13!, {R3-R9, R14}

 #if JIT_debug == 1 OR PLL_vsync == 0
   LDR     R11, [R0, #0]
   ADD     R11, R11, #1			;increase counter
   TEQ     R11, #50
   STRNE   R11, [R0, #0]
   BNE     _P1
  
   MOV     R12, #0
   #if compile_GPU == 1 AND JIT_debug == 1
     LDR     R11, blitter_counter
     STR     R11, blitter_FPS
     LDR     R11, vsync_counter
     STR     R11, monitor_hz
     STR     R12, blitter_counter
   #endif
   STR     R12, vsync_counter
   STR     R12, [R0, #0]
  
   #if PLL_vsync == 0
     TEQ     R11, #50			;is the monitor at 50hz?
     BNE     _P1			;NO
     BL      stop_fifty_hz_ticker	;YES, stop ticker and use VSync
     LDMFD   R13!, {R3-R9, PC}
   #endif
  
   ._P1
 #endif
 #if compile_GPU == 1 AND No_Frame_Pacing == 0
   #if PLL_vsync == 0 AND No_Frame_Pacing == 0
     LDR     R1, force_dirty_at		;time to force next frame blit
     CMP     R10, R1			;do we need to force an update?
     ADDHS   R14, R10, #2		;YES, force at 50 FPS
     STRHS   R14, force_dirty_at	;and blit the display
     BLHS    blit_DA2_frame
   #endif

   MOV     R4, #1			;trigger a VSync
   STR     R4, vsync_being_triggered	;tell GraphicsV 1 to pass-thru
   #if PLL_vsync == 0 AND No_Frame_Pacing == 0
     MOV     R9, #Vector_GraphicsV
     SWI     XOS_CallAVector		;PRM1-70
   #endif
 #endif

 ._exit
 MOV     R0, #0
 ADD     R10, R10, #2
 STR     R0, _rt_pollword		;don't call until timeout
 MOV     R2, R10
 MOV     R0, #%100			;R2 contains time to call us
LDMFD   R13!, {R3-R9, PC}


#if 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
 .rt_pollword2		DCD 0
 .vsync_trigger_enabled	DCD 0, 0	;<>0 if we're raising 50Hz VSyncs
 .fifty_hz		DCD 0		;50hz ticker for PLL
 .vsync_being_triggered	DCD 0
