; GnarlPlot sprite plotting assembler macros V0.10 27/12/03
; Copyright 2008 Jeffrey Lee
; This file is part of GnarlPlot.
; GnarlPlot is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
; GnarlPlot is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
; You should have received a copy of the GNU General Public License
; along with GnarlPlot.  If not, see <http://www.gnu.org/licenses/>.

	GET gp.s


;
;
;				Calculation stuff
;
;


	MACRO
	GP_SPR_CALCDRAW $x,$y,$spr,$spr_w,$spr_h,$spr_g,$spr_c,$scr,$scr_w,$scr_h,$scr_g,$scr_c,$tmp
	; Calculate the vars needed to draw a sprite on a screen
	; In:
	; $x,$y = Register coords of where to plot sprite
	; $spr = Register sprite pointer
	; $spr_w,$spr_h = Register size of sprite in pixels
	; $spr_g = Register sprite end-of-line gap in bytes
	; $spr_c = Constant sprite pixel size: 1 << spr_c = bytes per pixel
	; $scr = Register screen pointer
	; $scr_w,$scr_h = Register screen dimensions in pixels
	; $scr_g = Register screen end-of-line gap in bytes
	; $scr_c = Constant screen pixel size: 1 << scr_c = bytes per pixel
	; $tmp = temporary register
	; Out:
	; $spr,$scr = pointers to required data start
	; $spr_w,$spr_h = size of area to be drawn (pixels)
	; $spr_g,$scr_g = new end-of-line gaps (bytes)
	; $tmp mangled
	; Plan:
	; 1. Clip to bottom of screen
	; 2. Clip to top of screen
	; 3. Clip to right of screen
	; 4. Clip to left of screen

	; Step 1
	ADD $tmp,$y,$spr_h ; Work out bottom edge of sprite
	SUBS $tmp,$tmp,$scr_h ; Off bottom?
	SUBGT $spr_h,$spr_h,$tmp ; Decrease number of rows

	; Step 2
	CMP $y,#0 ; Off top edge?
	ADDLT $spr_h,$spr_h,$y ; Yes, so decrease number of rows
	ADDLT $tmp,$spr_g,$spr_w,LSL #$spr_c ; Work out sprite line length (Allows sprites to have gaps)
	MULLT $tmp,$y,$tmp ; Shift on sprite ptr
	SUBLT $spr,$spr,$tmp
	ADDGT $tmp,$scr_g,$scr_w,LSL #$scr_c ; Work out screen line length
	MLAGT $scr,$y,$tmp,$scr ; Shift on screen ptr if on screen

	; Step 3
	ADD $tmp,$x,$spr_w ; Work out right edge of sprite
	SUBS $tmp,$tmp,$scr_w ; Off right edge?
	ADDGT $spr_g,$spr_g,$tmp,LSL #$spr_c ; Yes, so increase sprite gap
	SUBGT $spr_w,$spr_w,$tmp ; And decrease row length
	SUBLT $scr_g,$scr_g,$tmp,LSL #$scr_c ; Increase screen gap

	; Step 4
	CMP $x,#0 ; Off left edge?
	SUBLT $spr_g,$spr_g,$x,LSL #$spr_c ; Yes, so increase sprite gap
	ADDLT $spr_w,$spr_w,$x ; And decrease row length
	SUBLT $spr,$spr,$x,LSL #$spr_c ; And shift on sprite ptr
	ADDGT $scr,$scr,$x,LSL #$scr_c ; Else shift on screen ptr
	ADDGT $scr_g,$scr_g,$x,LSL #$scr_c ; And increase screen gap
	MEND


	MACRO
	GP_SPR_CALCDRAW_PRESHIFTED_8BPP $x,$y,$spr,$spr_w,$spr_h,$spr_g,$scr,$scr_w,$scr_h,$scr_g,$tmp,$tmp2
	; Calculate the vars needed to draw an 8bpp sprite on an 8bpp screen, using preshifting
	; In:
	; $x,$y = Register coords of where to plot sprite
	; $spr = Register sprite set pointer: 4 contigous sprites representing the preshifted states. 1st sprite assumed to contain the blank word column on the right side.
	; $spr_w,$spr_h = Register size of sprite. spr_w must include the extra 4 pixels containing the preshift data.
	; $spr_g = Register sprite end-of-line gap
	; $scr = Register screen pointer
	; $scr_w,$scr_h = Register size of screen
	; $scr_g = Register screen end-of-line gap
	; $tmp,$tmp2 = temporary registers
	; Out:
	; $spr,$scr = pointers to required data start
	; $spr_w,$spr_h = size of area to be drawn (pixels)
	; $spr_g,$scr_g = new end-of-line gaps (bytes)
	; $x,$y = start/end bit masks for the first/last words: scr = (scr & mask) | (spr & ~mask)
	; $tmp,$tmp2 mangled
	; Plan:
	; 1. Advance to correct preshift number
	; 2. Clip to bottom of screen
	; 3. Clip to top of screen
	; 4. Clip to right of screen
	; 5. Clip to left of screen
	; 6. Decode the bit masks from the data held in tmp2

	; 1. Advance to correct preshift number
	ANDS $tmp2,$x,#3
	MOVEQ $tmp,#0 ; Skip the next few instructions if =0
	ADDNE $tmp,$spr_w,$spr_g ; Calc line length
	MULNE $tmp,$spr_h,$tmp ; Calc sprite size
	CMPNE $tmp2,#2 ; Either 1, 2 or 3 if here
	ADDNE $spr,$spr,$tmp
	ADDGE $spr,$spr,$tmp,LSL #1
	BIC $x,$x,#3

	; 2. Clip to bottom of screen
	ADD $tmp,$y,$spr_h ; Work out bottom edge of sprite
	SUBS $tmp,$tmp,$scr_h ; Off bottom?
	SUBGT $spr_h,$spr_h,$tmp ; Decrease number of rows

	; 3. Clip to top of screen
	CMP $y,#0 ; Off top edge?
	ADDLT $spr_h,$spr_h,$y ; Yes, so decrease number of rows
	ADDLT $tmp,$spr_g,$spr_w ; Work out sprite line length
	MULLT $tmp,$y,$tmp ; Shift on sprite ptr
	SUBLT $spr,$spr,$tmp
	ADDGT $tmp,$scr_g,$scr_w ; Work out screen line length
	MLAGT $scr,$y,$tmp,$scr ; Shift on screen ptr if on screen

	; 4. Clip to right of screen
	ADD $tmp,$x,$spr_w ; Work out right edge of sprite
	ADD $tmp,$tmp,#4 ; Take into account preshift column
	SUBS $tmp,$tmp,$scr_w ; Off right edge?
	ADDGT $spr_g,$spr_g,$tmp ; Yes, so increase sprite gap
	SUBGT $spr_w,$spr_w,$tmp ; And decrease row length
	ORRGT $tmp2,$tmp2,#4 ; And set flag to disable right hand masking
	SUBLT $scr_g,$scr_g,$tmp ; Increase screen gap
	ADD $scr_g,$scr_g,#4 ; Yet more preshift column :|

	; 5. Clip to left of screen
	CMP $x,#0 ; Off left edge?
	SUBLT $spr_g,$spr_g,$x ; Yes, so increase sprite gap
	ADDLT $spr_w,$spr_w,$x ; And decrease row length
	SUBLT $spr,$spr,$x ; And shift on sprite ptr
	ORRLT $tmp2,$tmp2,#8 ; And set flag to disable left hand masking
	ADDGT $scr,$scr,$x ; Else shift on screen ptr
	ADDGT $scr_g,$scr_g,$x ; And increase screen gap

	; 6. Decode bitmasks from tmp2
	; tmp2 is of the format 'LROO' where:
	;    L is the lefthand mask disable flag (i.e. set to 00000000)
	;    R is the righthand mask disable flag (i.e. set to 00000000)
	;    O is the offset #
	; ofs	left mask	right mask
	; 0	00000000	ffffffff
	; 1	000000ff	ffffff00
	; 2	0000ffff	ffff0000
	; 3	00ffffff	ff000000
	; So need to:
	; * Calc left mask
	ANDS $tmp,$tmp2,#3
	MOVEQ $x,#0
	MOVNE $x,#&FF
	CMP $tmp,#2
	ORRGE $x,$x,#&FF00
	ORRGT $x,$x,#&FF0000
	; * Set right mask
	TST $tmp2,#4
	MOVNE $y,#0
	MVNEQ $y,$x ; right mask is just the inverse of the left
	; * Set left mask
	TST $tmp2,#8
	MOVNE $x,#0
	MEND


;
;
;				Plotting stuff
;
;


	MACRO
	GP_SPRDRAW_PROLOGUE $w,$cw
	; Prologue code for main plotting loop
	; In:
	; $w = Register contaning width of area to plot (pixels)
	; Out:
	; $cw = Register to hold the current width during plotting loop
98	MOV $cw,$w ; Make copy of sprite width
99
	MEND

	MACRO
	GP_SPRDRAW_EPILOGUE $cw,$h,$spr,$spr_g,$scr,$scr_g,$pixdrawn
	; Epilogue code for main plotting loop
	; In:
	; $cw = Register current width of line left to plot (pixels)
	; $h = Register height of area to plot (pixels)
	; $spr,$scr = Register sprite/screen ptrs
	; $spr_g,$scr_g = Register sprite/screen end-of-line gaps (bytes)
	; $pixdrawn = Register/constant (as '#...') number of pixels drawn during the loop body
	; Out:
	; $cw,$h,$spr,$scr updated as necessary
	SUBS $cw,$cw,$pixdrawn ; Decrease number of pixels to draw
	BGT %b99 ; Branch if more left
	ADD $spr,$spr,$spr_g ; Else advance ptrs to next row
	ADD $scr,$scr,$scr_g
	SUBS $h,$h,#1 ; Decrease row count
	BGT %b98 ; And go round again
	MEND

	MACRO
	GP_SPRDRAW_SIMPLE_8BPP $w,$h,$spr,$spr_g,$scr,$scr_g,$tmp,$tmp2
	; Draw sprite to screen pixel by pixel
	; In:
	; $w,$h = Register size of area to draw in pixels, both > 0
	; $spr,$scr = Register pointers to draw to/from
	; $spr_g,$scr_g = Register end of line gap amounts (bytes)
	; $tmp,$tmp2 = Temp registers
	; Out:
	; $h = 0
	; $spr,$scr = pointer to pixel after last one drawn
	; $tmp,$tmp2 mangled
	GP_SPRDRAW_PROLOGUE $w,$tmp
	LDRB $tmp2,[$spr],#1 ; Copy pixel
	STRB $tmp2,[$scr],#1
	GP_SPRDRAW_EPILOGUE $tmp,$h,$spr,$spr_g,$scr,$scr_g,#1
	MEND


	MACRO
	GP_SPRDRAW_SIMPLE_16BPP $w,$h,$spr,$spr_g,$scr,$scr_g,$tmp,$tmp2
	; Draw sprite to screen pixel by pixel
	; In:
	; $w,$h = Register size of area to draw in pixels, both > 0
	; $spr,$scr = Register pointers to draw to/from
	; $spr_g,$scr_g = Register end of line gap amounts (bytes)
	; $tmp,$tmp2 = Temp registers
	; Out:
	; $h = 0
	; $spr,$scr = pointer to pixel after last one drawn
	; $tmp,$tmp2 mangled
	GP_SPRDRAW_PROLOGUE $w,$tmp
	LDRB $tmp2,[$spr],#1 ; Copy pixel byte by byte
	STRB $tmp2,[$scr],#1
	LDRB $tmp2,[$spr],#1
	STRB $tmp2,[$scr],#1
	GP_SPRDRAW_EPILOGUE $tmp,$h,$spr,$spr_g,$scr,$scr_g,#1
	MEND


	MACRO
	GP_SPRDRAW_SIMPLE_WORDALIGNED $w,$h,$spr,$spr_g,$scr,$scr_g,$tmp,$da,$db,$dc,$dd,$c
	; Draw sprite to screen up to 4 words at a time
	; In:
	; $w,$h = Register size of area to draw (pixels). w must be word aligned according to pixel size, h must be > 0
	; $spr,$scr = Register pointers to draw to/from (word aligned)
	; $spr_g,$scr_g = Register end of line gap amounts (bytes, multiple of 4)
	; $tmp,$da,$db,$dc,$dd = Temp registers
	; $c = Constant sprite/screen pixel size: 1 << c = bytes per pixel. Must be 0, 1 or 2.
	; Out:
	; $h = 0
	; $spr,$scr = pointer to pixel after last one drawn
	; $tmp,$da,$db,$dc,$dd mangled
90	MOV $tmp,$w,LSR #2-$c ; Calculate width to draw, in words
	MOVS $tmp,$tmp,LSR #1 ; Any 1-word sections?
	LDRCS $da,[$spr],#4 ; Draw 1 word
	STRCS $da,[$scr],#4
	MOVS $tmp,$tmp,LSR #1 ; Any 2-word sections?
	LDMCSIA $spr!,{$da,$db} ; Draw 2 words
	STMCSIA $scr!,{$da,$db}
91	SUBS $tmp,$tmp,#1 ; Do loops of 4 word transfers
	LDMGEIA $spr!,{$da,$db,$dc,$dd}
	STMGEIA $scr!,{$da,$db,$dc,$dd}
	SUBS $tmp,$tmp,#1 ; Two transfers per loop, to give the pipeline a rest
	LDMGEIA $spr!,{$da,$db,$dc,$dd}
	STMGEIA $scr!,{$da,$db,$dc,$dd}
	BGT %b91 ; Loop round if still some data left
	ADD $spr,$spr,$spr_g ; Advance ptrs to next row
	ADD $scr,$scr,$scr_g
	SUBS $h,$h,#1 ; Decrease row count
	BGT %b90
	MEND


	MACRO
	GP_SPRDRAW_PRESHIFTED $w,$h,$spr,$spr_g,$scr,$scr_g,$tmp,$da,$db,$dc,$dd,$c,$lmask,$rmask
	; Draw preshifted sprite to screen up to 4 words at a time. For each row, first the left hand masked word is drawn, then w-(one word) normal pixels, then the right hand masked word.
	; In:
	; $w,$h = Register size of area to draw (pixels). w must be word aligned according to pixel size, both must be > 0. w must not take into account the right hand preshifting word
	; $spr,$scr = Register pointers to draw to/from (word aligned)
	; $spr_g,$scr_g = Register end of line gap amounts (bytes, multiple of 4). spr_g should take into account the right preshifting word
	; $tmp,$da,$db,$dc,$dd = Temp registers
	; $c = Constant sprite/screen pixel size: 1 << spr_c = bytes per pixel. Must be 0, 1 or 2.
	; $lmask, $rmask = Register left/right mask words, for the 1st and last words of each row. scr = (scr & mask) | (spr & ~mask)
	; Out:
	; $h = 0
	; $spr,$scr = pointer to last pixel drawn
	; $tmp,$da,$db,$dc,$dd mangled
90	MOV $tmp,$w,LSR #2-$c ; Calculate width to draw, in words
	; Draw the left hand masked area
	LDR $da,[$spr],#4
	LDR $db,[$scr]
	BIC $da,$da,$lmask
	AND $db,$db,$lmask
	ORR $da,$da,$db
	STR $da,[$scr],#4
	SUB $tmp,$tmp,#1
	MOVS $tmp,tmp,LSR #1 ; Any 1-word sections?
	LDRCS $da,[$spr],#4
	STRCS $da,[$scr],#4
	MOVS $tmp,$tmp,LSR #1 ; Any 2-word sections?
	LDMCSIA $spr!,{$da,$db}
	STMCSIA $scr!,{$da,$db}
91	SUBS $tmp,$tmp,#1
	LDMGEIA $spr!,{$da,$db,$dc,$dd}
	STMGEIA $scr!,{$da,$db,$dc,$dd}
	SUBS $tmp,$tmp,#1 ; Expand loop a bit to give the pipeline a rest
	LDMGEIA $spr!,{$da,$db,$dc,$dd}
	STMGEIA $scr!,{$da,$db,$dc,$dd}
	BGT %b91
	; Draw the right hand masked area
	LDR $da,[$spr],$spr_g
	LDR $db,[$scr]
	BIC $da,$da,$rmask
	AND $db,$db,$rmask
	ORR $da,$da,$db
	STR $da,[$scr],$scr_g
	SUBS $h,$h,#1
	BGT %b90
	MEND

;
;
;			Transformed sprite plotters
;
;

	MACRO
	GP_SPRDRAW_TRANSFORMEDROW_8BPP $scr,$len,$spr,$spr_g,$width,$height,$x,$y,$xd,$yd,$shift,$tmp,$tmp2
	; Draw a row on the screen from a transformed sprite, wrapping sprite coords
	; In:
	; $scr = Register screen pointer
	; $len = Register row length (pixels), must be > 0
	; $spr = Register sprite pointer; points to pixel 0,0
	; $spr_g = Register sprite end-of-line gap amount (bytes)
	; $width,$height = Register sprite size (pixels)
	; $x,$y = Register sprite start coords (pixels, fixed point precision with 'shift' fraction bits). Must be within sprite.
	; $xd,$yd = Register sprite coord advance per screen pixel (pixel, fixed point precision with 'shift' fraction bits). Fastest if pre-rounded to within sprite limits.
	; $shift = Constant fixed point precision value; sprite coords have 'shift' fraction bits beyond their binary point
	; $tmp,$tmp2 = Temporary registers
	; Out:
	; $scr = pointer to pixel after last one drawn
	; $len = 0
	; $x,$y = location of next sprite pixel to draw
	; $tmp,$tmp2 mangled
	ADD $tmp2,$width,$spr_g ; work out spr width in bytes
90	MOV $tmp,$y,LSR #$shift
	MLA $tmp,$tmp2,$tmp,$spr
	LDRB $tmp,[$tmp,$x,LSR #$shift]
	STRB $tmp,[$scr],#1
	SUBS $len,$len,#1
	BLE %f91
	; move on
	ADD $x,$x,$xd
	ADD $y,$y,$yd
	GP_ROUND $x,$width,$shift
	GP_ROUND $y,$height,$shift
	B %b90
91
	MEND


	MACRO
	GP_SPRDRAW_BOUNDEDTRANSFORMEDROW_8BPP $scr,$len,$spr,$spr_g,$width,$height,$x,$y,$xd,$yd,$shift,$tmp,$tmp2
	; Draw a row on the screen from a transformed sprite, stopping if the coords go out of bounds
	; In:
	; $scr = Register screen pointer
	; $len = Register row length (pixels), must be > 0
	; $spr = Register sprite pointer; points to pixel 0,0
	; $spr_g = Register sprite end-of-line gap amount (bytes)
	; $width,$height = Register sprite size (pixels)
	; $x,$y = Register sprite start coords (pixels, fixed point precision with 'shift' fraction bits). Must be within sprite.
	; $xd,$yd = Register sprite coord advance per screen pixel (pixel, fixed point precision with 'shift' fraction bits).
	; $shift = Constant fixed point precision value; sprite coords have 'shift' fraction bits beyond their binary point
	; $tmp,$tmp2 = Temporary registers
	; Out:
	; $scr = pointer to pixel after last one drawn
	; $len = number of pixels left to draw
	; $x,$y = location of next sprite pixel to draw
	; $tmp,$tmp2 mangled
	ADD $tmp2,$width,$spr_g ; work out spr width in bytes
90	MOV $tmp,$y,LSR #$shift
	MLA $tmp,$tmp2,$tmp,$spr
	LDRB $tmp,[$tmp,$x,LSR #$shift]
	STRB $tmp,[$scr],#1
	SUBS $len,$len,#1
	; move on
	ADD $x,$x,$xd
	ADD $y,$y,$yd
	BLE %f91 ; to ensure x,y are on the 'not drawn' pixel
	CMP $x,$width,LSL #$shift
	CMPLO $y,$height,LSL #$shift
	BLO %b90
91
	MEND

	END
