; GnarlPlot common assembler code V0.11 28/8/04
; 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/>.

; Registers

R0	RN	0
R1	RN	1
R2	RN	2
R3	RN	3
R4	RN	4
R5	RN	5
R6	RN	6
R7	RN	7
R8	RN	8
R9	RN	9
R10	RN	10
R11	RN	11
R12	RN	12
R13	RN	13
R14	RN	14
PC	RN	15


; Screen definition

gp_screen_width		EQU 0	; actual pixel width
gp_screen_height	EQU 4	; actual pixel height
gp_screen_ps		EQU 8	; pixel size
gp_screen_mt		EQU 12	; mask type
gp_screen_gap		EQU 16	; end-of-line gap in bytes
gp_screen_bank0		EQU 20	; bank to draw to
gp_screen_bank1		EQU 24	; bank to read from

; Sprite definition

gp_spr_fmt	EQU 0	; Sprite format ptr offset
gp_spr_data	EQU 4	; Sprite data ptr offset

; Misc macros

	MACRO
	GP_GEN_CALCPOS $w,$g,$x,$y,$pos,$c
	; Calculate address offset into sprite, screen, etc.
	; In:
	; $w - Register image width in pixels
	; $g - Register end-of-line gap in bytes
	; $x,$y - Registers coordinates of pixel address offset to return. Must lie within image bounds for sane results to be returned.
	; $c - Constant image pixel size: 1 << c = bytes per pixel
	; Out:
	; $pos - Register to be filled with the address offset of the specified pixel
	ADD $pos,$g,$w,LSL #$c
	MUL $pos,$y,$pos
	ADD $pos,$pos,$x,LSL #$c
	MEND

	MACRO
	GP_ROUND $val,$lim,$shift
	; Round off a value by addition/subtraction until within range
	; In:
	; $val = Register value to round off (fixed point, 'shift' fraction bits)
	; $lim = Register upper limit to use
	; $shift = Constant fixed point precision value
	; Out:
	; 0 <= $val < ($lim << $shift)
90	CMP $val,$lim,LSL #$shift
	BLO %f91 ; is this quicker than running through?
	SUBGE $val,$lim,LSL #$shift ; if >= then sub
	ADDLT $val,$lim,LSL #$shift ; else if < and not lo then must be add
	CMP $val,$lim,LSL #$shift
	BLO %f91
	SUBGE $val,$lim,LSL #$shift
	ADDLT $val,$lim,LSL #$shift
	CMP $val,$lim,LSL #$shift
	BHS %b90
91
	MEND

; Colour conversion macros

	MACRO
	GP_8_TO_24 $in,$out,$tmp
	; Convert a single 8bpp BGGRBRTT pixel to 24bpp &BBGGRR format
	; In:
	; $in = Register containing a BGGRBRTT format 8bpp pixel in the bottom byte
	; $tmp = Temporary register
	; Out:
	; $out = Register containing a &00BBGGRR format 24bpp pixel
	; $tmp mangled
	AND $out,$in,#3 ; Get tint bits
	ORR $out,$out,$out,LSL #8 ; Copy into other bytes
	ORR $out,$out,$out,LSL #8
	AND $tmp,$in,#4 ; Get low red bit
	ORR $out,$out,$tmp ; Combine
	AND $tmp,$in,#16 ; Get high red bit
	ORR $out,$out,$tmp,LSR #1 ; Combine
	AND $tmp,$in,#8 ; Get low blue bit
	ORR $out,$out,$tmp,LSL #15 ; Combine
	AND $tmp,$in,#128 ; Get high blue bit
	ORR $out,$out,$tmp,LSL #12 ; Combine
	AND $tmp,$in,#64+32 ; Get both green bits
	ORR $out,$out,$tmp,LSL #5 ; Combine
	; We now have a &000B0G0R format word
	ORR $out,$out,$out,LSL #4 ; Duplicate the nibbles to produce &00BBGGRR
	MEND

	MACRO
	GP_8_TO_16 $in,$out,$tmp
	; Convert a single 8bpp BGGRBRTT pixel to 16bpp format
	; In:
	; $in = Register containing a BGGRBRTT format 8bpp pixel in the bottom byte
	; $tmp = Temporary register
	; Out:
	; $out = Register containing a 16bpp pixel
	; $tmp mangled
	AND $out,$in,#3 ; Get tint bits
	MOV $out,$out,LSL #1 ; Shift across 1 bit - components are CcTtC format
	ORR $out,$out,$out,LSL #5 ; Copy into other components
	ORR $out,$out,$out,LSL #5
	AND $tmp,$in,#4 ; Get low red bit
	ORR $out,$out,$tmp,LSL #1 ; Combine
	AND $tmp,$in,#16 ; Get high red bit
	ORR $out,$out,$tmp ; Combine
	ORR $out,$out,$tmp,LSR #4 ; And again
	AND $tmp,$in,#8 ; Get low blue bit
	ORR $out,$out,$tmp,LSL #10 ; Combine
	AND $tmp,$in,#128 ; Get high blue bit
	ORR $out,$out,$tmp,LSL #7 ; Combine
	ORR $out,$out,$tmp,LSL #3 ; And again
	AND $tmp,$in,#64+32 ; Get both green bits
	ORR $out,$out,$tmp,LSL #3 ; Combine
	AND $tmp,$in,#64 ; Get high green bit
	ORR $out,$out,$tmp,LSR #1 ; Combine
	MEND

	MACRO
	GP_16_TO_24 $in,$out,$tmp
	; Convert a single 16bpp pixel to 24bpp format
	; In:
	; $in = Register containing a 16bpp pixel in the bottom two bytes
	; $tmp = Temporary register
	; Out:
	; $out = Register containing a 24bpp pixel
	; $tmp mangled
	AND $out,$in,#31 ; Red
	MOV $out,$out,LSL #3 ; Shift up a bit
	ORR $out,$out,$out,LSR #5 ; And duplicate upper 3 bits
	AND $tmp,$in,#31*32 ; Green
	ORR $tmp,$tmp,$tmp,LSR #5 ; Duplicate
	AND $tmp,$tmp,#255*4 ; Reduce to 8 bit value
	ORR $out,$out,$tmp,LSL #6 ; Combine
	AND $tmp,$in,#31*32*32 ; Blue
	ORR $tmp,$tmp,$tmp,LSR #5 ; Duplicate
	MOV $tmp,$tmp,LSR #7 ; Reduce to 8 bit value
	ORR $out,$out,$tmp,LSL #16 ; Combine
	MEND

	MACRO
	GP_16_TO_8 $in,$out,$tmp
	; Convert a single 16bpp pixel to 8bpp format
	; In:
	; $in = Register containing a 16bpp pixel in the bottom two bytes
	; $tmp = Temporary register
	; Out:
	; $out = Register containing a 8bpp pixel in the lower byte, zeros in the others
	; $tmp mangled
	AND $out,$in,#6 ; Get red the tint
	AND $tmp,$in,#6*32 ; Green tint
	ADD $out,$out,$tmp,LSR #5 ; Combine
	AND $tmp,$in,#6*32*32 ; Blue tint
	ADD $out,$out,$tmp,LSR #10 ; Combine
	ADD $out,$out,$out,LSL #1 ; *3
	MOV $out,$out,LSR #4 ; Shifting 54 (6*3*3) down to 3
	AND $tmp,$in,#8 ; Low red bit
	ORR $out,$out,$tmp,LSR #1 ; Combine
	AND $tmp,$in,#16 ; High red bit
	ORR $out,$out,$tmp ; Combine
	AND $tmp,$in,#24*32 ; Green bits
	ORR $out,$out,$tmp,LSR #3 ; Combine
	AND $tmp,$in,#8*32*32 ; Low blue bit
	ORR $out,$out,$tmp,LSR #10 ; Combine
	AND $tmp,$in,#16*32*32 ; High blue bit
	ORR $out,$out,$tmp,LSR #7 ; Combine
	MEND

	MACRO
	GP_24_TO_16 $in,$out,$tmp
	; Convert a single 24bpp pixel to 16bpp format
	; In:
	; $in = Register containing a 24bpp pixel
	; $tmp = Temporary register
	; Out:
	; $out = Register containing a 16bpp pixel in the lower two bytes, zeros in the others
	; $tmp mangled
	AND $out,$in,#31*8 ; Red
	MOV $out,$out,LSR #3
	AND $tmp,$in,#31*8*256 ; Green
	ORR $out,$out,$tmp,LSR #6
	AND $tmp,$in,#31*8*256*256 ; Blue
	ORR $out,$out,$tmp,LSR #9
	MEND

	MACRO
	GP_24_TO_8 $in,$out,$tmp
	; Convert a single 24bpp pixel to 8bpp format
	; In:
	; $in = Register containing a 24bpp pixel
	; $tmp = Temporary register
	; Out:
	; $out = Register containing an 8bpp pixel in the lower byte, zeros in the others
	; $tmp mangled
	AND $out,$in,#48 ; Get the tint
	AND $tmp,$in,#48*256
	ADD $out,$out,$tmp,LSR #8
	AND $tmp,$in,#48*65536
	ADD $out,$out,$tmp,LSR #16 ; Combine
	ADD $out,$out,$out,LSL #1 ; *3
	MOV $out,$out,LSR #7 ; Shifting 432 (48*3*3) down to 3
	AND $tmp,$in,#64 ; Get red bit #1
	ORR $out,$out,$tmp,LSR #4 ; Store
	AND $tmp,$in,#128 ; Red bit #2
	ORR $out,$out,$tmp,LSR #3 ; Store
	AND $tmp,$in,#192*256 ; Green bits
	ORR $out,$out,$tmp,LSR #9 ; Store
	AND $tmp,$in,#64*65536 ; Blue
	ORR $out,$out,$tmp,LSR #19
	AND $tmp,$in,#128*65536 ; Blue
	ORR $out,$out,$tmp,LSR #16
	MEND

; Mask format handling macros
	MACRO
	GP_24_GREY $spr,$scr,$out,$tmp,$tmp2,$tmp3
	; Combine two 24bpp pixels to one, according to the greyscale mask of $spr - $out=($scr*mask)/255+$spr
	; In:
	; $spr = Register containing 24bpp sprite pixel and 8bpp greyscale mask
	; $scr = Register containing 24bpp screen pixel
	; $tmp,$tmp2,$tmp3 = Temporary registers
	; Out:
	; $out = Register contaning a 24bpp pixel, top bits zeroed
	; $tmp,$tmp2,$tmp3 mangled
	MOV $tmp,$spr,LSR #24 ; Get greyscale
	ADDS $tmp,$tmp,$tmp,LSR #7 ; Scale it up to 256 to improve range
	MOVEQ $out,$spr
	BEQ %f90 ; Skip if none of screen visible
	AND $out,$spr,#255 ; Get red
	AND $tmp2,$scr,#255
	MUL $tmp2,$tmp,$tmp2
	ADD $out,$out,$tmp2,LSR #8
	CMP $out,#255
	MOVGT $out,#255 ; Avoid overflow
	AND $tmp3,$spr,#&FF00 ; Get green
	AND $tmp2,$scr,#&FF00
	MUL $tmp2,$tmp,$tmp2
	ADD $tmp3,$tmp3,$tmp2,LSR #8
	CMP $tmp3,#&FF00
	ORRGE $out,$out,#&FF00
	ANDLT $tmp3,$tmp3,#&FF00
	ORRLT $out,$out,$tmp3
	AND $tmp3,$spr,#&FF0000 ; Get blue
	AND $tmp2,$scr,#&FF0000
	MUL $tmp2,$tmp,$tmp2
	ADD $tmp3,$tmp3,$tmp2,LSR #8
	CMP $tmp3,#&FF0000
	ORRGE $out,$out,#&FF0000
	ANDLT $tmp3,$tmp3,#&FF0000
	ORRLT $out,$out,$tmp3
90
	MEND

	MACRO
	GP_24_RGB $spr,$rgb,$scr,$out,$tmp,$tmp2,$tmp3
	; Combine two 24bpp pixels to one, according to the RGB mask of $rgb - $out=($scr*$rgb)/255+$spr
	; In:
	; $spr = Register containing 24bpp sprite pixel
	; $rgb = Register containing 24bpp sprite mask
	; $scr = Register containing 24bpp screen pixel
	; $tmp,$tmp2,$tmp3 = Temporary registers
	; Out:
	; $out = Register contaning a 24bpp pixel, top bits zeroed
	; $tmp,$tmp2,$tmp3 mangled
	AND $tmp,$rgb,#255 ; Get red mask
	ADDS $tmp,$tmp,$tmp,LSR #7 ; Scale it up to 256 to improve range
	AND $out,$spr,#255 ; Get red
	BEQ %f90 ; Skip if none of screen visible
	AND $tmp2,$scr,#255
	MUL $tmp2,$tmp,$tmp2
	ADD $out,$out,$tmp2,LSR #8
	CMP $out,#255
	MOVGT $out,#255 ; Avoid overflow
90	MOV $tmp,$rgb,LSR #8 ; Green mask
	AND $tmp,$tmp,#255
	ADDS $tmp,$tmp,$tmp,LSR #7
	AND $tmp3,$spr,#&FF00 ; Get green
	ORREQ $out,$out,$tmp3
	BEQ %f91 ; Skip if none of screen visible
	AND $tmp2,$scr,#&FF00
	MUL $tmp2,$tmp,$tmp2
	ADD $tmp3,$tmp3,$tmp2,LSR #8
	CMP $tmp3,#&FF00
	ORRGE $out,$out,#&FF00
	ANDLT $tmp3,$tmp3,#&FF00
	ORRLT $out,$out,$tmp3
91	MOV $tmp,$rgb,LSR #16 ; Blue mask
	AND $tmp,$tmp,#255
	ADDS $tmp,$tmp,$tmp,LSR #7
	AND $tmp3,$spr,#&FF0000 ; Get blue
	ORREQ $out,$out,$tmp3
	BEQ %f92 ; Skip if none of screen visible
	AND $tmp2,$scr,#&FF0000
	MUL $tmp2,$tmp,$tmp2
	ADD $tmp3,$tmp3,$tmp2,LSR #8
	CMP $tmp3,#&FF0000
	ORRGE $out,$out,#&FF0000
	ANDLT $tmp3,$tmp3,#&FF0000
	ORRLT $out,$out,$tmp3
92
	MEND

	END
