STRUCT blitter_stack
{
  .GPU_border	DCD 0
  .DA2_border	DCD 0
  .Vstart	DCD 0
  .size
}


ALIGN   32, 4
;blit_DA2_frame
;--------------
;blits the frame in DA2 to the GPU
;
;Entry:
; R12 - pointer to VARS

.blit_DA2_frame
 STR     R14, [R13, #-4]!
 LDR     R14, [R12, #blit_in_progress - VARS]
 TEQ     R14, #0			;are we being re-entered?
 LDRNE   PC, [R13], #4			;YES, exit
 STR     R13, [R12, #blit_in_progress - VARS]		;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

 #if use_overlay == 0
   LDR     R1, HAL_vidc_list_dirty	;YES
   TEQ     R1, #0			;has the geometry changed?
   BLNE    blit_update_VIDC		;YES, update it
 #endif
 MRS     R0, CPSR			;enable IRQ
 STR     R0, [R13, #-4]!		;and stack old state
 BIC     R0, R0, #%10 << 6
 MSR     CPSR_c, R0
 
 BL      calc_borders
 #if use_overlay == 1
   TEQ     R1, #0
   BEQ     blit_exit_skip_pointer	;exit if there's no GPU buffer
 #endif

 #if use_overlay == 0
   ADD     R1, R1, R4, LSL #2		;R1=top left of GPU framebuffer
 #endif
 LDR     R10, [R12, #MEMC_Vend - VARS]	;R10=end of screen

 STMFD   R13!, {R1-R5}			;raise IOC IRQ for vsync
 BL      IOC_trigger_vsync		;this must occur after the screen
 LDMFD   R13!, {R1-R5}			;geometry has been calculated

 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, #blitter_stack.Vstart]	; |+  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_skip_pointer		;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_skip_pointer
					;************** POINTER ***************
 TEMP    R11
 STR     R1, GPU_screen_start
 LOCK    R11
					;**************************************
 PLD     [R12, #0]			;pre-load palette: 0-7

 ADD     PC, PC, R5, LSL #2
 NOP
#if use_overlay == 0
 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
#endif
 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_4bpp
 PLD     [R12, #32]			;pre-load palette: 8-15
 DA2_blitter 4, 1, 0			;4bpp, 2 pixels per byte, square

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

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

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

.blit_exit
 ADR     R12, VARS
 LDR     R0, [R12, #Cursor_state - VARS]
 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

.blit_exit_skip_pointer
 ADR     R12, VARS
 ADD     R13, R13, #blitter_stack.size	;drop borders from stack
 LDR     R0, [R13], #4			;get old IRQ state from stack
 MSR     CPSR_c, R0
 TEMP    R11
 LDR     R4, current_display_driver
 MOV     R4, R4, LSL #24
 MOV     R9, #Vector_GraphicsV
 LDR     R10, current_GPU_bank		;R10=current GPU bank
 #if use_overlay == 0
   LDR     R2, MMUControl_ARMop21
   ADR     R3, logical_VDU_start
   LDR     R0, [R3, R10, LSL #2]	;R0=GPU framestore start
   LDR     R1, [R3, #12]
   ADD     R1, R0, R1, LSL #2		;R1=GPU framestore end
   MOV     R14, PC
   MOV     PC, R2			;flush CPU L2 cache
  
   MOV     R0, #0			;current video display
   ADD     R1, R12, #hwd_framestore - VARS	;force to GPU framestore start
   LDR     R1, [R1, R10, LSL #2]	;get the current screen bank
   ORR     R4, R4, #6			;GraphicsV 6 - set DAG
   SWI     XOS_CallAVector		;PRM1-70  GraphicsV 6
 #endif
 #if use_overlay == 1 AND overlay_at_vsync == 0
   STR     R10, [R12, #current_overlay_bank - VARS]
 #endif
 SUBS    R10, R10, #1			;go to next buffer
 MOVMI   R10, #screen_banks-1		;wrapped
 STR     R10, current_GPU_bank
 #if use_overlay == 1 AND overlay_at_vsync == 1 AND overlay_via_callback == 0
   BL      update_overlay
 #endif
 #if use_overlay == 1 AND overlay_at_vsync == 1 AND overlay_via_callback == 1
   MOV     R1, R12
   ADR     R0, update_overlay
   SWI     XOS_AddCallBack
 #endif

 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, [R12, #blit_in_progress - VARS]		;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, [R12, #MEMC_CinitOS - VARS]	;NO, fallback to OS pointer
 BNE     _trusted_address		; |+ and trust the address is okay

 LDR     R0, [R12, #MEMC_Cinit - VARS]	;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
 ADD     R8, R12, #vidc20_regs - VARS - (VIDC20_HCR << 2)
 #if use_EDID == 1 OR use_overlay == 1
   LDR     R7, local_VIDC3 + VIDC_Type3.hdisp	;R7=GPU width
 #endif
 #if use_EDID == 0 AND use_overlay == 0
   LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp	;R7=GPU width
 #endif
 MOV     R7, R7, LSL #2			;R7=GPU width in bytes
 LDR     R2, HAL_vidc_list + VIDC_Type3.vdisp	;R2=visible height
 MLA     R2, R7, R2, R1			;R2=end of visible screen

 LDR     R10, [R12, #VIDC20_width - VARS]	;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     R5, [R8, #VIDC20_VCER << 2]
 LDR     R6, [R8, #VIDC20_VCSR << 2]
 #if use_overlay == 0
   LDR     R12, [R12, #VIDC20_aspect_ratio - VARS]
   CMP     R12, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
   MOVHI   R7, R7, LSL #1
 #endif
					;************** 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 ************
 #if use_overlay == 0
   CMP     R12, #1			;aspect ratio: 0=5:4 1=2:3 2=10:4
   MOVHI   R7, R7, LSR #1
 #endif

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


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


;calc_borders
;------------
;calculates the GPU and DA2 borders based on the VIDC20 and GPU VIDC parameters
;
;Entry:
; R12 - pointer to VARS
;
;exit:
; R0 - DA2 framebuffer start
; R1 - GPU framebuffer start
; R2 - VIDC20 screen width
; R3 - VIDC20 screen height
; R4 - pixels to wipe for TOP and LEFT border of first scanline
; R5 - blit routine to use (bits 0-3) / bpp (bits 0-1)
;        bits 2-3 = aspect ratio: 0=5:4 1=2:3 2=10:4
; (vicarious) R8 - VIDC20 registers
; (vicarious) R10 - GPU_border (left+right)
; R6,R7,R9,R11 - corrupt
; R12 - preserved
;
;stack:
; +0 = GPU_border (left+right)
; +4 = DA2_border (left+right)
; +8 = Vstart adjusted for VIDC20 borders

.calc_borders
 SUB     R13, R13, #blitter_stack.size
 STMFD   R13!, {R14}
					;***********************************
					;work out VIDC horizontal borders
 ADD     R8, R12, #vidc20_regs - VARS - (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, #4 + blitter_stack.DA2_border]	;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, [R12, #MEMC_Vinit - VARS]	;R0=DA2 framestore
 LDR     R1, [R1, R9, LSL #2]		;R1=GPU framestore

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

 #if use_EDID == 1 OR use_overlay == 1
   LDR     R4, local_VIDC3 + VIDC_Type3.vdisp	;R4=GPU height
   LDR     R7, local_VIDC3 + VIDC_Type3.hdisp	;R7=GPU width
 #endif
 #if use_EDID == 0 AND use_overlay == 0
   LDR     R4, HAL_vidc_list + VIDC_Type3.vdisp	;R4=GPU height
   LDR     R7, HAL_vidc_list + VIDC_Type3.hdisp	;R7=GPU width
 #endif
					;************ 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 R14		;add on byte offset to DA2

 LDR     R6, [R8, #VIDC20_VDER << 2]	;R6=VDER
 LDR     R10, [R8, #VIDC20_VBER << 2]	;R10=VBER
 SUBS    R10, R6, R10			;R10=bottom border overlap if +ve
 SUBGT   R3, R3, R10			;reduce screen height by bottom border
					;*********** ASPECT RATIO *************
 #if use_overlay == 0
   LDR     R10, [R12, #VIDC20_aspect_ratio - VARS]
   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 *************
 #endif
 SUB     R2, R2, R11			;reduce width by border overlap
 #if use_overlay == 0
   MOVEQ   R2, R2, LSL #1		;EQ for MODE 48, double width
 #endif
 SUBS    R10, R7, R2			;delta between GPU and VIDC20 width
 MOVMI   R10, #0			;border went negative
 #if use_overlay == 0
					;******** CENTRE HORIZONTALLY *********
   ADD     R4, R4, R10, LSR #1		;/2 to centre and add to top border
 #endif
					;**************************************
 STR     R10, [R13, #4 + blitter_stack.GPU_border]	;stack GPU border for blitter
LDMFD   R13!, {PC}
