1/* Copyright (c) 2002, Marek Michalkiewicz
2   All rights reserved.
3
4   Redistribution and use in source and binary forms, with or without
5   modification, are permitted provided that the following conditions are met:
6
7   * Redistributions of source code must retain the above copyright
8     notice, this list of conditions and the following disclaimer.
9   * Redistributions in binary form must reproduce the above copyright
10     notice, this list of conditions and the following disclaimer in
11     the documentation and/or other materials provided with the
12     distribution.
13   * Neither the name of the copyright holders nor the names of
14     contributors may be used to endorse or promote products derived
15     from this software without specific prior written permission.
16
17  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  POSSIBILITY OF SUCH DAMAGE. */
28
29/* $Id: setjmp.S 2484 2015-08-17 06:34:41Z pitchumani $ */
30
31
32
33/*
34   setjmp.S
35
36   Contributors:
37     Created by Marek Michalkiewicz <marekm@linux.org.pl>
38 */
39
40/*
41   jmp_buf:
42	offset	size	description
43	 0	16/2	call-saved registers (r2-r17)
44				(AVR_TINY arch has only 2 call saved registers (r18,r19))
45	16/2	 2	frame pointer (r29:r28)
46	18/4	 2	stack pointer (SPH:SPL)
47	20/6	 1	status register (SREG)
48	21/7	 2/3	return address (PC) (2 bytes used for <=128Kw flash)
49	23/24/9 = total size (AVR_TINY arch always has 2 bytes PC)
50   All multibytes are stored as little-endian.
51
52   int setjmp(jmp_buf __jmpb);
53   void longjmp(jmp_buf __jmpb, int __val) __attribute__((noreturn));
54 */
55
56#ifndef	__DOXYGEN__
57
58#include <avr/io.h>
59#include "macros.inc"
60
61/* ???: What was a reason to use aliases for common registers?
62   Check the address: is it a port number (value for IN/OUT)?	*/
63#if	AVR_STACK_POINTER_LO_ADDR != 0x3D	\
64     || AVR_STATUS_ADDR != 0x3F
65# error  "Strange address of common registers SPL, SREG"
66#endif
67
68#define jmpb_hi	r25
69#define jmpb_lo	r24
70#define val_hi	r23
71#define val_lo	r22
72
73#define ret_lo	r24
74#define ret_hi	r25
75
76	ASSEMBLY_CLIB_SECTION
77
78	.global _U(setjmp)
79	.type	_U(setjmp), @function
80
81_U(setjmp):
82	X_movw	XL, jmpb_lo
83  ; save call-saved registers and frame pointer
84#if !defined(__AVR_TINY__)
85	.irp	.L_regno, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,28,29
86#else
87	.irp	.L_regno, 18,19,28,29
88#endif
89	st	X+, r\.L_regno
90	.endr
91  ; get return address
92#if  defined(__AVR_3_BYTE_PC__) && __AVR_3_BYTE_PC__
93	pop	__tmp_reg__		; used below
94#endif
95	pop	ZH
96	pop	ZL
97  ; save stack pointer (after poping)
98	in	ret_lo, AVR_STACK_POINTER_LO_ADDR
99	st	X+, ret_lo
100#ifdef _HAVE_AVR_STACK_POINTER_HI
101	in	ret_lo, AVR_STACK_POINTER_HI_ADDR
102	st	X+, ret_lo
103#else
104	st	X+, __zero_reg__
105#endif
106  ; save status register (I flag)
107	in	ret_lo, AVR_STATUS_ADDR
108	st	X+, ret_lo
109  ; save return address
110	st	X+, ZL
111	st	X+, ZH
112  ; return zero
113	clr	ret_lo
114	clr	ret_hi
115#if  defined(__AVR_3_BYTE_PC__) && __AVR_3_BYTE_PC__
116	st	X+, __tmp_reg__
117	rjmp	.L_jmp3
118#else
119	ijmp
120#endif
121	.size	_U(setjmp), . - _U(setjmp)
122
123	.global _U(longjmp)
124	.type	_U(longjmp), @function
125
126_U(longjmp):
127	X_movw	XL, jmpb_lo
128  ; return value
129	X_movw	ret_lo, val_lo
130  ; if zero, change to 1
131	cpi	ret_lo, 1
132	cpc	ret_hi, __zero_reg__
133	adc	ret_lo, __zero_reg__
134  ; restore call-saved registers and frame pointer
135#if !defined(__AVR_TINY__)
136	.irp	.L_regno, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,28,29
137#else
138	.irp	.L_regno, 18,19,28,29
139#endif
140	ld	r\.L_regno, X+
141	.endr
142  ; restore stack pointer (SP value before the setjmp() call) and SREG
143	ld	ZL, X+
144	ld	ZH, X+
145	ld	__tmp_reg__, X+
146#if  defined (__AVR_XMEGA__) && __AVR_XMEGA__
147	/* A write to SPL will automatically disable interrupts for up to 4
148	   instructions or until the next I/O memory write.	*/
149	out	AVR_STATUS_ADDR, __tmp_reg__
150	out	AVR_STACK_POINTER_LO_ADDR, ZL
151	out	AVR_STACK_POINTER_HI_ADDR, ZH
152#else
153# ifdef _HAVE_AVR_STACK_POINTER_HI
154	/* interrupts disabled for shortest possible time (3 cycles) */
155	cli
156	out	AVR_STACK_POINTER_HI_ADDR, ZH
157# endif
158	/* Restore status register (including the interrupt enable flag).
159	   Interrupts are re-enabled only after the next instruction.  */
160	out	AVR_STATUS_ADDR, __tmp_reg__
161	out	AVR_STACK_POINTER_LO_ADDR, ZL
162#endif
163  ; get return address and jump
164	ld	ZL, X+
165	ld	ZH, X+
166#if  defined(__AVR_3_BYTE_PC__) && __AVR_3_BYTE_PC__
167	ld	__tmp_reg__, X+
168.L_jmp3:
169	push	ZL
170	push	ZH
171	push	__tmp_reg__
172	ret
173#else
174	ijmp
175#endif
176	.size	_U(longjmp), . - _U(longjmp)
177
178#endif	/* !__DOXYGEN__ */
179