/*  -*- Mode: Asm -*-  */
/* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
   Contributed by Denis Chertykov <denisc@overta.ru>

This file 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 2, or (at your option) any
later version.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file.  (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)

This file 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; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#define __zero_reg__ r1
#define __tmp_reg__ r0
#define __SREG__ 0x3f
#define __SP_H__ 0x3e
#define __SP_L__ 0x3d

/* Most of the functions here are called directly from avr.md
   patterns, instead of using the standard libcall mechanisms.
   This can make better code because GCC knows exactly which
   of the call-used registers (not all of them) are clobbered.  */

	.section .text.libgcc, "ax", @progbits

	.macro	mov_l  r_dest, r_src
#if defined (__AVR_ENHANCED__)
	movw	\r_dest, \r_src
#else
	mov	\r_dest, \r_src
#endif
	.endm

	.macro	mov_h  r_dest, r_src
#if defined (__AVR_ENHANCED__)
	; empty
#else
	mov	\r_dest, \r_src
#endif
	.endm

/* Note: mulqi3, mulhi3 are open-coded on the enhanced core.  */
#if !defined (__AVR_ENHANCED__)
/*******************************************************
               Multiplication  8 x 8
*******************************************************/
#if defined (L_mulqi3)

#define	r_arg2	r22		/* multiplicand */
#define	r_arg1 	r24		/* multiplier */
#define r_res	__tmp_reg__	/* result */

	.global	__mulqi3
	.func	__mulqi3
__mulqi3:
	clr	r_res		; clear result
__mulqi3_loop:
	sbrc	r_arg1,0
	add	r_res,r_arg2
	add	r_arg2,r_arg2	; shift multiplicand
	breq	__mulqi3_exit	; while multiplicand != 0
	lsr	r_arg1		; 
	brne	__mulqi3_loop	; exit if multiplier = 0
__mulqi3_exit:	
	mov	r_arg1,r_res	; result to return register
	ret

#undef r_arg2  
#undef r_arg1  
#undef r_res   
	
.endfunc
#endif 	/* defined (L_mulqi3) */

#if defined (L_mulqihi3)
	.global	__mulqihi3
	.func	__mulqihi3
__mulqihi3:
	clr	r25
	sbrc	r24, 7
	dec	r25
	clr	r23
	sbrc	r22, 7
	dec	r22
	rjmp	__mulhi3
	.endfunc
#endif /* defined (L_mulqihi3) */

#if defined (L_umulqihi3)
	.global	__umulqihi3
	.func	__umulqihi3
__umulqihi3:
	clr	r25
	clr	r23
	rjmp	__mulhi3
	.endfunc
#endif /* defined (L_umulqihi3) */

/*******************************************************
               Multiplication  16 x 16
*******************************************************/
#if defined (L_mulhi3)
#define	r_arg1L	r24		/* multiplier Low */
#define	r_arg1H	r25		/* multiplier High */
#define	r_arg2L	r22		/* multiplicand Low */
#define	r_arg2H	r23		/* multiplicand High */
#define r_resL	__tmp_reg__	/* result Low */
#define r_resH  r21		/* result High */

	.global	__mulhi3
	.func	__mulhi3
__mulhi3:
	clr	r_resH		; clear result
	clr	r_resL		; clear result
__mulhi3_loop:
	sbrs	r_arg1L,0
	rjmp	__mulhi3_skip1
	add	r_resL,r_arg2L	; result + multiplicand
	adc	r_resH,r_arg2H
__mulhi3_skip1:	
	add	r_arg2L,r_arg2L	; shift multiplicand
	adc	r_arg2H,r_arg2H

	cp	r_arg2L,__zero_reg__
	cpc	r_arg2H,__zero_reg__
	breq	__mulhi3_exit	; while multiplicand != 0

	lsr	r_arg1H		; gets LSB of multiplier
	ror	r_arg1L
	sbiw	r_arg1L,0
	brne	__mulhi3_loop	; exit if multiplier = 0
__mulhi3_exit:
	mov	r_arg1H,r_resH	; result to return register
	mov	r_arg1L,r_resL
	ret

#undef r_arg1L
#undef r_arg1H
#undef r_arg2L
#undef r_arg2H
#undef r_resL 	
#undef r_resH 

.endfunc
#endif /* defined (L_mulhi3) */
#endif /* !defined (__AVR_ENHANCED__) */

#if defined (L_mulhisi3)
	.global	__mulhisi3
	.func	__mulhisi3
__mulhisi3:
	mov_l	r18, r24
	mov_h	r19, r25
	clr	r24
	sbrc	r23, 7
	dec	r24
	mov	r25, r24
	clr	r20
	sbrc	r19, 7
	dec	r20
	mov	r21, r20
	rjmp	__mulsi3
	.endfunc
#endif /* defined (L_mulhisi3) */

#if defined (L_umulhisi3)
	.global	__umulhisi3
	.func	__umulhisi3
__umulhisi3:
	mov_l	r18, r24
	mov_h	r19, r25
	clr	r24
	clr	r25
	clr	r20
	clr	r21
	rjmp	__mulsi3
	.endfunc
#endif /* defined (L_umulhisi3) */

#if defined (L_mulsi3)
/*******************************************************
               Multiplication  32 x 32
*******************************************************/
#define r_arg1L  r22		/* multiplier Low */
#define r_arg1H  r23
#define	r_arg1HL r24
#define	r_arg1HH r25		/* multiplier High */


#define	r_arg2L  r18		/* multiplicand Low */
#define	r_arg2H  r19	
#define	r_arg2HL r20
#define	r_arg2HH r21		/* multiplicand High */
	
#define r_resL	 r26		/* result Low */
#define r_resH   r27
#define r_resHL	 r30
#define r_resHH  r31		/* result High */

	
	.global	__mulsi3
	.func	__mulsi3
__mulsi3:
#if defined (__AVR_ENHANCED__)
	mul	r_arg1L, r_arg2L
	movw	r_resL, r0
	mul	r_arg1H, r_arg2H
	movw	r_resHL, r0
	mul	r_arg1HL, r_arg2L
	add	r_resHL, r0
	adc	r_resHH, r1
	mul	r_arg1L, r_arg2HL
	add	r_resHL, r0
	adc	r_resHH, r1
	mul	r_arg1HH, r_arg2L
	add	r_resHH, r0
	mul	r_arg1HL, r_arg2H
	add	r_resHH, r0
	mul	r_arg1H, r_arg2HL
	add	r_resHH, r0
	mul	r_arg1L, r_arg2HH
	add	r_resHH, r0
	clr	r_arg1HH	; use instead of __zero_reg__ to add carry
	mul	r_arg1H, r_arg2L
	add	r_resH, r0
	adc	r_resHL, r1
	adc	r_resHH, r_arg1HH ; add carry
	mul	r_arg1L, r_arg2H
	add	r_resH, r0
	adc	r_resHL, r1
	adc	r_resHH, r_arg1HH ; add carry
	movw	r_arg1L, r_resL
	movw	r_arg1HL, r_resHL
	clr	r1		; __zero_reg__ clobbered by "mul"
	ret
#else
	clr	r_resHH		; clear result
	clr	r_resHL		; clear result
	clr	r_resH		; clear result
	clr	r_resL		; clear result
__mulsi3_loop:
	sbrs	r_arg1L,0
	rjmp	__mulsi3_skip1
	add	r_resL,r_arg2L		; result + multiplicand
	adc	r_resH,r_arg2H
	adc	r_resHL,r_arg2HL
	adc	r_resHH,r_arg2HH
__mulsi3_skip1:
	add	r_arg2L,r_arg2L		; shift multiplicand
	adc	r_arg2H,r_arg2H
	adc	r_arg2HL,r_arg2HL
	adc	r_arg2HH,r_arg2HH
	
	lsr	r_arg1HH	; gets LSB of multiplier
	ror	r_arg1HL
	ror	r_arg1H
	ror	r_arg1L
	brne	__mulsi3_loop
	sbiw	r_arg1HL,0
	cpc	r_arg1H,r_arg1L
	brne	__mulsi3_loop		; exit if multiplier = 0
__mulsi3_exit:
	mov	r_arg1HH,r_resHH	; result to return register
	mov	r_arg1HL,r_resHL
	mov	r_arg1H,r_resH
	mov	r_arg1L,r_resL
	ret
#endif /* !defined (__AVR_ENHANCED__) */
#undef r_arg1L 
#undef r_arg1H 
#undef r_arg1HL
#undef r_arg1HH
             
             
#undef r_arg2L 
#undef r_arg2H 
#undef r_arg2HL
#undef r_arg2HH
             
#undef r_resL  
#undef r_resH  
#undef r_resHL 
#undef r_resHH 

.endfunc
#endif /* defined (L_mulsi3) */
	
/*******************************************************
       Division 8 / 8 => (result + remainder)
*******************************************************/
#define	r_rem	r25	/* remainder */
#define	r_arg1	r24	/* dividend, quotient */
#define	r_arg2	r22	/* divisor */
#define	r_cnt	r23	/* loop count */

#if defined (L_udivmodqi4)
	.global	__udivmodqi4
	.func	__udivmodqi4
__udivmodqi4:
	sub	r_rem,r_rem	; clear remainder and carry
	ldi	r_cnt,9		; init loop counter
	rjmp	__udivmodqi4_ep	; jump to entry point
__udivmodqi4_loop:
	rol	r_rem		; shift dividend into remainder
	cp	r_rem,r_arg2	; compare remainder & divisor
	brcs	__udivmodqi4_ep	; remainder <= divisor
	sub	r_rem,r_arg2	; restore remainder
__udivmodqi4_ep:
	rol	r_arg1		; shift dividend (with CARRY)
	dec	r_cnt		; decrement loop counter
	brne	__udivmodqi4_loop
	com	r_arg1		; complement result 
				; because C flag was complemented in loop
	ret
	.endfunc
#endif /* defined (L_udivmodqi4) */

#if defined (L_divmodqi4)
	.global	__divmodqi4
	.func	__divmodqi4
__divmodqi4:
        bst     r_arg1,7	; store sign of dividend
        mov     __tmp_reg__,r_arg1
        eor     __tmp_reg__,r_arg2; r0.7 is sign of result
        sbrc	r_arg1,7
	neg     r_arg1		; dividend negative : negate
        sbrc	r_arg2,7
	neg     r_arg2		; divisor negative : negate
	rcall	__udivmodqi4	; do the unsigned div/mod
	brtc	__divmodqi4_1
	neg	r_rem		; correct remainder sign
__divmodqi4_1:
	sbrc	__tmp_reg__,7
	neg	r_arg1		; correct result sign
__divmodqi4_exit:
	ret
	.endfunc
#endif /* defined (L_divmodqi4) */

#undef r_rem
#undef r_arg1
#undef r_arg2
#undef r_cnt
	
		
/*******************************************************
       Division 16 / 16 => (result + remainder)
*******************************************************/
#define	r_remL	r26	/* remainder Low */
#define	r_remH	r27	/* remainder High */

/* return: remainder */
#define	r_arg1L	r24	/* dividend Low */
#define	r_arg1H	r25	/* dividend High */

/* return: quotient */
#define	r_arg2L	r22	/* divisor Low */
#define	r_arg2H	r23	/* divisor High */
	
#define	r_cnt	r21	/* loop count */

#if defined (L_udivmodhi4)
	.global	__udivmodhi4
	.func	__udivmodhi4
__udivmodhi4:
	sub	r_remL,r_remL
	sub	r_remH,r_remH	; clear remainder and carry
	ldi	r_cnt,17	; init loop counter
	rjmp	__udivmodhi4_ep	; jump to entry point
__udivmodhi4_loop:
        rol	r_remL		; shift dividend into remainder
	rol	r_remH
        cp	r_remL,r_arg2L	; compare remainder & divisor
	cpc	r_remH,r_arg2H
        brcs	__udivmodhi4_ep	; remainder < divisor
        sub	r_remL,r_arg2L	; restore remainder
        sbc	r_remH,r_arg2H
__udivmodhi4_ep:
        rol	r_arg1L		; shift dividend (with CARRY)
        rol	r_arg1H
        dec	r_cnt		; decrement loop counter
        brne	__udivmodhi4_loop
	com	r_arg1L
	com	r_arg1H
; div/mod results to return registers, as for the div() function
	mov_l	r_arg2L, r_arg1L	; quotient
	mov_h	r_arg2H, r_arg1H
	mov_l	r_arg1L, r_remL		; remainder
	mov_h	r_arg1H, r_remH
	ret
	.endfunc
#endif /* defined (L_udivmodhi4) */

#if defined (L_divmodhi4)
	.global	__divmodhi4
	.func	__divmodhi4
__divmodhi4:
	.global	_div
_div:
        bst     r_arg1H,7	; store sign of dividend
        mov     __tmp_reg__,r_arg1H
        eor     __tmp_reg__,r_arg2H   ; r0.7 is sign of result
	rcall	__divmodhi4_neg1 ; dividend negative : negate
	sbrc	r_arg2H,7
	rcall	__divmodhi4_neg2 ; divisor negative : negate
	rcall	__udivmodhi4	; do the unsigned div/mod
	rcall	__divmodhi4_neg1 ; correct remainder sign
	tst	__tmp_reg__
	brpl	__divmodhi4_exit
__divmodhi4_neg2:
	com	r_arg2H
	neg	r_arg2L		; correct divisor/result sign
	sbci	r_arg2H,0xff
__divmodhi4_exit:
	ret
__divmodhi4_neg1:
	brtc	__divmodhi4_exit
	com	r_arg1H
	neg	r_arg1L		; correct dividend/remainder sign
	sbci	r_arg1H,0xff
	ret
	.endfunc
#endif /* defined (L_divmodhi4) */

#undef r_remH  
#undef r_remL  
             
#undef r_arg1H 
#undef r_arg1L 
             
#undef r_arg2H 
#undef r_arg2L 
             	
#undef r_cnt   	
	
/*******************************************************
       Division 32 / 32 => (result + remainder)
*******************************************************/
#define	r_remHH	r31	/* remainder High */
#define	r_remHL	r30
#define	r_remH	r27
#define	r_remL	r26	/* remainder Low */

/* return: remainder */
#define	r_arg1HH r25	/* dividend High */
#define	r_arg1HL r24
#define	r_arg1H  r23
#define	r_arg1L  r22	/* dividend Low */

/* return: quotient */
#define	r_arg2HH r21	/* divisor High */
#define	r_arg2HL r20
#define	r_arg2H  r19
#define	r_arg2L  r18	/* divisor Low */
	
#define	r_cnt __zero_reg__  /* loop count (0 after the loop!) */

#if defined (L_udivmodsi4)
	.global	__udivmodsi4
	.func	__udivmodsi4
__udivmodsi4:
	ldi	r_remL, 33	; init loop counter
	mov	r_cnt, r_remL
	sub	r_remL,r_remL
	sub	r_remH,r_remH	; clear remainder and carry
	mov_l	r_remHL, r_remL
	mov_h	r_remHH, r_remH
	rjmp	__udivmodsi4_ep	; jump to entry point
__udivmodsi4_loop:
        rol	r_remL		; shift dividend into remainder
	rol	r_remH
	rol	r_remHL
	rol	r_remHH
        cp	r_remL,r_arg2L	; compare remainder & divisor
	cpc	r_remH,r_arg2H
	cpc	r_remHL,r_arg2HL
	cpc	r_remHH,r_arg2HH
	brcs	__udivmodsi4_ep	; remainder <= divisor
        sub	r_remL,r_arg2L	; restore remainder
        sbc	r_remH,r_arg2H
        sbc	r_remHL,r_arg2HL
        sbc	r_remHH,r_arg2HH
__udivmodsi4_ep:
        rol	r_arg1L		; shift dividend (with CARRY)
        rol	r_arg1H
        rol	r_arg1HL
        rol	r_arg1HH
        dec	r_cnt		; decrement loop counter
        brne	__udivmodsi4_loop
				; __zero_reg__ now restored (r_cnt == 0)
	com	r_arg1L
	com	r_arg1H
	com	r_arg1HL
	com	r_arg1HH
; div/mod results to return registers, as for the ldiv() function
	mov_l	r_arg2L,  r_arg1L	; quotient
	mov_h	r_arg2H,  r_arg1H
	mov_l	r_arg2HL, r_arg1HL
	mov_h	r_arg2HH, r_arg1HH
	mov_l	r_arg1L,  r_remL	; remainder
	mov_h	r_arg1H,  r_remH
	mov_l	r_arg1HL, r_remHL
	mov_h	r_arg1HH, r_remHH
	ret
	.endfunc
#endif /* defined (L_udivmodsi4) */

#if defined (L_divmodsi4)
	.global	__divmodsi4
	.func	__divmodsi4
__divmodsi4:
        bst     r_arg1HH,7	; store sign of dividend
        mov     __tmp_reg__,r_arg1HH
        eor     __tmp_reg__,r_arg2HH   ; r0.7 is sign of result
	rcall	__divmodsi4_neg1 ; dividend negative : negate
	sbrc	r_arg2HH,7
	rcall	__divmodsi4_neg2 ; divisor negative : negate
	rcall	__udivmodsi4	; do the unsigned div/mod
	rcall	__divmodsi4_neg1 ; correct remainder sign
	rol	__tmp_reg__
	brcc	__divmodsi4_exit
__divmodsi4_neg2:
	com	r_arg2HH
	com	r_arg2HL
	com	r_arg2H
	neg	r_arg2L		; correct divisor/quotient sign
	sbci	r_arg2H,0xff
	sbci	r_arg2HL,0xff
	sbci	r_arg2HH,0xff
__divmodsi4_exit:
	ret
__divmodsi4_neg1:
	brtc	__divmodsi4_exit
	com	r_arg1HH
	com	r_arg1HL
	com	r_arg1H
	neg	r_arg1L		; correct dividend/remainder sign
	sbci	r_arg1H, 0xff
	sbci	r_arg1HL,0xff
	sbci	r_arg1HH,0xff
	ret
	.endfunc
#endif /* defined (L_divmodsi4) */

/**********************************
 * This is a prologue subroutine
 **********************************/
#if defined (L_prologue)

	.global	__prologue_saves__
	.func	__prologue_saves__
__prologue_saves__:
	push r2
	push r3
	push r4
	push r5
	push r6
	push r7
	push r8
	push r9
	push r10
	push r11
	push r12
	push r13
	push r14
	push r15
	push r16
	push r17
	push r28
	push r29
	in	r28,__SP_L__
	in	r29,__SP_H__
	sub	r28,r26
	sbc	r29,r27
	in	__tmp_reg__,__SREG__
	cli
	out	__SP_H__,r29
	out	__SREG__,__tmp_reg__
	out	__SP_L__,r28
	ijmp
.endfunc
#endif /* defined (L_prologue) */

/*
 * This is an epilogue subroutine
 */
#if defined (L_epilogue)

	.global	__epilogue_restores__
	.func	__epilogue_restores__
__epilogue_restores__:
	ldd	r2,Y+18
	ldd	r3,Y+17
	ldd	r4,Y+16
	ldd	r5,Y+15
	ldd	r6,Y+14
	ldd	r7,Y+13
	ldd	r8,Y+12
	ldd	r9,Y+11
	ldd	r10,Y+10
	ldd	r11,Y+9
	ldd	r12,Y+8
	ldd	r13,Y+7
	ldd	r14,Y+6
	ldd	r15,Y+5
	ldd	r16,Y+4
	ldd	r17,Y+3
	ldd	r26,Y+2
	ldd	r27,Y+1
	add	r28,r30
	adc	r29,__zero_reg__
	in	__tmp_reg__,__SREG__
	cli
	out	__SP_H__,r29
	out	__SREG__,__tmp_reg__
	out	__SP_L__,r28
	mov_l	r28, r26
	mov_h	r29, r27
	ret
.endfunc
#endif /* defined (L_epilogue) */

#ifdef L_exit
	.weak	_exit
	.func	_exit
_exit:
	rjmp	_exit
.endfunc
#endif /* defined (L_exit) */

#ifdef L_cleanup
	.weak	_cleanup
	.func	_cleanup
_cleanup:
	ret
.endfunc
#endif /* defined (L_cleanup) */

#ifdef L_tablejump
	.global __tablejump2__
	.func	__tablejump2__
__tablejump2__:
	lsl	r30
	rol	r31
#if defined (__AVR_ENHANCED__)
	lpm	__tmp_reg__, Z+
	lpm	r31, Z
	mov	r30, __tmp_reg__
	ijmp
#else
	lpm
	push	r0
	inc	r30	; table is word aligned, no carry to high byte
	lpm
	push	r0
	ret
#endif
.endfunc
#endif /* defined (L_tablejump) */

