|  | /* libgcc routines for R8C/M16C/M32C | 
|  | Copyright (C) 2005-2020 Free Software Foundation, Inc. | 
|  | Contributed by Red Hat. | 
|  |  | 
|  | This file is part of GCC. | 
|  |  | 
|  | GCC 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. | 
|  |  | 
|  | GCC 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. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #if defined(__r8c_cpu__) || defined(__m16c_cpu__) | 
|  | #define A16 | 
|  | #define A(n,w) n | 
|  | #define W w | 
|  | #else | 
|  | #define A24 | 
|  | #define A(n,w) w | 
|  | #define W l | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #ifdef L__m32c_memregs | 
|  |  | 
|  | /* Warning: these memory locations are used as a register bank.  They | 
|  | *must* end up consecutive in any final executable, so you may *not* | 
|  | use the otherwise obvious ".comm" directive to allocate space for | 
|  | them. */ | 
|  |  | 
|  | .bss | 
|  | .global	mem0 | 
|  | mem0:	.space	1 | 
|  | .global	mem1 | 
|  | mem1:	.space	1 | 
|  | .global	mem2 | 
|  | mem2:	.space	1 | 
|  | .global	mem3 | 
|  | mem3:	.space	1 | 
|  | .global	mem4 | 
|  | mem4:	.space	1 | 
|  | .global	mem5 | 
|  | mem5:	.space	1 | 
|  | .global	mem6 | 
|  | mem6:	.space	1 | 
|  | .global	mem7 | 
|  | mem7:	.space	1 | 
|  | .global	mem8 | 
|  | mem8:	.space	1 | 
|  | .global	mem9 | 
|  | mem9:	.space	1 | 
|  | .global	mem10 | 
|  | mem10:	.space	1 | 
|  | .global	mem11 | 
|  | mem11:	.space	1 | 
|  | .global	mem12 | 
|  | mem12:	.space	1 | 
|  | .global	mem13 | 
|  | mem13:	.space	1 | 
|  | .global	mem14 | 
|  | mem14:	.space	1 | 
|  | .global	mem15 | 
|  | mem15:	.space	1 | 
|  |  | 
|  | #endif | 
|  |  | 
|  | #ifdef L__m32c_eh_return | 
|  | .text | 
|  | .global __m32c_eh_return | 
|  | __m32c_eh_return: | 
|  |  | 
|  | /* At this point, r0 has the stack adjustment, r1r3 has the | 
|  | address to return to.  The stack looks like this: | 
|  |  | 
|  | old_ra | 
|  | old_fp | 
|  | <- unwound sp | 
|  | ... | 
|  | fb | 
|  | through | 
|  | r0 | 
|  | <- sp | 
|  |  | 
|  | What we need to do is restore all the registers, update the | 
|  | stack, and return to the right place. | 
|  | */ | 
|  |  | 
|  | stc	sp,a0 | 
|  |  | 
|  | add.W	A(#16,#24),a0 | 
|  | /* a0 points to the current stack, just above the register | 
|  | save areas */ | 
|  |  | 
|  | mov.w	a0,a1 | 
|  | exts.w	r0 | 
|  | sub.W	A(r0,r2r0),a1 | 
|  | sub.W	A(#3,#4),a1 | 
|  | /* a1 points to the new stack.  */ | 
|  |  | 
|  | /* This is for the "rts" below.  */ | 
|  | mov.w	r1,[a1] | 
|  | #ifdef A16 | 
|  | mov.w	r2,r1 | 
|  | mov.b	r1l,2[a1] | 
|  | #else | 
|  | mov.w	r2,2[a1] | 
|  | #endif | 
|  |  | 
|  | /* This is for the "popc sp" below.  */ | 
|  | mov.W	a1,[a0] | 
|  |  | 
|  | popm    r0,r1,r2,r3,a0,a1,sb,fb | 
|  | popc	sp | 
|  | rts | 
|  | #endif | 
|  |  | 
|  | /* SImode arguments for SI foo(SI,SI) functions.  */ | 
|  | #ifdef A16 | 
|  | #define SAL  5[fb] | 
|  | #define SAH  7[fb] | 
|  | #define SBL  9[fb] | 
|  | #define SBH 11[fb] | 
|  | #else | 
|  | #define SAL  8[fb] | 
|  | #define SAH 10[fb] | 
|  | #define SBL 12[fb] | 
|  | #define SBH 14[fb] | 
|  | #endif | 
|  |  | 
|  | #ifdef L__m32c_mulsi3 | 
|  | .text | 
|  | .global ___mulsi3 | 
|  | ___mulsi3: | 
|  | enter	#0 | 
|  | push.w	r2 | 
|  | mov.w	SAL,r0 | 
|  | mulu.w	SBL,r0		/* writes to r2r0 */ | 
|  | mov.w	r0,mem0 | 
|  | mov.w	r2,mem2 | 
|  | mov.w	SAL,r0 | 
|  | mulu.w	SBH,r0		/* writes to r2r0 */ | 
|  | add.w	r0,mem2 | 
|  | mov.w	SAH,r0 | 
|  | mulu.w	SBL,r0		/* writes to r2r0 */ | 
|  | add.w	r0,mem2 | 
|  | pop.w	r2 | 
|  | exitd | 
|  | #endif | 
|  |  | 
|  | #ifdef L__m32c_cmpsi2 | 
|  | .text | 
|  | .global ___cmpsi2 | 
|  | ___cmpsi2: | 
|  | enter	#0 | 
|  | cmp.w	SBH,SAH | 
|  | jgt	cmpsi_gt | 
|  | jlt	cmpsi_lt | 
|  | cmp.w	SBL,SAL | 
|  | jgt	cmpsi_gt | 
|  | jlt	cmpsi_lt | 
|  | mov.w	#1,r0 | 
|  | exitd | 
|  | cmpsi_gt: | 
|  | mov.w	#2,r0 | 
|  | exitd | 
|  | cmpsi_lt: | 
|  | mov.w	#0,r0 | 
|  | exitd | 
|  | #endif | 
|  |  | 
|  | #ifdef L__m32c_ucmpsi2 | 
|  | .text | 
|  | .global ___ucmpsi2 | 
|  | ___ucmpsi2: | 
|  | enter	#0 | 
|  | cmp.w	SBH,SAH | 
|  | jgtu	cmpsi_gt | 
|  | jltu	cmpsi_lt | 
|  | cmp.w	SBL,SAL | 
|  | jgtu	cmpsi_gt | 
|  | jltu	cmpsi_lt | 
|  | mov.w	#1,r0 | 
|  | exitd | 
|  | cmpsi_gt: | 
|  | mov.w	#2,r0 | 
|  | exitd | 
|  | cmpsi_lt: | 
|  | mov.w	#0,r0 | 
|  | exitd | 
|  | #endif | 
|  |  | 
|  | #ifdef L__m32c_jsri16 | 
|  | .text | 
|  | #ifdef A16 | 
|  | .global	m32c_jsri16 | 
|  | m32c_jsri16: | 
|  | add.w	#-1, sp | 
|  |  | 
|  | /* Read the address (16 bits) and return address (24 bits) off | 
|  | the stack.  */ | 
|  | mov.w	4[sp], r0 | 
|  | mov.w	1[sp], r3 | 
|  | mov.b	3[sp], a0 /* This zero-extends, so the high byte has | 
|  | zero in it.  */ | 
|  |  | 
|  | /* Write the return address, then new address, to the stack.  */ | 
|  | mov.w	a0, 1[sp] /* Just to get the zero in 2[sp].  */ | 
|  | mov.w	r0, 0[sp] | 
|  | mov.w	r3, 3[sp] | 
|  | mov.b	a0, 5[sp] | 
|  |  | 
|  | /* This "returns" to the target address, leaving the pending | 
|  | return address on the stack.  */ | 
|  | rts | 
|  | #endif | 
|  |  | 
|  | #endif |