/* -*- asm -*- */
/*
 * Copyright 2008 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio 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, or (at your option)
 * any later version.
 * 
 * GNU Radio 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef INCLUDED_GC_SPU_MACS_H
#define INCLUDED_GC_SPU_MACS_H

/*
 * This file contains a set of macros that are generally useful when
 * coding in SPU assembler
 *
 * Note that the multi-instruction macros in here may overwrite
 * registers 77, 78, and 79 without warning.
 */

/*
 * defines for all registers
 */
#define r0	$0
#define r1	$1
#define r2	$2
#define r3	$3
#define r4	$4
#define r5	$5
#define r6	$6
#define r7	$7
#define r8	$8
#define r9	$9
#define r10	$10
#define r11	$11
#define r12	$12
#define r13	$13
#define r14	$14
#define r15	$15
#define r16	$16
#define r17	$17
#define r18	$18
#define r19	$19
#define r20	$20
#define r21	$21
#define r22	$22
#define r23	$23
#define r24	$24
#define r25	$25
#define r26	$26
#define r27	$27
#define r28	$28
#define r29	$29
#define r30	$30
#define r31	$31
#define r32	$32
#define r33	$33
#define r34	$34
#define r35	$35
#define r36	$36
#define r37	$37
#define r38	$38
#define r39	$39
#define r40	$40
#define r41	$41
#define r42	$42
#define r43	$43
#define r44	$44
#define r45	$45
#define r46	$46
#define r47	$47
#define r48	$48
#define r49	$49
#define r50	$50
#define r51	$51
#define r52	$52
#define r53	$53
#define r54	$54
#define r55	$55
#define r56	$56
#define r57	$57
#define r58	$58
#define r59	$59
#define r60	$60
#define r61	$61
#define r62	$62
#define r63	$63
#define r64	$64
#define r65	$65
#define r66	$66
#define r67	$67
#define r68	$68
#define r69	$69
#define r70	$70
#define r71	$71
#define r72	$72
#define r73	$73
#define r74	$74
#define r75	$75
#define r76	$76
#define r77	$77
#define r78	$78
#define r79	$79
#define r80	$80
#define r81	$81
#define r82	$82
#define r83	$83
#define r84	$84
#define r85	$85
#define r86	$86
#define r87	$87
#define r88	$88
#define r89	$89
#define r90	$90
#define r91	$91
#define r92	$92
#define r93	$93
#define r94	$94
#define r95	$95
#define r96	$96
#define r97	$97
#define r98	$98
#define r99	$99
#define r100	$100
#define r101	$101
#define r102	$102
#define r103	$103
#define r104	$104
#define r105	$105
#define r106	$106
#define r107	$107
#define r108	$108
#define r109	$109
#define r110	$110
#define r111	$111
#define r112	$112
#define r113	$113
#define r114	$114
#define r115	$115
#define r116	$116
#define r117	$117
#define r118	$118
#define r119	$119
#define r120	$120
#define r121	$121
#define r122	$122
#define r123	$123
#define r124	$124
#define r125	$125
#define r126	$126
#define r127	$127


#define	lr	r0	// link register
#define	sp	r1	// stack pointer
                        // r2 is environment pointer for langs that need it (ALGOL)

#define	retval	r3	// return values are passed in regs starting at r3

#define	arg1	r3	// args are passed in regs starting at r3
#define	arg2	r4
#define	arg3	r5
#define	arg4	r6
#define	arg5	r7
#define	arg6	r8
#define	arg7	r9
#define	arg8	r10
#define	arg9	r11
#define	arg10	r12

//  r3 -  r74 are volatile (caller saves)
// r74 -  r79 are volatile (scratch regs possibly destroyed by fct prolog/epilog)
// r80 - r127 are non-volatile (callee-saves)

// scratch registers reserved for use by the macros in this file.

#define _gc_t0	r79
#define	_gc_t1	r78
#define	_gc_t2	r77

/*
 * ----------------------------------------------------------------
 * 		    	    pseudo ops
 * ----------------------------------------------------------------
 */
#define PROC_ENTRY(name)		\
        .text;				\
	.p2align 4;			\
	.global	name;			\
	.type	name, @function;	\
name:

/*
 * ----------------------------------------------------------------
 * 		    aliases for common operations
 * ----------------------------------------------------------------
 */

// Move register (even pipe, 2 cycles)
#define MR(rt, ra) 			or	rt, ra, ra;

// Move register (odd pipe, 4 cycles)
#define	LMR(rt, ra) 			rotqbyi	rt, ra, 0;

// return
#define	RETURN() 			bi	lr;

// hint for a return
#define	HINT_RETURN(ret_label)		hbr	ret_label, lr;

// return if zero
#define BRZ_RETURN(rt)			biz	rt, lr;

// return if not zero
#define BRNZ_RETURN(rt)			binz	rt, lr;

// return if halfword zero
#define	BRHZ_RETURN(rt)			bihz	rt, lr;

// return if halfword not zero
#define BRHNZ_RETURN(rt)		bihnz	rt, lr;


/*
 * ----------------------------------------------------------------
 * modulo like things for constant moduli that are powers of 2
 * ----------------------------------------------------------------
 */

// rt = ra & (pow2 - 1)
#define MODULO(rt, ra, pow2) \
	andi	rt, ra, (pow2)-1;

// rt = pow2 - (ra & (pow2 - 1))
#define MODULO_NEG(rt, ra, pow2) \
	andi	rt, ra, (pow2)-1;	      	\
	sfi	rt, rt, (pow2);

// rt = ra & -(pow2)
#define	ROUND_DOWN(rt, ra, pow2) \
	andi	rt, ra, -(pow2);

// rt = (ra + (pow2 - 1)) & -(pow2)
#define ROUND_UP(rt, ra, pow2) \
	ai	rt, ra, (pow2)-1;	      	\
	andi	rt, rt, -(pow2);

/*
 * ----------------------------------------------------------------
 * Splat - replicate a particular slot into all slots
 * Altivec analogs...
 * ----------------------------------------------------------------
 */

// replicate byte from slot s [0,15]
#define VSPLTB(rt, ra, s) \
	ilh	_gc_t0, (s)*0x0101;	        \
	shufb	rt, ra, ra, _gc_t0;

// replicate halfword from slot s [0,7]
#define	VSPLTH(rt, ra, s) \
	ilh	_gc_t0, 2*(s)*0x0101 + 0x0001; 	\
	shufb	rt, ra, ra, _gc_t0;

// replicate word from slot s [0,3]
#define VSPLTW(rt, ra, s) \
	iluh	_gc_t0, 4*(s)*0x0101 + 0x0001;	\
	iohl	_gc_t0, 4*(s)*0x0101 + 0x0203;	\
	shufb	rt, ra, ra, _gc_t0;
	
// replicate double from slot s [0,1]
#define	VSPLTD(rt, ra, s) \
	/* sp is always 16-byte aligned */ \
	cdd	_gc_t0, 8(sp);		/* 0x10111213 14151617 00010203 04050607 */ \
	rotqbyi	rt, ra, ra, (s) << 3;	/* rotate double into preferred slot 	 */ \
	shufb	rt, rt, rt, _gc_t0;

/*
 * ----------------------------------------------------------------
 * lots of min/max variations...
 *
 * On a slot by slot basis, compute the min or max
 *
 * U - unsigned, else signed
 * B,H,{} - byte, halfword, word
 * F float
 * ----------------------------------------------------------------
 */

#define MIN_SELB(rt, ra, rb, rc)	selb	rt, ra, rb, rc;
#define MAX_SELB(rt, ra, rb, rc)	selb	rt, rb, ra, rc;
	
	// words

#define MIN(rt, ra, rb) \
	cgt	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	MAX(rt, ra, rb) \
	cgt	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

#define UMIN(rt, ra, rb) \
	clgt	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	UMAX(rt, ra, rb) \
	clgt	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

	// bytes
	
#define MINB(rt, ra, rb) \
	cgtb	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	MAXB(rt, ra, rb) \
	cgtb	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

#define UMINB(rt, ra, rb) \
	clgtb	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	UMAXB(rt, ra, rb) \
	clgtb	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

	// halfwords
	
#define MINH(rt, ra, rb) \
	cgth	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	MAXH(rt, ra, rb) \
	cgth	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

#define UMINH(rt, ra, rb) \
	clgth	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	UMAXH(rt, ra, rb) \
	clgth	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

	// floats
	
#define FMIN(rt, ra, rb) \
	fcgt	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
#define	FMAX(rt, ra, rb) \
	fcgt	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)

// Ignoring the sign, select the values with the minimum magnitude
#define FMINMAG(rt, ra, rb) \
	fcmgt	_gc_t0, ra, rb; \
	MIN_SELB(rt, ra, rb, _gc_t0)
	
// Ignoring the sign, select the values with the maximum magnitude
#define	FMAXMAG(rt, ra, rb) \
	fcmgt	_gc_t0, ra, rb; \
	MAX_SELB(rt, ra, rb, _gc_t0)


#endif /* INCLUDED_GC_SPU_MACS_H */
