d5db5dec1a7e7c909c1243cc9dfab1d1fa251063
[bertos.git] / bertos / cpu / pgm.h
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2005, 2006, 2007, 2008 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief Support for reading program memory on Harvard architectures.
33  *
34  * Support is currently provided for AVR microcontrollers only.
35  *
36  * These macros allow building code twice, with and without
37  * pgm support (e.g.: strcpy() and strcpy_P()).
38  *
39  * Set the _PROGMEM predefine to compile in conditional
40  * program-memory support.
41  *
42  *
43  * \note This module contains code ripped out from avr-libc,
44  *       which is distributed under a 3-clause BSD license.
45  *
46  * \version $Id:$
47  * \author Bernie Innocenti <bernie@codewiz.org>
48  */
49 #ifndef MWARE_PGM_H
50 #define MWARE_PGM_H
51
52 #include <cfg/compiler.h> /* For intXX_t */
53 #include <cpu/detect.h>
54 #include <cpu/attr.h>     /* For CPU_HARVARD */
55 #include <cpu/types.h>    /* For SIZEOF_INT */
56
57 #if CPU_AVR
58
59         #ifdef __AVR_ENHANCED__
60                 #define pgm_read_char(addr) \
61                 ({ \
62                         uint16_t __addr16 = (uint16_t)(addr); \
63                         uint8_t __result; \
64                         __asm__ \
65                         ( \
66                                 "lpm %0, Z" "\n\t" \
67                                 : "=r" (__result) \
68                                 : "z" (__addr16) \
69                         ); \
70                         __result; \
71                 })
72                 #define pgm_read_uint16_t(addr) \
73                 ({ \
74                         uint16_t __addr16 = (uint16_t)(addr); \
75                         uint16_t __result; \
76                         __asm__ \
77                         ( \
78                                 "lpm %A0, Z+"   "\n\t" \
79                                 "lpm %B0, Z"    "\n\t" \
80                                 : "=r" (__result), "=z" (__addr16) \
81                                 : "1" (__addr16) \
82                         ); \
83                         __result; \
84                 })
85
86
87         #else /* !__AVR_ENHANCED__ */
88
89                 #define pgm_read_char(addr) \
90                 ({ \
91                         uint16_t __addr16 = (uint16_t)(addr); \
92                         uint8_t __result; \
93                         __asm__ \
94                         ( \
95                                 "lpm" "\n\t" \
96                                 "mov %0, r0" "\n\t" \
97                                 : "=r" (__result) \
98                                 : "z" (__addr16) \
99                                 : "r0" \
100                         ); \
101                         __result; \
102                 })
103                 #define pgm_read_uint16_t(addr) \
104                 ({ \
105                         uint16_t __addr16 = (uint16_t)(addr); \
106                         uint16_t __result; \
107                         __asm__ \
108                         ( \
109                                 "lpm"           "\n\t" \
110                                 "mov %A0, r0"   "\n\t" \
111                                 "adiw r30, 1"   "\n\t" \
112                                 "lpm"           "\n\t" \
113                                 "mov %B0, r0"   "\n\t" \
114                                 : "=r" (__result), "=z" (__addr16) \
115                                 : "1" (__addr16) \
116                                 : "r0" \
117                         ); \
118                         __result; \
119                 })
120
121         #endif /* !__AVR_ENHANCED__ */
122
123         #if SIZEOF_INT == 2
124                 #define pgm_read_int(addr) ((int)pgm_read_uint16_t(addr))
125         #else
126                 #error Missing support for CPU word size != 16bit
127         #endif
128
129         #ifndef PROGMEM
130         #define PROGMEM  __attribute__((__progmem__))
131         #endif
132         #ifndef PSTR
133         #define PSTR(s) ({ static const char __c[] PROGMEM = (s); &__c[0]; })
134         #endif
135         #ifndef PFUNC
136         #define PFUNC(x)      x ## _P
137         #endif
138
139 #elif CPU_HARVARD
140         #error Missing CPU support
141 #endif
142
143 #ifndef PSTR
144 #define PSTR            /* nothing */
145 #endif
146
147 #ifndef PFUNC
148 #define PFUNC(x) x
149 #endif
150
151 #ifndef PROGMEM
152 #define PROGMEM         /* nothing */
153 #endif
154
155 /**
156  * \name Types for variables stored in program memory (harvard processors).
157  * \{
158  */
159 typedef PROGMEM char pgm_char;
160 typedef PROGMEM int8_t pgm_int8_t;
161 typedef PROGMEM uint8_t pgm_uint8_t;
162 typedef PROGMEM int16_t pgm_int16_t;
163 typedef PROGMEM uint16_t pgm_uint16_t;
164 typedef PROGMEM int32_t pgm_int32_t;
165 typedef PROGMEM uint32_t pgm_uint32_t;
166 /*\}*/
167
168 /**
169  * \name PGM support macros.
170  *
171  * These macros enable dual compilation of code for both program
172  * and data memory.
173  *
174  * Such a function may be defined like this:
175  *
176  * \code
177  *      void PGM_FUNC(lcd_puts)(PGM_ATTR const char *str)
178  *      {
179  *              char c;
180  *              while ((c = PGM_READ_CHAR(str++))
181  *                      lcd_putchar(c);
182  *      }
183  * \endcode
184  *
185  * The above code can be compiled twice: once with the _PROGMEM preprocessor
186  * symbol defined, and once without.  The two object modules can then be
187  * linked in the same application for use by client code:
188  *
189  * \code
190  *      lcd_puts("Hello, world!");
191  *      lcd_puts_P(PSTR("Hello, world!"));
192  *
193  *      // To be used when invoking inside other PGM_FUNC functions:
194  *      PGM_FUNC(lcd_puts)(some_string);
195  * \endcode
196  *
197  * \{
198  */
199 #ifdef _PROGMEM
200         #define PGM_READ_CHAR(s) pgm_read_char(s)
201         #define PGM_FUNC(x)      PFUNC(x)
202         #define PGM_STR(x)       PSTR(x)
203         #define PGM_ATTR         PROGMEM
204 #else
205         #define PGM_READ_CHAR(s) (*(s))
206         #define PGM_FUNC(x)      x
207         #define PGM_STR(x)       x
208         #define PGM_ATTR         /* nothing */
209 #endif
210 /* \} */
211
212
213 #endif /* MWARE_PGM_H */