Use new CPU-neutral program-memory API.
[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 <cpu_detect.h>
26
27 #if CPU_AVR
28
29         #ifdef __AVR_ENHANCED__
30                 #define pgm_read_char(addr) \
31                 ({ \
32                         uint16_t __addr16 = (uint16_t)(addr); \
33                         uint8_t __result; \
34                         __asm__ \
35                         ( \
36                                 "lpm %0, Z" "\n\t" \
37                                 : "=r" (__result) \
38                                 : "z" (__addr16) \
39                         ); \
40                         __result; \
41                 })
42                 #if 0 // 128/103
43                 #define pgm_read_uint16_t(addr) \
44                 ({ \
45                         uint32_t __addr32 = (uint32_t)(addr); \
46                         uint16_t __result; \
47                         __asm__ \
48                         ( \
49                                 "out %2, %C1"   "\n\t" \
50                                 "movw r30, %1"  "\n\t" \
51                                 "elpm %A0, Z+"  "\n\t" \
52                                 "elpm %B0, Z"   "\n\t" \
53                                 : "=r" (__result) \
54                                 : "r" (__addr32), \
55                                   "I" (_SFR_IO_ADDR(RAMPZ)) \
56                                 : "r30", "r31" \
57                         ); \
58                         __result; \
59                 })
60                 #endif
61
62                 #define pgm_read_uint16_t(addr) \
63                 ({ \
64                         uint16_t __addr16 = (uint16_t)(addr); \
65                         uint16_t __result; \
66                         __asm__ \
67                         ( \
68                                 "lpm %A0, Z+"   "\n\t" \
69                                 "lpm %B0, Z"    "\n\t" \
70                                 : "=r" (__result), "=z" (__addr16) \
71                                 : "1" (__addr16) \
72                         ); \
73                         __result; \
74                 })
75
76
77         #else
78                 #define pgm_read_char(addr) \
79                 ({ \
80                         uint16_t __addr16 = (uint16_t)(addr); \
81                         uint8_t __result; \
82                         __asm__ \
83                         ( \
84                                 "lpm" "\n\t" \
85                                 "mov %0, r0" "\n\t" \
86                                 : "=r" (__result) \
87                                 : "z" (__addr16) \
88                                 : "r0" \
89                         ); \
90                         __result; \
91                 })
92                 #if 0 // 128/103
93                 #define pgm_read_uint16_t(addr) \
94                 ({ \
95                         uint32_t __addr32 = (uint32_t)(addr); \
96                         uint16_t __result; \
97                         __asm__ \
98                         ( \
99                                 "out %2, %C1"   "\n\t" \
100                                 "mov r31, %B1"  "\n\t" \
101                                 "mov r30, %A1"  "\n\t" \
102                                 "elpm"          "\n\t" \
103                                 "mov %A0, r0"   "\n\t" \
104                                 "in r0, %2"     "\n\t" \
105                                 "adiw r30, 1"   "\n\t" \
106                                 "adc r0, __zero_reg__" "\n\t" \
107                                 "out %2, r0"    "\n\t" \
108                                 "elpm"          "\n\t" \
109                                 "mov %B0, r0"   "\n\t" \
110                                 : "=r" (__result) \
111                                 : "r" (__addr32), \
112                                   "I" (_SFR_IO_ADDR(RAMPZ)) \
113                                 : "r0", "r30", "r31" \
114                         ); \
115                         __result; \
116                 })
117                 #endif
118                 #define pgm_read_uint16_t(addr) \
119                 ({ \
120                         uint16_t __addr16 = (uint16_t)(addr); \
121                         uint16_t __result; \
122                         __asm__ \
123                         ( \
124                                 "lpm"           "\n\t" \
125                                 "mov %A0, r0"   "\n\t" \
126                                 "adiw r30, 1"   "\n\t" \
127                                 "lpm"           "\n\t" \
128                                 "mov %B0, r0"   "\n\t" \
129                                 : "=r" (__result), "=z" (__addr16) \
130                                 : "1" (__addr16) \
131                                 : "r0" \
132                         ); \
133                         __result; \
134                 })
135
136         #endif
137
138         #define PROGMEM  __attribute__((__progmem__))
139         #define PSTR(s) ({ static const char __c[] PROGMEM = (s); &__c[0]; })
140
141 #elif CPU_HARVARD
142         #error Missing CPU support
143 #endif
144
145 #ifndef PSTR
146 #define PSTR            /* nothing */
147 #endif
148
149 #ifndef PROGMEM
150 #define PROGMEM                /* nothing */
151 #endif
152
153 /*!
154  * \name Types for variables stored in program memory (harvard processors).
155  * \{
156  */
157 typedef PROGMEM char pgm_char;
158 typedef PROGMEM int8_t pgm_int8_t;
159 typedef PROGMEM uint8_t pgm_uint8_t;
160 typedef PROGMEM int16_t pgm_int16_t;
161 typedef PROGMEM uint16_t pgm_uint16_t;
162 typedef PROGMEM int32_t pgm_int32_t;
163 typedef PROGMEM uint32_t pgm_uint32_t;
164 /*\}*/
165
166 /*!
167  * \def PGM_READ_CHAR
168  * \def PGM_FUNC
169  * \def PGM_ATTR
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 #ifdef _PROGMEM
198         #define PGM_READ_CHAR(s) pgm_read_char(s)
199         #define PGM_FUNC(x)      x ## _P
200         #define PGM_ATTR         PROGMEM
201 #else
202         #define PGM_READ_CHAR(s) (*(s))
203         #define PGM_FUNC(x)      x
204         #define PGM_ATTR         /* nothing */
205 #endif
206
207
208 #endif /* MWARE_PGM_H */