ALIGN   32, 4
;blit_DA2_frame
;--------------
;blits the frame in DA2 to the GPU

.blit_DA2_frame
 STR     R14, [R13, #-4]!
 LDR     R14, blit_in_progress
 TEQ     R14, #0			;are we being re-entered?
 LDRNE   PC, [R13], #4			;YES, exit
 STR     R13, blit_in_progress		;NO, prevent re-entry
 STMFD   R13!, {R0-R12}

 #if power_save_processor == 1
   MOV     R0, #0
   MOV     R1, #0
   SWI     XPortable_Speed		;force CPU to full speed
   STR     R0, [R13, #-4]!		;and stack old state
 #endif

 LDR     R1, HAL_vidc_list_dirty	;YES
 TEQ     R1, #0				;has the geometry changed?
 BLNE    blit_update_VIDC		;YES, update it
					;***********************************
					;work out VIDC horizontal borders
 ADR     R8, vidc20_regs - (VIDC20_HCR << 2)	;R8=VIDC20 registers
 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
 STR     R11, [R13, #-8]!		;stack total border for blitter

 #if compile_GPU == 1 AND JIT_debug == 1
   MOV     R1, R6, LSL #24		;<LB>
   ORR     R1, R1, R5, LSL #16		;<LB><RB>
   LDR     R5, [R8, #VIDC20_VDSR << 2]
   LDR     R0, [R8, #VIDC20_VBSR << 2]
   SUBS    R0, R0, R5
   ANDPL   R0, R0, #&FF
   ORRPL   R1, R1, R0, LSL #8		;<LB><RB><TB>
   LDR     R0, [R8, #VIDC20_VDER << 2]
   LDR     R5, [R8, #VIDC20_VBER << 2]
   SUBS    R0, R0, R5
   ANDPL   R0, R0, #&FF
   ORRPL   R1, R1, R0			;<LB><RB><TB><BB>
   TEMP    R5
   STR     R1, screen_borders		;<left><right><top><bottom>
   LOCK    R5
 #endif
					;***********************************
 LDR     R9, current_GPU_bank		;R9=current GPU bank
 ADR     R1, logical_VDU_start		;R1=screen buffer addresses
 LDR     R0, MEMC_Vinit			;R0=DA2 framestore
 LDR     R1, [R1, R9, LSL #2]		;R1=GPU framestore

 LDR     R9, [R8, #VIDC20_VDSR << 2]
 LDR     R12, [R8, #VIDC20_VBSR << 2]
 SUBS    R9, R12, R9			;R9=top border if +ve
 LDR     R5, current_mode_bitdepth	;R5=bpp
 LDR     R7, MEMC_Vstart
 RSB     R12, R5, #3			;R12=shift to convert bpp to bytes
 LDR     R2, VIDC20_width		;R2=screen width
 LDR     R3, VIDC20_height		;R3=screen height
 ADD     R7, R7, R6, LSR R12		;correct Vstart by left border
 ADD     R0, R0, R6, LSR R12		;add left border to DA2
 STR     R7, [R13, #4]			;stack corrected Vstart for blitter

 LDR     R4, HAL_vidc_list + VIDC_Type3.vdisp	;R4=GPU height
 LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp	;R7=GPU width
 MOVLE   R9, R1				;no top border, R9=GPU screen start
					;************ TOP BORDER *************
 SUBGT   R3, R3, R9			;reduce height by top border overlap
 MULGT   R10, R9, R2			;R10=top border * DA2 screen width
 ADDGT   R0, R0, R10, LSR R12		;add on byte offset to DA2
 MULGT   R10, R9, R7			;R10=top border * GPU screen width
 ADDGT   R9, R1, R10, LSL #2		;R9=GPU screen start
					;********* CENTRE VERTICALLY **********
 LDR     R6, [R8, #VIDC20_VDER << 2]	;R6=VDER
 LDR     R12, [R8, #VIDC20_VBER << 2]	;R12=VBER
 SUBS    R10, R6, R12			;R10=bottom border overlap if +ve
 SUBGT   R3, R3, R10			;reduce screen height by bottom border
					;*********** ASPECT RATIO *************
 LDR     R10, VIDC20_aspect_ratio
 CMP     R10, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
 ADDLO   R5, R5, #8			;LO for MODE 13, R5=blit routine
 ADDHI   R5, R5, #4			;HI for MODE 15
 MOVHI   R3, R3, LSL #1			; |+ double height
					;**************************************
 SUB     R4, R4, R3			;delta between GPU and VIDC20 height
 MOV     R4, R4, LSR #1			;/2 to centre
 BIC     R4, R4, #1
 MUL     R4, R7, R4			;R4=vertical centre

					;************ LEFT BORDER *************
 SUB     R2, R2, R11			;reduce width by border overlap
 MOVEQ   R2, R2, LSL #1			;EQ for MODE 48, double width
 SUBS    R10, R7, R2			;delta between GPU and VIDC20 width
 MOVMI   R10, #0			;border went negative
 STR     R10, [R13, #-4]!		;stack GPU border for blitter
					;******** CENTRE HORIZONTALLY *********
 ADDS    R4, R4, R10, LSR #1		;/2 to centre and add to top border
 MOV     R10, #0
 MOV     R11, #0
					;******* BLANK TOP/LEFT BORDER ********
 ._start_border_L1
   STRGTD  R10, R11, [R1], #8		;write two pixels
   SUBS    R4, R4, #2
 BGT     _start_border_L1
					;**************************************
 LDR     R10, MEMC_Vend			;R10=end of screen

 STMFD   R13!, {R1-R6, R12}
 BL      IOC_trigger_vsync		;raise IOC IRQ for vsync
 LDMFD   R13!, {R1-R6, R12}

 ADD     R10, R10, #16			;correct for DMA size
 CMP     R0, R10			;have we hit Vend?
 MVNHS   R10, #0			;YES, ignore Vend from now on
 LDRHS   R0, [R13, #8]			; |+  use corrected Vstart from stack
 ADR     R12, GPU_palette		;R12=palette lookup table
 CMP     R2, #8				;check screen is at least 8 pixels wide
 CMPHI   R3, #8				;..and 8 pixels high
 BLT     blit_exit			;NO, exit

 STMFD   R13!, {R0-R1}			;validate screen address
 MUL     R4, R2, R3
 AND     R6, R5, #%11
 RSB     R6, R6, #3
 ADD     R1, R0, R4, LSR R6
 SWI     XOS_ValidateAddress
 LDMFD   R13!, {R0-R1}
 BCS     blit_exit
					;************** POINTER ***************
 STR     R1, GPU_screen_start
					;**************************************
 PLD     [R12, #0]			;pre-load palette: 0-7

 ADD     PC, PC, R5, LSL #2
 NOP
 B       blit_1bpp_DW			;double width pixels
 B       blit_2bpp_DW
 B       blit_4bpp_DW
 B       blit_8bpp_DW
 B       blit_1bpp_DH			;double height pixels
 B       blit_2bpp_DH
 B       blit_4bpp_DH
 B       blit_8bpp_DH
 B       blit_1bpp			;square pixels
 B       blit_2bpp
 B       blit_4bpp

.blit_8bpp
 PLD     [R12, #32]			;pre-load palette: 8-15
 PLD     [R12, #64]			;pre-load palette: 16-31
 PLD     [R12, #96]			;pre-load palette: 32-63
 DA2_blitter 8, 0, 0			;8bpp, 1 pixel per byte, square

.blit_8bpp_DW
 PLD     [R12, #32]			;pre-load palette: 8-15
 PLD     [R12, #64]			;pre-load palette: 16-31
 PLD     [R12, #96]			;pre-load palette: 32-63
 DA2_blitter 8, 0, 1			;8bpp, 1 pixel per byte, double width

.blit_8bpp_DH
 PLD     [R12, #32]			;pre-load palette: 8-15
 PLD     [R12, #64]			;pre-load palette: 16-31
 PLD     [R12, #96]			;pre-load palette: 32-63
 DA2_blitter 8, 0, 2			;8bpp, 1 pixel per byte, double height

.blit_4bpp
 PLD     [R12, #32]			;pre-load palette: 8-15
 DA2_blitter 4, 1, 0			;4bpp, 2 pixels per byte, square

.blit_4bpp_DW
 PLD     [R12, #32]			;pre-load palette: 8-15
 DA2_blitter 4, 1, 1			;4bpp, 2 pixel per byte, double width

.blit_4bpp_DH
 PLD     [R12, #32]			;pre-load palette: 8-15
 DA2_blitter 4, 1, 2			;4bpp, 2 pixel per byte, double height

.blit_2bpp
 DA2_blitter 2, 2, 0			;2bpp, 4 pixels per byte, square

.blit_2bpp_DW
 DA2_blitter 2, 2, 1			;2bpp, 4 pixels per byte, double width

.blit_2bpp_DH
 DA2_blitter 2, 2, 2			;2bpp, 4 pixels per byte, double height

.blit_1bpp
 DA2_blitter 1, 3, 0			;1bpp, 8 pixels per byte, square

.blit_1bpp_DW
 DA2_blitter 1, 3, 1			;1bpp, 8 pixels per byte, double width

.blit_1bpp_DH
 DA2_blitter 1, 3, 2			;1bpp, 8 pixels per byte, double height

.blit_exit
 ADD     R13, R13, #12			;drop borders from stack

					;******** BLANK BOTTOM BORDER *********
 LDR     R9, current_GPU_bank		;R9=current GPU bank
 ADR     R4, logical_VDU_start + 4
 LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp	;R7=GPU width
 LDR     R4, [R4, R9, LSL #2]		;R4=screen buffer end address
 SUB     R4, R4, R7			;reduce by overrun raster
 CMP     R4, R1				;did we hit the screen end?
 BLS     _no_bottom_border		;YES, skip if no border

 MOV     R5, #0
 MOV     R6, #0
 MOV     R7, #0
 MOV     R8, #0
 MOV     R9, #0
 MOV     R10, #0
 MOV     R11, #0
 MOV     R12, #0
 ._bottom_border_L1
   STMIA   R1!, {R5-R12}		;blank 8 pixels
   STMIA   R1!, {R5-R12}		;blank 8 pixels
   CMP     R1, R4
 BLO     _bottom_border_L1

 ._no_bottom_border			;**************************************
 LDR     R0, Cursor_state
 TEQ     R0, #0				;is the cursor on?
 BLNE    blit_pointer			;YES

 #if compile_GPU == 1 AND JIT_debug == 1
   BL      report_JIT			;display JIT information
   .report_JIT_blitter_return
 #endif

 #if MMUControl2_support == 1 AND compile_GPU == 1
   LDR     R2, MMUControl_ARMop21
   LDR     R10, current_GPU_bank	;R9=current GPU bank
   ADR     R3, logical_VDU_start	;R1=screen buffer addresses
   ADD     R9, R10, #1
   LDR     R0, [R3, R10, LSL #2]	;R0=GPU framestore start
   LDR     R1, [R3, R9, LSL #2]		;R1=GPU framestore end
   MOV     R14, PC
   MOV     PC, R2			;flush CPU L2 cache
 #endif

 #if MMUControl2_support == 0 AND compile_GPU == 1
   LDR     R10, current_GPU_bank	;switch banks
 #endif
 ADR     R1, hwd_framestore		;force to GPU framestore start
 LDR     R1, [R1, R10, LSL #2]		;get the current screen bank
 SUBS    R10, R10, #1			;go to next buffer
 MOVMI   R10, #2			;three buffers 0..1..2

 MOV     R0, #0				;current video display
 LDR     R4, current_display_driver
 MOV     R4, R4, LSL #24
 ORR     R4, R4, #6			;GraphicsV 6 - set DAG
 MOV     R9, #Vector_GraphicsV
 TEMP    R11
 #if ((JIT_debug == 1 AND JIT_display_actual_PC == 0) OR JIT_debug == 0) AND compile_GPU == 1
   STR     R10, current_GPU_bank
 #endif
 SWI     XOS_CallAVector		;PRM1-70

 BL      IOC_VFLYBK_high		;IR pin goes high during flyback
 #if power_save_processor == 1
   LDR     R0, [R13], #4		;get old state from stack
   MOV     R1, #0
   SWI     XPortable_Speed		;set CPU speed back
;   MCR     P15, 0, R0, C7, C0, 4	;wait for interrupt pre ARMv7
;   WFI					;wait for interrupt ARMv7
;   SWI     XPortable_Idle		;generic WFI
 #endif

 MOV     R0, #0
 STR     R0, blit_in_progress		;allow updates
 LOCK    R11
LDMFD   R13!, {R0-R12, PC}




;blit_pointer
;-----------
;blits the cursor over the current frame

.blit_pointer
 STMFD   R13!, {R14}
 BL      check_JIT_and_taskID		;is the task supported?
 LDRNE   R0, MEMC_CinitOS		;NO, fallback to OS pointer
 BNE     _trusted_address		; |+ and trust the address is okay

 LDR     R0, MEMC_Cinit			;YES, use the MEMC address
 ADD     R1, R0, #8			;1 row of the pointer
 SWI     XOS_ValidateAddress		;is cursor address valid?
 LDRCS   R0, MEMC_CinitOS		;NO, fallback to OS pointer

 ._trusted_address			;R0=pointer sprite (2bpp 32xY pixels)
 PLD     [R0, #0]			;pre-load cursor
 LDR     R1, GPU_screen_start		;R1=start address of raster 0
 ADR     R8, vidc20_regs - (VIDC20_HCR << 2)
 LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp	;R7=GPU width
 LDR     R9, current_GPU_bank		;R9=current GPU bank
 MOV     R7, R7, LSL #2			;R7=GPU width in bytes
 ADR     R2, logical_VDU_start + 4
 LDR     R2, [R2, R9, LSL #2]		;R2=end address of screen
 SUB     R2, R2, R7			;reduce by overrun raster

 LDR     R10, VIDC20_width		;R10=screen width
 LDR     R11, [R8, #VIDC20_HCSR << 2]	;R11=pointer X pos
 LDR     R4, [R8, #VIDC20_VCSR << 2]	;R4=pointer Y pos

 LDR     R5, [R8, #VIDC20_HDSR << 2]
 LDR     R6, [R8, #VIDC20_HBSR << 2]
 CMP     R5, R6				;is HDSR<HBSR?
 SUBLO   R11, R11, R6			;YES, correct X using HBSR
 SUBHS   R11, R11, R5			;NO, correct X using HDSR
 LDR     R5, [R8, #VIDC20_VDSR << 2]
 LDR     R6, [R8, #VIDC20_VBSR << 2]
 CMP     R5, R6				;is VDSR<VBSR?
 SUBLO   R4, R4, R6			;YES, correct Y using VBSR
 SUBHS   R4, R4, R5			;NO, correct Y using VDSR

 LDR     R12, VIDC20_aspect_ratio
 LDR     R5, [R8, #VIDC20_VCER << 2]
 LDR     R6, [R8, #VIDC20_VCSR << 2]
 CMP     R12, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
 MOVHI   R7, R7, LSL #1
					;************** CLIP TOP **************
 CMP     R4, #0				;off the top of the screen?
 RSBMI   R4, R4, #1			;YES,
 ADDMI   R0, R0, R4, LSL #3		; |   skip pointer rows
 SUBMI   R5, R5, R4			; |   reduce cursor height
 MOVMI   R4, #0				; |+  and start at raster 0
 MULGT   R4, R7, R4			;R4=GPU width * Y pos
 ADDGT   R1, R1, R4			;R1=start raster address of pointer
					;**************************************
 SUBS    R5, R5, R6			;cursor height
 LDMLEFD R13!, {PC}			;exit early if no cursor

 MUL     R6, R5, R7
 ADD     R6, R1, R6			;end of cursor in GPU
					;************* CLIP BOTTOM ************
 CMP     R6, R2				;is the end before the screen end?
 MOVLO   R2, R6				;YES, end at VCER instead
					;************ ASPECT RATIO ************
 CMP     R12, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
 MOVHI   R7, R7, LSR #1

 ADR     R12, Cursor_palette
 SUB     R1, R1, R7			;go back one raster to speed up loop
 PLD     [R12, #0]			;pre-load palette: 0-3
					;************* CLIP RIGHT *************
 ADD     R4, R11, #32			;right cursor pixel
 MVN     R8, #0				;R8=mask for pixels 0-15
 MVN     R9, #0				;R9=mask for pixels 16-31
 SUBS    R4, R4, R10			;does the cursor overhang right?
 MOVGT   R4, R4, LSL #1			;YES, *2 for 2bpp
 MOVGT   R9, R9, LSR R4			; |   update 16-31 mask
 SUBGTS  R4, R4, #16 * 2		; |   R4=pixel overhang on 00-15
 MOVGT   R8, R8, LSR R4			; |+  update 0-15 mask
					;**************************************
 ._yloop
   ADD     R1, R1, R7			;go to next row
   CMP     R1, R2			;past cursor or screen end?
   LDMHSFD R13!, {PC}			;YES, exit

   LDMIA   R0!, {R5-R6}			;get one row
   AND     R5, R5, R8			;mask pixels 00-15
   AND     R6, R6, R9			;mask pixels 16-31
					;************** CLIP LEFT *************
   MOVS    R10, R11			;R10=start X pos, PL if no left clip
   BPL     _no_left_clip		;NO left clip
   RSB     R3, R11, #1			;R3=pixels to clip
   CMP     R3, #16			;more than 16?
   MOVHS   R5, R6			;YES
   MOVHS   R6, #0			; |   drop 1st word
   SUBHS   R3, R3, #16			; |+  and reduce shift
   MOV     R3, R3, LSL #1		;*2 for 2bpp
   RSB     R4, R3, #32			;create reverse shift
   MOV     R5, R5, LSR R3		;drop left pixels
   ORR     R5, R5, R6, LSL R4		;add in pixels from 16-31
   MOV     R6, R6, LSR R3		;drop pixels

   MOV     R10, #0			;start at X=0
   ._no_left_clip
   LDR     R4, VIDC20_aspect_ratio
   CMP     R4, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
   BEQ     _double_width
   BHI     _double_height
					;**************************************
   ORRS    R3, R5, R6			;blank row?
   BEQ     _yloop			;YES, skip row

   ._xloop
     ANDS    R3, R5, #%11		;R3=colour index, EQ set if transparent
     LDRNE   R4, [R12, R3, LSL #2]	;get palette entry
     MOV     R5, R5, LSR #2		;shift out pixel
     STRNE   R4, [R1, R10, LSL #2]	;store non-transparent pixel to screen
     ORRS    R5, R5, R6, LSL #32 - 2	;shift in pixel from next word
     MOV     R6, R6, LSR #2		;shift out pixel from next word
     TEQEQ   R6, #0			;last pixel?
     ADD     R10, R10, #1		;go to next pixel
   BNE     _xloop			;EQ if no pixels left
 B      _yloop


 ._double_height
   ORRS    R3, R5, R6			;blank row?
   BEQ     _skip_DH			;YES, skip row
   ADD     R7, R7, R1			;R7=2nd raster
   ._xloop_DH
     ANDS    R3, R5, #%11		;R3=colour index, EQ set if transparent
     LDRNE   R4, [R12, R3, LSL #2]	;get palette entry
     MOV     R5, R5, LSR #2		;shift out pixel
     STRNE   R4, [R1, R10, LSL #2]	;store non-transparent pixel to screen
     STRNE   R4, [R7, R10, LSL #2]	;store non-transparent pixel to screen
     ORRS    R5, R5, R6, LSL #32 - 2	;shift in pixel from next word
     MOV     R6, R6, LSR #2		;shift out pixel from next word
     TEQEQ   R6, #0			;last pixel?
     ADD     R10, R10, #1		;go to next pixel
   BNE     _xloop_DH			;EQ if no pixels left
   SUB     R7, R7, R1
   ._skip_DH
   ADD     R1, R1, R7			;go to next raster
 B      _yloop


 ._double_width
   MOV     R10, R10, LSL #1
   ORRS    R3, R5, R6			;blank row?
   BEQ     _yloop			;YES, skip row

  ._xloop_DW
     ANDS    R3, R5, #%11		;R3=colour index, EQ set if transparent
     LDRNE   R4, [R12, R3, LSL #2]	;get palette entry
     MOV     R5, R5, LSR #2		;shift out pixel
     STRNE   R4, [R1, R10, LSL #2]	;store non-transparent pixel to screen
     ADD     R10, R10, #1		;go to next pixel
     STRNE   R4, [R1, R10, LSL #2]	;store non-transparent pixel to screen
     ORRS    R5, R5, R6, LSL #32 - 2	;shift in pixel from next word
     MOV     R6, R6, LSR #2		;shift out pixel from next word
     TEQEQ   R6, #0			;last pixel?
     ADD     R10, R10, #1		;go to next pixel
   BNE     _xloop_DW			;EQ if no pixels left
 B      _yloop
