;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
 .GPU_palette		DBD 256		;GPU palette
 .Cursor_palette	DBD 4		;hardware cursor palette
 .HAL_vidc_list		DBB 252		;VIDC type 3 list for current MODE
			DCD -1		;terminator
 .GPU_screen_start	DCD 0		;start address in GPU of top\left
 .hwd_graphicsV8_values	DCD 0, 0, 0	;return values for GraphicsV 8
 .current_GPU_bank	DCD 0		;current back-buffer blitter is using
 .logical_VDU_start	DBD 4		;framestore addresses and size
 .HAL_vidc_list_dirty	DCD 0		;set to 1 if MODE update required
 .current_display_driver	DCD 0	;driver ID we're taking over
 .graphicsV_pixel_formats	DCD 0, 0	;pixel formats and count

#if compile_GPU == 1 AND use_overlay == 0
 .hwd_framestore	DBD screen_banks	;physical address of hardware framestore
#endif


.our_pixel_formats_list
	;NColour	ModeFlags	Log2BPP
 DCD	1,		0,		0		;1bpp
 DCD	3,		0,		1		;2bpp
 DCD	15,		0,		2		;4bpp
 DCD	63,		0,		3		;8bpp semi-palette
 DCD	255,		1<<7,		3		;8bpp
 DCD    -1,		0,		5		;32bpp TBGR


#if use_EDID == 1 OR use_overlay == 1
  .local_VIDC3
  DCD 3
  DCD 5			;32bit
  ; pi-top  720x576 50Hz
  DCD 64,68,0,720,0,12	;horizontal
  DCD 5,39,0,576,0,5	;vertical
  DCD 27000		;Pixel rate
  DCD 3			;Sync

  ; pi-top  640x480 75Hz
  ;DCD 64,120,0,640,0,16	;horizontal
  ;DCD 3,16,0,480,0,1	;vertical
  ;DCD 31500		;Pixel rate
  ;DCD 3		;Sync
  ; LG  720x400 70Hz
  ;DCD 108,54,0,720,0,18	;horizontal
  ;DCD 2,34,0,400,0,13	;vertical
  ;DCD 28322		;Pixel rate
  ;DCD 1		;Sync
  ; LG 800x600 60Hz
  ;DCD 128,88,0,80,0,40	;horizontal
  ;DCD 4,23,0,600,0,1	;vertical
  ;DCD 40000		;Pixel rate
  ;DCD 0		;Sync
  
  DCD -1
#endif

#if use_overlay == 1
STRUCT mode_block
{
 .flags		DCD 0
 .width		DCD 0
 .height	DCD 0
}
STRUCT overlay_info
{
 .id		DCD 0
 .flags		DCD 0
 .min_width	DCD 0
 .min_height	DCD 0
 .max_width	DCD 0
 .max_height	DCD 0
 .size
}

.current_GPU_res	DCD 0,0,0,0
#endif





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

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

.entry_GraphicsV
 STMFD   R13!, {R1, R14}

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

 LDR     R1, [R12, #our_GraphicsV_ID - VARS]
 TEQ     R1, R4, LSR #24		;event for us?
 LDMNEFD R13!, {R1, 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, #17			;pixel formats?
 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?
 BNE     _retain

 LDMFD   R13!, {R1}
 B       _pass_to_GPU			;NO

 ._retain
 LDMFD   R13!, {R1}
 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
 B       _framestore_info		;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       _pixel_formats			;17 - List pixel formats
 B       _pass_to_GPU			;18 - Read Info
 B       _vet_MODE			;19 - Vet mode 2

._pixel_formats
 LDR     R14, graphicsV_pixel_formats
 TEQ     R14, #0			;are we supporting 17?
 LDREQ   PC, [R13], #4			;NO
 ADR     R14, graphicsV_pixel_formats
 LDMIA   R14, {R0, R1}
 B       _exit_claimed


._GPU_VSync
 LDMFD   R13!, {R1}
 #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
 LDRB    R14, [R12, #vsync_unthreaded - VARS]
 TEQ     R14, #0			;do we need to raise VSync unthreaded?
 BNE     _exit_claimed			;YES
;*********************************************************************

 LDRB    R14, [R12, #vsync_triggered - VARS]	;YES
 TEQ     R14, #0			;50hz ticked?
 BEQ     _exit_claimed			;NO, ignore VSync
 MOV     R14, #0
 STRB    R14, [R12, #vsync_triggered - VARS]	;mark as actioned
 #if compile_GPU == 1 AND use_overlay == 1
   LDRB    R14, [R12, #current_mode_bitdepth - VARS]
   CMP     R14, #3			;>8bpp
   BHI     _raise_as_us
 #endif
 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  GraphicsV 1
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  GraphicsV n
 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, [R12, #Cursor_state - VARS]		;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, #%11			;%01 = hardware scrolling
					;%10 = hardware pointer
 BICNE   R0, R0, #%1011000		;%0001000 = no framestore
					;%0010000 = VSync's generated
					;%1000000 = 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     R4, GPU_palette
 MOV     R0, R1, ROR #8			;SSBBGGRR
 BL      correct_gamma
 STR     R0, [R4, R2, LSL #2]		;store the palette entry
 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     R4, Cursor_palette
 MOV     R0, R1, ROR #8			;SSBBGGRR
 BL      correct_gamma
 STR     R0, [R4, R2, LSL #2]		;store the palette entry
 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
 LDRB    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
   BL      correct_gamma
   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]
 STRB    R4, [R12, #current_mode_bitdepth - VARS]	;store it for the VIDC code
 #if use_overlay == 0
   MOV     R1, #5			;32 bpp
   STR     R1, [R8, #VIDC_Type3.log2bpp]
 #endif
 LDR     R5, [R8, #VIDC_Type3.hdisp]
 LDR     R6, [R8, #VIDC_Type3.vdisp]
 STR     R5, [R12, #VIDC20_width - VARS]
 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

 #if use_overlay == 0
   MOV     R0, #1			;use DA2
 #endif
 MOV     R1, #0				;start alignment
 MOV     R2, #0				;no padding
					;fall-thru to exit_claimed
 #if use_overlay == 1
   LDR     R14, [R0, #VIDC_Type3.log2bpp]
   CMP     R14, #3			;8bpp and lower are emulated
   MOVLS   R0, #1			;use DA2
   MOVHI   R0, #3			;point to overlay
 #endif

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


._framestore_info
; #if use_overlay == 0
   LDR     PC, [R13], #4
; #endif
; #if use_overlay == 1
;   LDR     R14, [R12, #closest_screen_mode - VARS]
;   CMN     R14, #1			;are we going to support this MODE?
;   LDREQ   PC, [R13], #4		;NO
;
;   LDRB    R14, [R12, #current_mode_bitdepth - VARS]
;   CMP     R14, #3			;8bpp and lower are emulated
;   LDRLS   PC, [R13], #4
;
;   STMFD   R13!, {R2-R4}
;   ADR     R4, logical_VDU_start	;R0=framebuffer addresses
;   LDR     R1, [R4, #0]
;   MOV     R0, #65
;   SWI     XOS_Memory			;translate to physical
;;   MOV     R1, R2			;size
;   LDR     R1, [R4, #4]
;   LDR     R2, [R4, #0]
;   SUB     R1, R1, R2
;   LDMFD   R13!, {R2-R4}
;   B       _exit_claimed
; #endif



#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
 ADD     R9, R12, #(vidc20_regs - (VIDC20_HCR << 2)) - VARS

					;***********************************
 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
					;*********** ASPECT RATIO *************
 MOV     R5, #0				;default to 5:4 eg MODE 13 320x256
 STR     R5, HAL_vidc_list_dirty	;mark VIDC change as actioned
 LOCK    R1

 #if use_overlay == 0
   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
 #endif

 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
 #if use_overlay == 0
   MOVHI   R7, R7, LSL #1		;double the width to 640x480
 #endif
 CMP     R3, R4, LSL #1
 MOVHI   R5, #2				;10:4 eg MODE 15 640x256
 #if use_overlay == 0
   MOVHI   R6, R6, LSL #1		;double the height to 640x512
 #endif
 STR     R5, [R12, #VIDC20_aspect_ratio - VARS]

 ._leave_to_gpu				;***********************************
 #if use_overlay == 0
   #if use_overlay == 0 AND use_EDID == 0
     ADD     R7, R7, #32 - 1		;ensure width is DIV 32
     BIC     R7, R7, #32 - 1
   #endif
   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?
   STR     R7, [R8, #VIDC_Type3.hdisp]	;set as it may have been cleared
   STR     R6, [R8, #VIDC_Type3.vdisp]
   LDMEQFD R13!, {R12, PC}		;NO, don't raise a GraphicsV 2
   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 not 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
;   BHS     _borders			;NO, just clear the borders
   LDMHSFD R13!, {R12, PC}

   ._keep
   STR     R3, [R12, #old_VIDC20_HCR - VARS]
   STR     R4, [R12, #old_VIDC20_VCR - VARS]
  
   LDR     R0, [R9, #VIDC20_VBSR << 2]
   LDR     R1, [R9, #VIDC20_VDER << 2]
   CMP     R0, R1
;   BHS     _borders
   LDMHSFD R13!, {R12, PC}
  
   #if use_overlay == 0 AND use_EDID == 1
     ADR     R0, local_VIDC3
     LDR     R7, [R0, #VIDC_Type3.hdisp]
     LDR     R6, [R0, #VIDC_Type3.vdisp]
   #endif
   #if use_overlay == 0 AND use_EDID == 0
     MOV     R0, R8
   #endif
   MLA     R5, R6, R7, R7		;R5=pixels per frame
					;..with an extra line for blitter overrun
   LDR     R6, current_display_driver
   MOV     R6, R6, LSL #24		;R8=driver ID
   ORR     R4, R6, #2			;set MODE
   MOV     R9, #Vector_GraphicsV
   SWI     XOS_CallAVector		;PRM1-70  GraphicsV 2
   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  GraphicsV 9
  
   ADR     R9, hwd_framestore
   STR     R0, [R9, #0]			;R0=physical address of framestore
   #if use_overlay == 0 AND screen_banks > 1
     ADD     R2, R0, R5, LSL #2
     STR     R2, [R9, #4]		;set 2nd GPU frame buffer
   #endif
   #if use_overlay == 0 AND screen_banks > 2
     ADD     R2, R2, R5, LSL #2
     STR     R2, [R9, #8]		;set 3rd GPU frame buffer
   #endif
   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]			;GPU framestore 1
   #if use_overlay == 0 AND screen_banks > 1
     ADD     R3, R3, R5, LSL #2
     STR     R3, [R9, #4]		;GPU framestore 2
   #endif
   #if use_overlay == 0 AND screen_banks > 2
     ADD     R3, R3, R5, LSL #2
     STR     R3, [R9, #8]		;GPU framestore 3
   #endif
   SUB     R5, R5, R7			;reduce by 1 line
   STR     R5, [R9, #12]		;size of each framebuffer in pixels
   ._borders
					;*********** BLANK BORDERS ***********
   STMFD   R13!, {R10-R11}
   BL      calc_borders			;R4=top*gpu_width*left border
   LDR     R8, [R13, #blitter_stack.GPU_border]
   ADD     R13, R13, #blitter_stack.size	;drop stacked registers from calc_borders
   ADR     R11, logical_VDU_start
   LDMIA   R11, {R1, R2, R6}		;load up to three GPU framestore addresses

					;******* BLANK TOP/LEFT BORDER ********
   MOV     R0, #0
   #if use_overlay == 0 AND compile_DEBUG == 1
     BL      random_number_R0
     BIC     R0, R0, #&FF<<24
   #endif
   MOV     R5, R0
   MOV     R7, R0
   MOV     R9, R0
   MOV     R10, R0
   MOV     R11, R0
   MOV     R12, R0
   MOV     R14, R0
   ._top_border_L1
     SUBS    R4, R4, #16
     STMPLIA R1!, {R0,R5,R7,R9,R10,R11,R12,R14}		;8 pixels in framebuffer 1
     #if use_overlay == 0 AND screen_banks > 1
       STMPLIA R2!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       STMPLIA R6!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 3
     #endif
     STMPLIA R1!, {R0,R5,R7,R9,R10,R11,R12,R14}		;8 pixels in framebuffer 1
     #if use_overlay == 0 AND screen_banks > 1
       STMPLIA R2!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       STMPLIA R6!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 3
     #endif
   BGT     _top_border_L1
   ADDMIS  R4, R4, #16
   ._top_border_L2
     SUBGTS  R4, R4, #2
     STRPLD  R10, R11, [R1], #8		;write two pixels to framebuffer 1
     #if use_overlay == 0 AND screen_banks > 1
       STRPLD  R10, R11, [R2], #8	;write two pixels to framebuffer 2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       STRPLD  R10, R11, [R6], #8	;write two pixels to framebuffer 3
     #endif
   BGT     _top_border_L2
					;****** BLANK LEFT/RIGHT BORDER *******
   #if use_overlay == 0 AND use_EDID == 1
     LDR     R7, local_VIDC3 + VIDC_Type3.hdisp
   #endif
   #if use_overlay == 0 AND use_EDID == 0
     LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp
   #endif
   SUB     R7, R7, R8			;adjust GPU width to get image size
  
   ._blank_Y_L1
     MOV     R5, R8			;R5=GPU left+right border
     ADD     R1, R1, R7, LSL #2
     #if use_overlay == 0 AND screen_banks > 1
       ADD     R2, R2, R7, LSL #2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       ADD     R6, R6, R7, LSL #2
     #endif
     ._left_right_border_L1
       SUBS    R5, R5, #2
       STRGED  R10, R11, [R1], #8	;write two pixels to framebuffer 1
       #if use_overlay == 0 AND screen_banks > 1
         STRGED  R10, R11, [R2], #8	;write two pixels to framebuffer 2
       #endif
       #if use_overlay == 0 AND screen_banks > 2
         STRGED  R10, R11, [R6], #8	;write two pixels to framebuffer 3
       #endif
     BGT     _left_right_border_L1
  
     SUBS    R3, R3, #1
   BNE     _blank_Y_L1
					;******** BLANK BOTTOM BORDER *********
   ._no_display
   ADR     R8, logical_VDU_start
   LDR     R7, [R8, #0]			;framestore 1
   LDR     R4, [R8, #12]		;size of buffer
   ADD     R4, R7, R4, LSL #2
  
   MOV     R5, R0
   MOV     R7, R0
   MOV     R9, R0
   ._bottom_border_L1
     CMP     R1, R4
     STMLOIA R1!, {R0,R5,R7,R9,R10,R11,R12,R14}		;8 pixels in framebuffer 1
     #if use_overlay == 0 AND screen_banks > 1
       STMLOIA R2!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       STMLOIA R6!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 3
     #endif
     STMLOIA R1!, {R0,R5,R7,R9,R10,R11,R12,R14}		;8 pixels in framebuffer 1
     #if use_overlay == 0 AND screen_banks > 1
       STMLOIA R2!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 2
     #endif
     #if use_overlay == 0 AND screen_banks > 2
       STMLOIA R6!, {R0,R5,R7,R9,R10,R11,R12,R14}	;8 pixels in framebuffer 3
     #endif
   BLO     _bottom_border_L1
  LDMFD   R13!, {R10-R11, R12, PC}
 #endif
 #if use_overlay == 1
   CMP     R6, #4
   CMPHI   R7, #4			;ensure width/height are at least 4
   LDMLOFD R13!, {R12, PC}

   LDR     R8, current_display_driver
   MOV     R8, R8, LSL #24		;R8=driver ID

#if overlay_fixed_size == 0
   LDR     R6, [R9, #VIDC20_HCR << 2]	;R6=VIDC HCR
   LDR     R7, [R9, #VIDC20_VCR << 2]	;R7=VIDC VCR

   LDR     R1, [R12, #VIDC20_width - VARS]
   LDR     R2, [R12, #VIDC20_height - VARS]
   CMP     R6, R1			;sanity check HCR/VCR (fixes Bubble Impact)
   MOVLO   R6, R1
   CMP     R7, R2
   MOVLO   R7, R2
#endif
#if overlay_fixed_size == 1
   MOV     R6, #1024
   MOV     R7, #1024
#endif

   LDR     R0, [R12, #old_VIDC20_HCR - VARS]
   LDR     R1, [R12, #old_VIDC20_VCR - VARS]
   MOV     R9, #Vector_GraphicsV
   TEQ     R0, R6			;has the overall width changed?
   TEQEQ   R1, R7			;NO, overall height?
   BEQ     _check_screen_size		;check if borders have changed

   STMFD   R13!, {R6-R7}
   BL      destroy_overlay
   ADD     R0, R12, #overlay_mode - VARS
   STR     R6, [R0, #mode_block.width]
   STR     R7, [R0, #mode_block.height]
   ORR     R1, R7, R6, LSL #16		;aspect ratio
   MOV     R2, #1			;scaling is required
   MOV     R3, #%1			;head mask - use head 0
   ORR     R4, R8, #20			;create overlay
   SWI     XOS_CallAVector		;PRM1-70  GraphicsV 20
   TEQ     R4, #0			;did the overlay get created?
   LDMFD   R13!, {R6-R7}
   BNE     _shutdown_blitter		;failed, shutdown the blitter

   STR     R6, [R12, #old_VIDC20_HCR - VARS]
   STR     R7, [R12, #old_VIDC20_VCR - VARS]

   STR     R6, local_VIDC3+VIDC_Type3.hdisp
   STR     R7, local_VIDC3+VIDC_Type3.vdisp
   MOV     R6, #0
   STR     R4, [R12, #overlay_transform + 8 - VARS]	;YES, force an overlay update
   ORR     R0, R8, R0, LSL #16		; | R0=<driver><overlay><15..0 unset>
   ADD     R4, R12, #overlay_id - VARS	; |
   STMIA   R4, {R0-R3, R5-R6}		; | 
   MOV     R8, R0			; | overlay no
   ADR     R7, logical_VDU_start	; | R7=framebuffer addresses
;   MOV     R0, #0			; | framebuffer 1
;   BL      map_overlay_bank		; | EQ if successful, R3=address, R4=stride
;   STR     R3, [R7, #0]			; | logical address
;   MOVEQ   R6, R4			; |
;   #if use_overlay == 1 AND screen_banks > 1
;     MOV     R0, #1			; | framebuffer 2
;     BL      map_overlay_bank		; |
;     STR     R3, [R7, #4]		; | logical address
;     MOVEQ   R6, R4			; |
;   #endif				; |
;   #if use_overlay == 1 AND screen_banks > 2
;     MOV     R0, #2			; | framebuffer 3
;     BL      map_overlay_bank		; |
;     STR     R3, [R7, #8]		; |
;     MOVEQ   R6, R4			; |
;   #endif
;   MOV     R6, R6, LSR #2		;assume stride is in bytes
;   STR     R6, local_VIDC3+VIDC_Type3.hdisp

   LDR     R1, [R12, #overlay_mode - VARS + mode_block.width]
   LDR     R2, [R12, #overlay_mode - VARS + mode_block.height]
   SUB     R2, R2, #1			;reduce to avoid overrun
   MUL     R3, R1, R2			;R3=pixels per frame
   STR     R3, [R7, #12]		;size of each framestore in pixels

   #if use_overlay == 1 AND JIT_debug == 1
     LDR     R8, [R12, #overlay_id - VARS]
     AND     R4, R8, #&FF0000		;R4=overlay ID
     LDR     R0, debug_overlay_id
     ORR     R0, R0, R4, LSR #8
     STR     R0, [R13, #-4]!
     ORR     R4, R8, #28		;set Z order
     BIC     R4, R4, #&FF0000
     MOV     R0, R13
     MOV     R1, #2			;2 entries
     SWI     XOS_CallAVector		;PRM1-70  GraphicsV 28
     ADD     R13, R13, #4
   #endif

   ._check_screen_size
   STMFD   R13!, {R8-R11}
   ADD     R8, R12, #(vidc20_regs - (VIDC20_HCR << 2)) - VARS
   LDR     R6, [R8, #VIDC20_HBSR << 2]	;HBSR
   LDR     R7, [R8, #VIDC20_HDSR << 2]	;HDSR
   LDR     R4, [R8, #VIDC20_HDER << 2]	;HDER
   LDR     R5, [R8, #VIDC20_HBER << 2]	;HBER
   SUB     R6, R6, R7			;R6=start border overlap if +ve
   SUB     R5, R4, R5			;R5=end border overlap if +ve
   SUBS    R6, R6, #18-12		;correct HDSR/HBSR
   MOVMI   R6, #0			;zero if no overlapping start border
   ADDS    R5, R5, #18-12		;correct HDER/HBER
   MOVMI   R5, #0			;zero if no overlapping end border
   ADD     R11, R5, R6			;R11=total border overlap
   LDR     R9, [R8, #VIDC20_VDSR << 2]
   LDR     R10, [R8, #VIDC20_VBSR << 2]
   LDRB    R14, [R12, #display_V_border - VARS]	;R14=0 if top/bottom border need displaying
   SUBS    R9, R10, R9			;R9=top border if +ve
   LDR     R2, [R12, #VIDC20_width - VARS]	;R2=screen width
   LDR     R3, [R12, #VIDC20_height - VARS]	;R3=screen height
   SUBGT   R3, R3, R9			;reduce height by top border overlap
   SUBMI   R3, R3, R9, LSR R14		;increase screen height if borders are displayed
   MOVGE   R0, R9
   RSBMI   R0, R9, #1
   MOV     R0, R0, LSR R14		;R0=top border
   LDR     R6, [R8, #VIDC20_VDER << 2]	;R6=VDER
   LDR     R10, [R8, #VIDC20_VBER << 2]	;R12=VBER
   SUBS    R10, R6, R10			;R10=bottom border overlap if +ve
   SUBGT   R3, R3, R10			;reduce screen height by bottom border
   SUBMI   R3, R3, R10, LSR R14		;increase screen height if borders are displayed
   SUB     R2, R2, R11			;reduce width by border overlap

   LDR     R4, [R12, #overlay_clip - VARS]	;width ratio
   TEQ     R4, #0
   BEQ     _no_ratio
   LDR     R10, [R12, #overlay_clip+8 - VARS]
   LDR     R8, [R12, #overlay_clip+4 - VARS]	;height ratio
   SUB     R2, R2, R10, LSL #1			;reduce width by leftclip*2
   MUL     R10, R2, R8
   BL      blit_DIV_R10_R4_R11
   MOV     R3, R10
   ._no_ratio

   LDR     R7, current_GPU_res		;R7=GPU width
   LDR     R8, current_GPU_res+4	;R8=GPU height
   LDR     R5, [R12, #overlay_mode - VARS + mode_block.width]
   LDR     R6, [R12, #overlay_mode - VARS + mode_block.height]
   LDR     R9, [R12, #VIDC20_aspect_ratio - VARS]
   CMP     R9, #1
   MOVEQ   R2, R2, LSL #1		;2:3 double width pixels eg MODE 48 320x480
   MOVEQ   R5, R5, LSL #1
   MOVHI   R3, R3, LSL #1		;10:4 double height pixels eg MODE 15 640x256
   MOVHI   R6, R6, LSL #1
   MUL     R4, R7, R3			;R4=GPU_width*VIDC20_height
   MUL     R14, R8, R2			;R14=GPU_height*VIDC20_width
   CMP     R4, R14			;which edge should we scale?
   MOVHI   R10, R8, LSL #16		;width
   MOVHI   R4, R3
   MOVLS   R10, R7, LSL #16		;height
   MOVLS   R4, R2
   BL      blit_DIV_R10_R4_R11		;R10=(GPU_width/height<<16)/VIDC20_width

   MOV     R14, #1<<15			;round up
   LDR     R9, [R12, #overlay_clip+8 - VARS]	;clip left
   ADD     R2, R2, R9, LSL #1
   MLA     R2, R10, R2, R14		;clip_right =GPU_width*R10
   LDR     R9, [R12, #overlay_clip+12 - VARS]	;clip top
   ADD     R3, R3, R9, LSL #1
   MLA     R3, R10, R3, R14		;clip_bottom=GPU_height*R10
   MLA     R5, R10, R5, R14		;width      =VIDC20_width*R10
   MLA     R6, R10, R6, R14		;height     =VIDC20_height*R10
   MLA     R0, R10, R0, R14		;top border
   MOV     R2, R2, ASR #16
   MOV     R3, R3, ASR #16
   MOV     R5, R5, ASR #16
   MOV     R6, R6, ASR #16
   MOV     R0, R0, ASR #16

   SUB     R7, R7, R2
   MOV     R7, R7, ASR #1		;centre X
   SUB     R8, R8, R3
   MOV     R8, R8, ASR #1		;centre Y
   ADD     R8, R8, R0			;add top border if borders are being displayed
   STR     R7, [R12, #overlay_pos + 4 - VARS]
   STR     R8, [R12, #overlay_pos + 8 - VARS]

   ADD     R4, R12, #overlay_transform + 8 - VARS
   LDMIA   R4, {R9,R10, R11,R14}
   ORR     R7, R6, R5, LSL #16		;R7=aspect ratio
   TEQ     R9, R2			;right clip
   TEQEQ   R10, R3			;bottom clip
   TEQEQ   R11, R5			;width
   TEQEQ   R14, R6			;height
   STMNEIA R4, {R2,R3, R5,R6, R7}	;only update if it changed
 LDMFD  R13!, {R8-R11, R12, PC}

._shutdown_blitter
 MVN     R1, #0				;hand back to the hardware driver
 STR     R1, [R12, #closest_screen_mode - VARS]
 STR     R1, [R12, #blit_in_progress - VARS]		;disable blitter
 MOV     R0, #0
 ADR     R1, debug_mode_28
 SWI     XOS_ScreenMode			;MODE 28 C16M
 SWI     XOS_WriteS
 DCB     "Overlay creation failed, blitter temporarily shutdown",0
 ALIGN
LDMFD   R13!, {R12, PC}
 #endif


;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
; R10 = rt_vflybk_time
; R12 = pointer to VARS

;Exit:
; 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

 #if compile_GPU == 1 AND JIT_debug == 1 AND use_overlay == 1
   LDR     R4, debug_overlay_id
   CMN     R4, #1
   BLNE    report_JIT			;display JIT information
   CMN     R4, #1
   BLNE    display_debug_overlay
 #endif

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

 #if use_overlay == 1 AND overlay_at_vsync == 0 AND overlay_via_callback == 0
   BL      update_overlay
 #endif
 #if use_overlay == 1 AND overlay_at_vsync == 0 AND overlay_via_callback == 1
   STMFD   R13!, {R0-R1}
   MOV     R1, R12
   ADR     R0, update_overlay
   SWI     XOS_AddCallBack
   LDMFD   R13!, {R0-R1}
 #endif
 STRB    R0, [R12, #vsync_triggered - VARS]	;R0<>0

 #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
 LDRB    R4, [R12, #vsync_unthreaded - VARS]
 TEQ     R4, #0				;do we need to raise VSync unthreaded?
 MOVEQ   PC, R8				;NO

 #if compile_GPU == 1 AND use_overlay == 1
   LDRB    R14, [R12, #current_mode_bitdepth - VARS]
   CMP     R14, #3			;>8bpp
   BHI     _dont_blit
 #endif
 BL      blit_DA2_frame
 #if compile_GPU == 1 AND use_overlay == 1
   ._dont_blit
 #endif
 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  GraphicsV 1
 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



;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
 STR     R0, graphicsV_pixel_formats

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

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

 MOV     R4, #17			;get pixel formats
 ORR     R4, R4, R8
 SWI     XOS_CallAVector		;PRM1-70  GraphicsV 17
 TEQ     R4, #0				;does the driver support 17
 BNE     _no_gv17_support		;NO, skip copying formats

 MOV     R8, R0				;pointer to format list
 MOV     R6, R1				;number of formats
 ADR     R7, our_pixel_formats_list
 ._L3
   LDR     R0, [R7], #12
   ADD     R6, R6, #1
   CMN     R0, #1			;last entry?
 BNE     _L3
 STR     R6, graphicsV_pixel_formats + 4

 MOV     R0, #Module_Claim
 MOV     R3, R6, LSL #2
 ADD     R3, R3, R3, LSL #1		;*12
 SWI     XOS_Module			;claim some RMA space
 BVS     _no_gv17_support

 ADR     R7, our_pixel_formats_list
 STR     R2, graphicsV_pixel_formats
 ._L1
   LDMIA   R8!, {R0, R4, R5}
   STMIA   R2!, {R0, R4, R5}
   SUBS    R1, R1, #1
 BNE     _L1

 ._L2
   LDMIA   R7!, {R0, R4, R5}
   STMIA   R2!, {R0, R4, R5}
   CMN     R0, #1			;last entry?
 BNE     _L2

 ._no_gv17_support
 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, #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-R4, R9-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-R4, R9-R12, PC}	;NO, exit

 #if use_overlay == 1
   BL      destroy_overlay
 #endif
 #if use_overlay == 1 AND JIT_debug == 1
   BL      destroy_debug_overlay
 #endif

 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]

 LDR     R1, current_display_driver

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

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

 MOV     R0, #Module_Free
 LDR     R2, graphicsV_pixel_formats
 ADR     R3, our_pixel_formats_list
 TEQ     R2, R3				;did we claim RMA space?
 SWINE   XOS_Module			;YES

 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-R4, R9-R12, PC}


#if use_overlay == 1
; R12 - pointer to VARS
; Exit: EQ if destroy succeeded

.destroy_overlay
 STMFD   R13!, {R4, R9, R14}
 LDR     R4, [R12, #overlay_id - VARS]
 CMN     R4, #1
 LDMEQFD R13!, {R4, R9, PC}

 MOV     R14, #0			;clear bank addresses
 ADR     R9, logical_VDU_start
 STR     R14, [R9, #0]
 #if use_overlay == 1 AND screen_banks > 1
   STR     R14, [R9, #4]
 #endif
 #if use_overlay == 1 AND screen_banks > 2
   STR     R14, [R9, #8]
 #endif

 MOV     R9, #Vector_GraphicsV
 ORR     R4, R4, #21			;destroy overlay
 SWI     XOS_CallAVector		;PRM1-70  GraphicsV 21
 TEQ     R4, #0
 MVNNE   R14, #0			;mark overlay as destroyed
 STRNE   R14, [R12, #overlay_id - VARS]
LDMFD   R13!, {R4, R9, PC}



;Entry:
; R0 - bank number
; R8 - overlay_id (ie <driver><overlay><15..0 unset>)
;
;Exit:
; R0 - corrupt
; R3 - logical address
; R4 - stride (overlay width in bytes)
; EQ - map succeeded

.map_overlay_bank
 STMFD   R13!, {R14}
 ORR     R4, R8, #23			;map bank
 SWI     XOS_CallAVector		;PRM1-70  GraphicsV 23
 TEQ     R4, #0				;was the framebuffer mapped?
 LDMEQIA R0, {R3, R4}			;YES
 MOVNE   R3, #0				;failed
LDMFD   R13!, {PC}



;Entry:
; R12 - pointer to VARS

.update_overlay
 STMFD   R13!, {R0-R9, R14}

 STR     R13, [R12, #blit_in_progress - VARS]

 LDR     R1, HAL_vidc_list_dirty	;YES
 TEQ     R1, #0				;has the geometry changed?
 BLNE    blit_update_VIDC		;YES, update it

 LDR     R8, [R12, #overlay_id - VARS]
 CMN     R8, #1
 BEQ     _exit				;exit if there's no overlay

 ADD     R4, R12, #overlay_transform - VARS
 MOV     R9, #Vector_GraphicsV
 MOV     R14, #0
 LDMIA   R4!, {R0-R3, R5-R7}		;R0/R1 always =0
 TEQ     R7, #0				;has it changed?
 TEQNE   R2, #0				;YES, are they valid parameters?
 STRNE   R14, [R4, #-4]			;R7=0 next time around
 ORRNE   R4, R8, #27			;transform overlay
 SWINE   XOS_CallAVector		;PRM1-70  GraphicsV 27

 ADD     R0, R12, #overlay_pos - VARS
 MOV     R1, #1				;one coordinate in the array
 LDR     R2, [R12, #current_overlay_bank - VARS]
 ORR     R4, R8, #22			;display overlay
 SWI     XOS_CallAVector		;PRM1-70  GraphicsV 22

;*************** Temporary workaround for banks not being mapped in ***************
 MOV     R6, #0
 ADR     R7, logical_VDU_start		; R7=framebuffer addresses
 LDR     R1, [R7, #0]			; 
 TEQ     R1, #0				; 
 MOVEQ   R0, #0				; framebuffer 1
 BLEQ    map_overlay_bank		; EQ if successful, R3=address, R4=stride
 STREQ   R3, [R7, #0]			; logical address
 MOVEQ   R6, R4				; 
 #if use_overlay == 1 AND screen_banks > 1
   LDR     R1, [R7, #4]			; 
   TEQ     R1, #0			; 
   MOVEQ   R0, #1			; framebuffer 2
   BLEQ    map_overlay_bank		; 
   STREQ   R3, [R7, #4]			; logical address
   MOVEQ   R6, R4			; 
 #endif					; 
 #if use_overlay == 1 AND screen_banks > 2
   LDR     R1, [R7, #8]			; 
   TEQ     R1, #0			; 
   MOVEQ   R0, #2			; framebuffer 3
   BLEQ    map_overlay_bank		; 
   STREQ   R3, [R7, #8]			; 
   MOVEQ   R6, R4			;  R6=stride
 #endif
 TEQ     R6, #0
 MOVNE   R6, R6, LSR #2			;assume stride is in bytes
 STRNE   R6, local_VIDC3+VIDC_Type3.hdisp
;****************

 ._exit
 MOV     R0, #0
 STR     R0, [R12, #blit_in_progress - VARS]
LDMFD   R13!, {R0-R9, PC}



.blit_DIV_R10_R4_R11
 U_DIV   R10, R4, R11
 MOV     PC, R14
#endif
