pgm_read_int(): New macro.
[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
154 #elif CPU_HARVARD
155         #error Missing CPU support
156 #endif
157
158 #ifndef PSTR
159 #define PSTR            /* nothing */
160 #endif
161
162 #ifndef PROGMEM
163 #define PROGMEM         /* nothing */
164 #endif
165
166 /*!
167  * \name Types for variables stored in program memory (harvard processors).
168  * \{
169  */
170 typedef PROGMEM char pgm_char;
171 typedef PROGMEM int8_t pgm_int8_t;
172 typedef PROGMEM uint8_t pgm_uint8_t;
173 typedef PROGMEM int16_t pgm_int16_t;
174 typedef PROGMEM uint16_t pgm_uint16_t;
175 typedef PROGMEM int32_t pgm_int32_t;
176 typedef PROGMEM uint32_t pgm_uint32_t;
177 /*\}*/
178
179 /*!
180  * \name PGM support macros.
181  *
182  * These macros enable dual compilation of code for both program
183  * and data memory.
184  *
185  * Such a function may be defined like this:
186  *
187  * \code
188  *      void PGM_FUNC(lcd_puts)(PGM_ATTR const char *str)
189  *      {
190  *              char c;
191  *              while ((c = PGM_READ_CHAR(str++))
192  *                      lcd_putchar(c);
193  *      }
194  * \endcode
195  *
196  * The above code can be compiled twice: once with the _PROGMEM preprocessor
197  * symbol defined, and once without.  The two object modules can then be
198  * linked in the same application for use by client code:
199  *
200  * \code
201  *      lcd_puts("Hello, world!");
202  *      lcd_puts_P(PSTR("Hello, world!"));
203  *
204  *      // To be used when invoking inside other PGM_FUNC functions:
205  *      PGM_FUNC(lcd_puts)(some_string);
206  * \endcode
207  *
208  * \{
209  */
210 #ifdef _PROGMEM
211         #define PGM_READ_CHAR(s) pgm_read_char(s)
212         #define PGM_FUNC(x)      x ## _P
213         #define PGM_STR(x)       PSTR(x)
214         #define PGM_ATTR         PROGMEM
215 #else
216         #define PGM_READ_CHAR(s) (*(s))
217         #define PGM_FUNC(x)      x
218         #define PGM_STR(x)       x
219         #define PGM_ATTR         /* nothing */
220 #endif
221 /* \} */
222
223
224 #endif /* MWARE_PGM_H */