Convert to new Doxygen style.
[bertos.git] / mware / pgm.h
1 /**
2  * \file
3  * <!--
4  * Copyright 2005 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See README.devlib for information.
6  * -->
7  *
8  * \brief Support for reading program memory on Harvard architectures.
9  *
10  * Support is currently provided for AVR microcontrollers only.
11  *
12  * These macros allow building code twice, with and without
13  * pgm support (e.g.: strcpy() and strcpy_P()).
14  *
15  * Set the _PROGMEM predefine to compile in conditional
16  * program-memory support.
17  *
18  *
19  * \note This module contains code ripped out from avr-libc,
20  *       which is distributed under a 3-clause BSD license.
21  */
22 #ifndef MWARE_PGM_H
23 #define MWARE_PGM_H
24
25 #include <cfg/cpu_detect.h>
26 #include <cfg/compiler.h> /* For intXX_t */
27 #include <cfg/cpu.h> /* For CPU_HARVARD */
28
29 #if CPU_AVR
30
31         #ifdef __AVR_ENHANCED__
32                 #define pgm_read_char(addr) \
33                 ({ \
34                         uint16_t __addr16 = (uint16_t)(addr); \
35                         uint8_t __result; \
36                         __asm__ \
37                         ( \
38                                 "lpm %0, Z" "\n\t" \
39                                 : "=r" (__result) \
40                                 : "z" (__addr16) \
41                         ); \
42                         __result; \
43                 })
44                 #if 0 // 128/103
45                 #define pgm_read_uint16_t(addr) \
46                 ({ \
47                         uint32_t __addr32 = (uint32_t)(addr); \
48                         uint16_t __result; \
49                         __asm__ \
50                         ( \
51                                 "out %2, %C1"   "\n\t" \
52                                 "movw r30, %1"  "\n\t" \
53                                 "elpm %A0, Z+"  "\n\t" \
54                                 "elpm %B0, Z"   "\n\t" \
55                                 : "=r" (__result) \
56                                 : "r" (__addr32), \
57                                   "I" (_SFR_IO_ADDR(RAMPZ)) \
58                                 : "r30", "r31" \
59                         ); \
60                         __result; \
61                 })
62                 #endif
63
64                 #define pgm_read_uint16_t(addr) \
65                 ({ \
66                         uint16_t __addr16 = (uint16_t)(addr); \
67                         uint16_t __result; \
68                         __asm__ \
69                         ( \
70                                 "lpm %A0, Z+"   "\n\t" \
71                                 "lpm %B0, Z"    "\n\t" \
72                                 : "=r" (__result), "=z" (__addr16) \
73                                 : "1" (__addr16) \
74                         ); \
75                         __result; \
76                 })
77
78
79         #else /* !__AVR_ENHANCED__ */
80
81                 #define pgm_read_char(addr) \
82                 ({ \
83                         uint16_t __addr16 = (uint16_t)(addr); \
84                         uint8_t __result; \
85                         __asm__ \
86                         ( \
87                                 "lpm" "\n\t" \
88                                 "mov %0, r0" "\n\t" \
89                                 : "=r" (__result) \
90                                 : "z" (__addr16) \
91                                 : "r0" \
92                         ); \
93                         __result; \
94                 })
95                 #if 0 // 128/103
96                 #define pgm_read_uint16_t(addr) \
97                 ({ \
98                         uint32_t __addr32 = (uint32_t)(addr); \
99                         uint16_t __result; \
100                         __asm__ \
101                         ( \
102                                 "out %2, %C1"   "\n\t" \
103                                 "mov r31, %B1"  "\n\t" \
104                                 "mov r30, %A1"  "\n\t" \
105                                 "elpm"          "\n\t" \
106                                 "mov %A0, r0"   "\n\t" \
107                                 "in r0, %2"     "\n\t" \
108                                 "adiw r30, 1"   "\n\t" \
109                                 "adc r0, __zero_reg__" "\n\t" \
110                                 "out %2, r0"    "\n\t" \
111                                 "elpm"          "\n\t" \
112                                 "mov %B0, r0"   "\n\t" \
113                                 : "=r" (__result) \
114                                 : "r" (__addr32), \
115                                   "I" (_SFR_IO_ADDR(RAMPZ)) \
116                                 : "r0", "r30", "r31" \
117                         ); \
118                         __result; \
119                 })
120                 #endif
121                 #define pgm_read_uint16_t(addr) \
122                 ({ \
123                         uint16_t __addr16 = (uint16_t)(addr); \
124                         uint16_t __result; \
125                         __asm__ \
126                         ( \
127                                 "lpm"           "\n\t" \
128                                 "mov %A0, r0"   "\n\t" \
129                                 "adiw r30, 1"   "\n\t" \
130                                 "lpm"           "\n\t" \
131                                 "mov %B0, r0"   "\n\t" \
132                                 : "=r" (__result), "=z" (__addr16) \
133                                 : "1" (__addr16) \
134                                 : "r0" \
135                         ); \
136                         __result; \
137                 })
138
139         #endif /* !__AVR_ENHANCED__ */
140
141         #if SIZEOF_INT == 2
142                 #define pgm_read_int(addr) ((int)pgm_read_uint16_t(addr))
143         #else
144                 #error Missing support for CPU word size != 16bit
145         #endif
146
147         #ifndef PROGMEM
148         #define PROGMEM  __attribute__((__progmem__))
149         #endif
150         #ifndef PSTR
151         #define PSTR(s) ({ static const char __c[] PROGMEM = (s); &__c[0]; })
152         #endif
153         #ifndef PFUNC
154         #define PFUNC(x)      x ## _P
155         #endif
156
157 #elif CPU_HARVARD
158         #error Missing CPU support
159 #endif
160
161 #ifndef PSTR
162 #define PSTR            /* nothing */
163 #endif
164
165 #ifndef PFUNC
166 #define PFUNC(x) x
167 #endif
168
169 #ifndef PROGMEM
170 #define PROGMEM         /* nothing */
171 #endif
172
173 /**
174  * \name Types for variables stored in program memory (harvard processors).
175  * \{
176  */
177 typedef PROGMEM char pgm_char;
178 typedef PROGMEM int8_t pgm_int8_t;
179 typedef PROGMEM uint8_t pgm_uint8_t;
180 typedef PROGMEM int16_t pgm_int16_t;
181 typedef PROGMEM uint16_t pgm_uint16_t;
182 typedef PROGMEM int32_t pgm_int32_t;
183 typedef PROGMEM uint32_t pgm_uint32_t;
184 /*\}*/
185
186 /**
187  * \name PGM support macros.
188  *
189  * These macros enable dual compilation of code for both program
190  * and data memory.
191  *
192  * Such a function may be defined like this:
193  *
194  * \code
195  *      void PGM_FUNC(lcd_puts)(PGM_ATTR const char *str)
196  *      {
197  *              char c;
198  *              while ((c = PGM_READ_CHAR(str++))
199  *                      lcd_putchar(c);
200  *      }
201  * \endcode
202  *
203  * The above code can be compiled twice: once with the _PROGMEM preprocessor
204  * symbol defined, and once without.  The two object modules can then be
205  * linked in the same application for use by client code:
206  *
207  * \code
208  *      lcd_puts("Hello, world!");
209  *      lcd_puts_P(PSTR("Hello, world!"));
210  *
211  *      // To be used when invoking inside other PGM_FUNC functions:
212  *      PGM_FUNC(lcd_puts)(some_string);
213  * \endcode
214  *
215  * \{
216  */
217 #ifdef _PROGMEM
218         #define PGM_READ_CHAR(s) pgm_read_char(s)
219         #define PGM_FUNC(x)      PFUNC(x)
220         #define PGM_STR(x)       PSTR(x)
221         #define PGM_ATTR         PROGMEM
222 #else
223         #define PGM_READ_CHAR(s) (*(s))
224         #define PGM_FUNC(x)      x
225         #define PGM_STR(x)       x
226         #define PGM_ATTR         /* nothing */
227 #endif
228 /* \} */
229
230
231 #endif /* MWARE_PGM_H */