--- /dev/null
+/* -*- Mode: Asm -*- */
+/* Copyright (C) 1998-2026 Free Software Foundation, Inc.
+ Contributed by Denis Chertykov <chertykov@gmail.com>
+
+ 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 3, or (at your option) any
+ later version.
+
+ 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.
+
+ 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/>. */
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Definitions for common SFRs and fixed registers.
+
+#if defined (__AVR_TINY__)
+#define __zero_reg__ r17
+#define __tmp_reg__ r16
+#else
+#define __zero_reg__ r1
+#define __tmp_reg__ r0
+#endif
+
+#if defined (__AVR_HAVE_SPH__)
+#define __SP_H__ 0x3e
+#endif
+
+#define __SP_L__ 0x3d
+#define __RAMPZ__ 0x3B
+#define __EIND__ 0x3C
+#define __SREG__ 0x3f
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Helper macros.
+
+;;; REGNO sets symbol SYM to the register number of REG.
+;;; Valid REGs are: r0..r31, R0..R31 and XL, yh, ZH etc.
+;;; Expressions that evaluate to a number can also be used.
+.macro REGNO sym, reg
+ ..regno.done = 0
+ ..regno = 0
+ .irp name, \
+ r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, \
+ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
+ r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \
+ r30, r31, \
+ R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, \
+ R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, \
+ R20, R21, R22, R23, R24, R25, R26, R27, R28, R29, \
+ R30, R31
+ .ifc \name,\reg
+ \sym = ..regno % 32
+ ..regno.done = 1
+ .endif
+ ..regno = ..regno + 1
+ .endr
+
+ ..regno = 0
+ .irp name, \
+ xl, xh, yl, yh, zl, zh, \
+ Xl, Xh, Yl, Yh, Zl, Zh, \
+ xL, xH, yL, yH, zL, zH, \
+ XL, XH, YL, YH, ZL, ZH
+ .ifc \name,\reg
+ \sym = 26 + ..regno % 6
+ ..regno.done = 1
+ .endif
+ ..regno = ..regno + 1
+ .endr
+
+ .if ! ..regno.done
+ ;; Assume that \reg is some valid numeric expression.
+ \sym = \reg
+ .endif
+.endm
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Definitions to factor out different ISAs.
+
+#if defined (__AVR_HAVE_JMP_CALL__)
+#define XCALL call
+#define XJMP jmp
+#else
+#define XCALL rcall
+#define XJMP rjmp
+#endif
+
+#if defined (__AVR_HAVE_EIJMP_EICALL__)
+#define XICALL eicall
+#define XIJMP eijmp
+#else
+#define XICALL icall
+#define XIJMP ijmp
+#endif
+
+
+;;; Like MOVW
+#ifdef __AVR_HAVE_MOVW__
+#define wmov movw
+#else
+.macro wmov dst, src
+ REGNO ..wmov.dst, \dst
+ REGNO ..wmov.src, \src
+ mov ..wmov.dst+0, ..wmov.src+0
+ mov ..wmov.dst+1, ..wmov.src+1
+.endm
+#endif /* __AVR_HAVE_MOVW__ */
+
+
+;;; Like SBIW, ADIW
+#ifdef __AVR_HAVE_ADIW__
+#define waddi adiw
+#define wsubi sbiw
+#else
+.macro wsubi reg, arg
+ REGNO ..wsubi.reg, \reg
+ subi ..wsubi.reg+0, lo8 (\arg)
+ sbci ..wsubi.reg+1, hi8 (\arg)
+.endm
+
+.macro waddi reg, arg
+ REGNO ..waddi.reg, \reg
+ subi ..waddi.reg+0, lo8 (-(\arg))
+ sbci ..waddi.reg+1, hi8 (-(\arg))
+.endm
+#endif /* __AVR_HAVE_ADIW__ */
+
+
+;;; [R]JMP to label LABL when REG is positive (REG.7 = 0).
+;;; Otherwise, fallthrough.
+.macro .branch_plus reg, labl
+#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
+ ;; Some cores have a problem skipping 2-word instructions.
+ tst \reg
+ brmi .L.branch_plus.\@
+#else
+ sbrs \reg, 7
+#endif /* skip erratum */
+ XJMP \labl
+.L.branch_plus.\@:
+.endm ; .branch_plus
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Macros for convenience.
+
+;; Skip the next instruction, typically a jump target.
+#define skip cpse 16,16
+
+
+.macro mov4 dst, src
+ REGNO ..mov4.dst, \dst
+ REGNO ..mov4.src, \src
+ .if ..mov4.dst < ..mov4.src
+ wmov ..mov4.dst+0, ..mov4.src+0
+ wmov ..mov4.dst+2, ..mov4.src+2
+ .else
+ wmov ..mov4.dst+2, ..mov4.src+2
+ wmov ..mov4.dst+0, ..mov4.src+0
+ .endif
+.endm
+
+
+;; Negate a 2-byte value held in consecutive registers.
+.macro NEG2 reg
+ com \reg+1
+ neg \reg
+ sbci \reg+1, -1
+.endm
+
+
+;; Negate a 4-byte value held in consecutive registers.
+;; Sets the V flag for signed overflow tests if REG >= 16.
+.macro NEG4 reg
+ com \reg+3
+ com \reg+2
+ com \reg+1
+.if \reg >= 16
+ neg \reg
+ sbci \reg+1, -1
+ sbci \reg+2, -1
+ sbci \reg+3, -1
+.else
+ com \reg
+ adc \reg, __zero_reg__
+ adc \reg+1, __zero_reg__
+ adc \reg+2, __zero_reg__
+ adc \reg+3, __zero_reg__
+.endif
+.endm
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Function entry and exit.
+
+.macro DEFUN name
+ .global \name
+ .func \name
+ \name:
+.endm
+
+.macro ENDF name
+ .size \name, .-\name
+ .endfunc
+.endm
+
+.macro FALIAS name
+ .global \name
+ .func \name
+ \name:
+ .size \name, .-\name
+ .endfunc
+.endm
+
+
+#ifndef __AVR_TINY__
+
+;;; Prologue stuff
+.macro do_prologue_saves n_pushed n_frame=0
+ ldi r26, lo8 (\n_frame)
+ ldi r27, hi8 (\n_frame)
+ ldi r30, lo8 (gs(.L_prologue_saves.\@))
+ ldi r31, hi8 (gs(.L_prologue_saves.\@))
+ XJMP __prologue_saves__ + ((18 - (\n_pushed)) * 2)
+.L_prologue_saves.\@:
+.endm
+
+
+;; Epilogue stuff
+.macro do_epilogue_restores n_pushed n_frame=0
+ in r28, __SP_L__
+#ifdef __AVR_HAVE_SPH__
+ in r29, __SP_H__
+.if \n_frame > 63
+ subi r28, lo8 (-(\n_frame))
+ sbci r29, hi8 (-(\n_frame))
+.elseif \n_frame > 0
+ adiw r28, \n_frame
+.endif
+#else
+ clr r29
+.if \n_frame > 0
+ subi r28, lo8 (-(\n_frame))
+.endif
+#endif /* HAVE SPH */
+ ldi r30, \n_pushed
+ XJMP __epilogue_restores__ + ((18 - (\n_pushed)) * 2)
+.endm
+
+#endif /* AVR_TINY */
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
-#if defined (__AVR_TINY__)
-#define __zero_reg__ r17
-#define __tmp_reg__ r16
-#else
-#define __zero_reg__ r1
-#define __tmp_reg__ r0
-#endif
-#define __SREG__ 0x3f
-#if defined (__AVR_HAVE_SPH__)
-#define __SP_H__ 0x3e
-#endif
-#define __SP_L__ 0x3d
-#define __RAMPZ__ 0x3B
-#define __EIND__ 0x3C
+#include "asm-defs.h"
/* 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. */
-
-/* FIXME: At present, there is no SORT directive in the linker
- script so that we must not assume that different modules
- in the same input section like .libgcc.text.mul will be
- located close together. Therefore, we cannot use
- RCALL/RJMP to call a function like __udivmodhi4 from
- __divmodhi4 and have to use lengthy XCALL/XJMP even
- though they are in the same input section and all same
- input sections together are small enough to reach every
- location with a RCALL/RJMP instruction. */
+ of the call-used registers (not all of them) are clobbered.
+ Some functions are using a non-ABI interface. */
#if defined (__AVR_HAVE_EIJMP_EICALL__) && !defined (__AVR_HAVE_ELPMX__)
#error device not supported
#endif
- .macro mov_l r_dest, r_src
-#if defined (__AVR_HAVE_MOVW__)
- movw \r_dest, \r_src
-#else
- mov \r_dest, \r_src
-#endif
- .endm
-
- .macro mov_h r_dest, r_src
-#if defined (__AVR_HAVE_MOVW__)
- ; empty
-#else
- mov \r_dest, \r_src
-#endif
- .endm
-
-.macro wmov r_dest, r_src
-#if defined (__AVR_HAVE_MOVW__)
- movw \r_dest, \r_src
-#else
- mov \r_dest, \r_src
- mov \r_dest+1, \r_src+1
-#endif
-.endm
-
-.macro mov4 r_dest, r_src
- wmov \r_dest, \r_src
- wmov \r_dest+2, \r_src+2
-.endm
-
-#if defined (__AVR_HAVE_JMP_CALL__)
-#define XCALL call
-#define XJMP jmp
-#else
-#define XCALL rcall
-#define XJMP rjmp
-#endif
-
-#if defined (__AVR_HAVE_EIJMP_EICALL__)
-#define XICALL eicall
-#define XIJMP eijmp
-#else
-#define XICALL icall
-#define XIJMP ijmp
-#endif
-
-;;; [R]JMP to label \labl when \reg is positive (\reg.7 = 0).
-;;; Otherwise, fallthrough.
-.macro .branch_plus reg, labl
-#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
- ;; Some cores have a problem skipping 2-word instructions
- tst \reg
- brmi .L..\@
-#else
- sbrs \reg, 7
-#endif /* skip erratum */
- XJMP \labl
-.L..\@:
-.endm ; .branch_plus
-
-;; Prologue stuff
-
-.macro do_prologue_saves n_pushed n_frame=0
- ldi r26, lo8(\n_frame)
- ldi r27, hi8(\n_frame)
- ldi r30, lo8(gs(.L_prologue_saves.\@))
- ldi r31, hi8(gs(.L_prologue_saves.\@))
- XJMP __prologue_saves__ + ((18 - (\n_pushed)) * 2)
-.L_prologue_saves.\@:
-.endm
-
-;; Epilogue stuff
-
-.macro do_epilogue_restores n_pushed n_frame=0
- in r28, __SP_L__
-#ifdef __AVR_HAVE_SPH__
- in r29, __SP_H__
-.if \n_frame > 63
- subi r28, lo8(-\n_frame)
- sbci r29, hi8(-\n_frame)
-.elseif \n_frame > 0
- adiw r28, \n_frame
-.endif
-#else
- clr r29
-.if \n_frame > 0
- subi r28, lo8(-\n_frame)
-.endif
-#endif /* HAVE SPH */
- ldi r30, \n_pushed
- XJMP __epilogue_restores__ + ((18 - (\n_pushed)) * 2)
-.endm
-
-;; Support function entry and exit for convenience
-
-.macro wsubi r_arg1, i_arg2
-#if defined (__AVR_TINY__)
- subi \r_arg1, lo8(\i_arg2)
- sbci \r_arg1+1, hi8(\i_arg2)
-#else
- sbiw \r_arg1, \i_arg2
-#endif
-.endm
-
-.macro waddi r_arg1, i_arg2
-#if defined (__AVR_TINY__)
- subi \r_arg1, lo8(-\i_arg2)
- sbci \r_arg1+1, hi8(-\i_arg2)
-#else
- adiw \r_arg1, \i_arg2
-#endif
-.endm
-
-.macro DEFUN name
-.global \name
-.func \name
-\name:
-.endm
-
-.macro ENDF name
-.size \name, .-\name
-.endfunc
-.endm
-
-.macro FALIAS name
-.global \name
-.func \name
-\name:
-.size \name, .-\name
-.endfunc
-.endm
-
-;; Skip next instruction, typically a jump target
-#define skip cpse 16,16
-
-;; Negate a 2-byte value held in consecutive registers
-.macro NEG2 reg
- com \reg+1
- neg \reg
- sbci \reg+1, -1
-.endm
-
-;; Negate a 4-byte value held in consecutive registers
-;; Sets the V flag for signed overflow tests if REG >= 16
-.macro NEG4 reg
- com \reg+3
- com \reg+2
- com \reg+1
-.if \reg >= 16
- neg \reg
- sbci \reg+1, -1
- sbci \reg+2, -1
- sbci \reg+3, -1
-.else
- com \reg
- adc \reg, __zero_reg__
- adc \reg+1, __zero_reg__
- adc \reg+2, __zero_reg__
- adc \reg+3, __zero_reg__
-.endif
-.endm
-
#define exp_lo(N) hlo8 ((N) << 23)
#define exp_hi(N) hhi8 ((N) << 23)
breq __mulqi3_exit ; while multiplicand != 0
lsr r_arg1 ;
brne __mulqi3_loop ; exit if multiplier = 0
-__mulqi3_exit:
+__mulqi3_exit:
mov r_arg1,r_res ; result to return register
ret
ENDF __mulqi3
#undef r_arg2
#undef r_arg1
#undef r_res
-
+
#endif /* defined (L_mulqi3) */
wmov C2, CC2
#if defined (__AVR_TINY__)
pop B1 ; restore callee saved regs
- pop B0
+ pop B0
#endif /* defined (__AVR_TINY__) */
ret
;;; (C3:C0) = (signed long) A1:A0 * B3:B0
;;; Clobbers: __tmp_reg__
DEFUN __mulshisi3
-#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
- ;; Some cores have problem skipping 2-word instruction
- tst A1
- brmi __mulohisi3
-#else
- sbrs A1, 7
-#endif /* __AVR_HAVE_JMP_CALL__ */
- XJMP __muluhisi3
+ .branch_plus A1, __muluhisi3
;; FALLTHRU
ENDF __mulshisi3
DEFUN __mulpsi3
#if defined (__AVR_TINY__)
- in r26,__SP_L__
+ in r26,__SP_L__
in r27,__SP_H__
subi r26, lo8(-3) ; Add 3 to point past return address
sbci r27, hi8(-3)
push B0 ; save callee saved regs
push B1
- ld B0,X+ ; load from caller stack
+ ld B0,X+ ; load from caller stack
ld B1,X+
ld B2,X+
#endif /* defined (__AVR_TINY__) */
#if defined (__AVR_HAVE_MUL__)
#define A0 r22
-#define A1 r23
+#define A1 r23
#define A2 r24
#define A3 r25
-
+
#define B0 r18
#define B1 r19
#define B2 r20
#define B3 r21
-
+
#define C0 18
#define C1 C0+1
#define C2 20
#endif /* L_mulsidi3 && !HAVE_MUL */
#endif /* if not __AVR_TINY__ */
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
+
\f
.section .text.libgcc.div, "ax", @progbits
#undef r_arg1
#undef r_arg2
#undef r_cnt
-
-
+
+
/*******************************************************
Division 16 / 16 => (result + remainder)
*******************************************************/
/* return: quotient */
#define r_arg2L r22 /* divisor Low */
#define r_arg2H r23 /* divisor High */
-
+
#define r_cnt r21 /* loop count */
#if defined (L_udivmodhi4)
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
+ wmov r_arg2L, r_arg1L ; quotient
+ wmov r_arg1L, r_remL ; remainder
ret
ENDF __udivmodhi4
#endif /* defined (L_udivmodhi4) */
#undef r_arg2H
#undef r_arg2L
-
-#undef r_cnt
+
+#undef r_cnt
/*******************************************************
Division 24 / 24 => (result + remainder)
#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)
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
+ wmov r_remHL, r_remL
rjmp __udivmodsi4_ep ; jump to entry point
__udivmodsi4_loop:
rol r_remL ; shift dividend into remainder
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
+ wmov r_arg2L, r_arg1L ; quotient
+ wmov r_arg2HL, r_arg1HL
+ wmov r_arg1L, r_remL ; remainder
+ wmov r_arg1HL, r_remHL
ret
ENDF __udivmodsi4
#endif /* defined (L_udivmodsi4) */
out __SP_H__,r29
out __SREG__,__tmp_reg__
out __SP_L__,r28
- mov_l r28, r26
- mov_h r29, r27
+ wmov r28, r26
#endif /* #SP = 8/16 */
ret
ENDF __epilogue_restores__
wsubi 30, -(__AVR_TINY_PM_BASE_ADDRESS__) ; Add PM offset to Z
ld __tmp_reg__, Z+
ld r31, Z ; Use ld instead of lpm to load Z
- mov r30, __tmp_reg__
+ mov r30, __tmp_reg__
ijmp
#else
lpm
sbc r16, __zero_reg__
mov r24, r16
#endif /* HAVE_EIJMP */
- mov_h r31, r29
- mov_l r30, r28
+ wmov r30, r28
XCALL __tablejump2__
.L__do_global_ctors_start:
cpi r28, pm_lo8(__ctors_start)
#ifdef __AVR_HAVE_EIJMP_EICALL__
mov r24, r16
#endif /* HAVE_EIJMP */
- mov_h r31, r29
- mov_l r30, r28
+ wmov r30, r28
XCALL __tablejump2__
waddi 28, 1
#ifdef __AVR_HAVE_EIJMP_EICALL__
#if !defined (__AVR_TINY__)
#if defined (L_strlen_memx)
DEFUN __strlen_memx
-#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
- tst r24
- brmi 1f
-#else
- sbrs r24, 7
-#endif
- XJMP strlen_PF ; AVR-LibC
-1: wmov 24, 22
+ .branch_plus r24, strlen_PF ; AVR-LibC
+ wmov 24, 22
XJMP strlen ; AVR-LibC
ENDF __strlen_memx
#endif /* L_strlen_memx */
#undef LOOP
#endif /* L_movmemf */
-#endif /* !defined (__AVR_TINY__) */
+#endif /* !defined (__AVR_TINY__) */
\f
.section .text.libgcc.builtins, "ax", @progbits
XCALL __clzsi2
sbrs r24, 5
ret
- mov_l r22, r18
- mov_h r23, r19
- mov_l r24, r20
- mov_h r25, r21
+ wmov r22, r18
+ wmov r24, r20
XCALL __clzsi2
subi r24, -32
ret
XCALL __clzhi2
sbrs r24, 4
ret
- mov_l r24, r22
- mov_h r25, r23
+ wmov r24, r22
XCALL __clzhi2
subi r24, -16
ret
DEFUN __popcountsi2
XCALL __popcounthi2
push r24
- mov_l r24, r22
- mov_h r25, r23
+ wmov r24, r22
XCALL __popcounthi2
XJMP __popcounthi2_tail
ENDF __popcountsi2
DEFUN __popcountdi2
XCALL __popcountsi2
push r24
- mov_l r22, r18
- mov_h r23, r19
- mov_l r24, r20
- mov_h r25, r21
+ wmov r22, r18
+ wmov r24, r20
XCALL __popcountsi2
XJMP __popcounthi2_tail
ENDF __popcountdi2
;; A1 = |A1|
sbrc A1, 7
neg A1
-#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
- ;; Some cores have problem skipping 2-word instruction
- tst A0
- brmi 1f
-#else
- sbrs A0, 7
-#endif /* __AVR_HAVE_JMP_CALL__ */
- XJMP __fmul
-1: XCALL __fmul
+ .branch_plus A0, __fmul
+ XCALL __fmul
;; C = -C iff A0.7 = 1
NEG2 C0
ret