mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
* Refactor asm tabs for `.S` cosmetics * Avoid fixed regname (`a14`) allocation * Remove unnecessary explicit cast and word masking
178 lines
7.0 KiB
C++
178 lines
7.0 KiB
C++
/* PGMSPACE.H - Accessor utilities/types for accessing PROGMEM data */
|
|
|
|
#ifndef _PGMSPACE_H_
|
|
#define _PGMSPACE_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifndef ICACHE_RODATA_ATTR
|
|
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
|
|
#endif
|
|
#ifndef PROGMEM
|
|
// The following two macros cause a parameter to be enclosed in quotes
|
|
// by the preopressor (i.e. for concatenating ints to strings)
|
|
#define __STRINGIZE_NX(A) #A
|
|
#define __STRINGIZE(A) __STRINGIZE_NX(A)
|
|
// Since __section__ is supposed to be only use for global variables,
|
|
// there could be conflicts when a static/inlined function has them in the
|
|
// same file as a non-static PROGMEM object.
|
|
// Ref: https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html
|
|
// Place each progmem object into its own named section, avoiding conflicts
|
|
#define PROGMEM __attribute__((section( "\".irom.text." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\"")))
|
|
#endif
|
|
#ifndef PGM_P
|
|
#define PGM_P const char *
|
|
#endif
|
|
#ifndef PGM_VOID_P
|
|
#define PGM_VOID_P const void *
|
|
#endif
|
|
|
|
#ifndef PSTR_ALIGN
|
|
// PSTR() macro starts by default on a 32-bit boundary. This adds on average
|
|
// 1.5 bytes/string, but in return memcpy_P and strcpy_P will work 4~8x faster
|
|
// Allow users to override the alignment with PSTR_ALIGN
|
|
#define PSTR_ALIGN 4
|
|
#endif
|
|
#ifndef PSTRN
|
|
// Multi-alignment variant of PSTR, n controls the alignment and should typically be 1 or 4
|
|
// Adapted from AVR-specific code at https://forum.arduino.cc/index.php?topic=194603.0
|
|
// Uses C attribute section instead of ASM block to allow for C language string concatenation ("x" "y" === "xy")
|
|
#define PSTRN(s,n) (__extension__({static const char __pstr__[] __attribute__((__aligned__(n))) __attribute__((section( "\".irom0.pstr." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\", \"aSM\", @progbits, 1 #"))) = (s); &__pstr__[0];}))
|
|
#endif
|
|
#ifndef PSTR
|
|
// PSTR() uses the default alignment defined by PSTR_ALIGN
|
|
#define PSTR(s) PSTRN(s,PSTR_ALIGN)
|
|
#endif
|
|
#ifndef PSTR4
|
|
// PSTR4() enforces 4-bytes alignment whatever the value of PSTR_ALIGN
|
|
// as required by functions like ets_strlen() or ets_memcpy()
|
|
#define PSTR4(s) PSTRN(s,4)
|
|
#endif
|
|
|
|
// Flash memory must be read using 32 bit aligned addresses else a processor
|
|
// exception will be triggered.
|
|
// The order within the 32 bit values are:
|
|
// --------------
|
|
// b3, b2, b1, b0
|
|
// w1, w0
|
|
|
|
#define pgm_read_with_offset(addr, res) \
|
|
__asm__( \
|
|
"extui\t%0, %1, 0, 2\n\t" /* Extract offset within word (in bytes) */ \
|
|
"sub\t%1, %1, %0\n\t" /* Subtract offset from addr, yielding an aligned address */ \
|
|
"l32i.n\t%1, %1, 0\n\t" /* Load word from aligned address */ \
|
|
"ssa8l\t%0\n\t" /* Prepare to shift by offset (in bits) */ \
|
|
"src\t%0, %1, %1" /* Shift right; now the requested byte is the first one */ \
|
|
: "=r"(res), "+r"(addr))
|
|
|
|
#define pgm_read_dword_with_offset(addr, res) \
|
|
do { \
|
|
uint32_t temp; \
|
|
__asm__( \
|
|
"extui\t%0, %1, 0, 2\n\t" /* Extract offset within word (in bytes) */ \
|
|
"sub\t%1, %1, %0\n\t" /* Subtract offset from addr, yielding an aligned address */ \
|
|
"l32i.n\t%2, %1, 0\n\t" /* Load 2 words */ \
|
|
"l32i.n\t%1, %1, 4\n\t" /* from aligned address */ \
|
|
"ssa8l\t%0\n\t" /* Prepare to shift by offset (in bits) */ \
|
|
"src\t%0, %1, %2" /* Shift right in order to extract the requested dword */ \
|
|
: "=r"(res), "+r"(addr), "=r"(temp)); \
|
|
} while(0)
|
|
|
|
static inline uint8_t pgm_read_byte_inlined(const void* addr) {
|
|
uint32_t res;
|
|
pgm_read_with_offset(addr, res);
|
|
return res; /* Implicit cast to uint8_t masks the lower byte from the returned word */
|
|
}
|
|
|
|
/* Although this says "word", it's actually 16 bit, i.e. half word on Xtensa */
|
|
static inline uint16_t pgm_read_word_inlined(const void* addr) {
|
|
uint32_t res;
|
|
pgm_read_with_offset(addr, res);
|
|
return res; /* Implicit cast to uint16_t masks the lower half-word from the returned word */
|
|
}
|
|
|
|
/* Can't legally cast bits of uint32_t to a float w/o conversion or std::memcpy, which is inefficient. */
|
|
/* The ASM block doesn't care the type, so just pass in what C thinks is a float and return in custom fcn. */
|
|
static inline float pgm_read_float_unaligned(const void* addr) {
|
|
float res;
|
|
pgm_read_with_offset(addr, res);
|
|
return res;
|
|
}
|
|
|
|
#define pgm_read_byte(addr) pgm_read_byte_inlined(addr)
|
|
#define pgm_read_word_aligned(addr) pgm_read_word_inlined(addr)
|
|
#ifdef __cplusplus
|
|
#define pgm_read_dword_aligned(addr) (*reinterpret_cast<const uint32_t*>(addr))
|
|
#define pgm_read_float_aligned(addr) (*reinterpret_cast<const float*>(addr))
|
|
#define pgm_read_ptr_aligned(addr) (*reinterpret_cast<const void* const*>(addr))
|
|
#else
|
|
#define pgm_read_dword_aligned(addr) (*(const uint32_t*)(addr))
|
|
#define pgm_read_float_aligned(addr) (*(const float*)(addr))
|
|
#define pgm_read_ptr_aligned(addr) (*(const void* const*)(addr))
|
|
#endif
|
|
|
|
static inline uint32_t pgm_read_dword_unaligned(const void *addr) {
|
|
uint32_t res;
|
|
pgm_read_dword_with_offset(addr, res);
|
|
return res;
|
|
}
|
|
|
|
#define pgm_read_ptr_unaligned(addr) ((void*)pgm_read_dword_unaligned(addr))
|
|
#define pgm_read_word_unaligned(addr) ((uint16_t)pgm_read_dword_unaligned(addr))
|
|
|
|
|
|
// Allow selection of _aligned or _unaligned, but default to _unaligned for Arduino compatibility
|
|
// Add -DPGM_READ_UNALIGNED=0 or "#define PGM_READ_UNALIGNED 0" to code to use aligned-only (faster) macros by default
|
|
#ifndef PGM_READ_UNALIGNED
|
|
#define PGM_READ_UNALIGNED 1
|
|
#endif
|
|
|
|
#if PGM_READ_UNALIGNED
|
|
#define pgm_read_word(a) pgm_read_word_unaligned(a)
|
|
#define pgm_read_dword(a) pgm_read_dword_unaligned(a)
|
|
#define pgm_read_float(a) pgm_read_float_unaligned(a)
|
|
#define pgm_read_ptr(a) pgm_read_ptr_unaligned(a)
|
|
#else
|
|
#define pgm_read_word(a) pgm_read_word_aligned(a)
|
|
#define pgm_read_dword(a) pgm_read_dword_aligned(a)
|
|
#define pgm_read_float(a) pgm_read_float_aligned(a)
|
|
#define pgm_read_ptr(a) pgm_read_ptr_aligned(a)
|
|
#endif
|
|
|
|
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
|
|
#define pgm_read_word_near(addr) pgm_read_word(addr)
|
|
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
|
|
#define pgm_read_float_near(addr) pgm_read_float(addr)
|
|
#define pgm_read_ptr_near(addr) pgm_read_ptr(addr)
|
|
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
|
|
#define pgm_read_word_far(addr) pgm_read_word(addr)
|
|
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
|
|
#define pgm_read_float_far(addr) pgm_read_float(addr)
|
|
#define pgm_read_ptr_far(addr) pgm_read_ptr(addr)
|
|
|
|
#define _SFR_BYTE(n) (n)
|
|
|
|
#ifdef __PROG_TYPES_COMPAT__
|
|
|
|
typedef void prog_void;
|
|
typedef char prog_char;
|
|
typedef unsigned char prog_uchar;
|
|
typedef int8_t prog_int8_t;
|
|
typedef uint8_t prog_uint8_t;
|
|
typedef int16_t prog_int16_t;
|
|
typedef uint16_t prog_uint16_t;
|
|
typedef int32_t prog_int32_t;
|
|
typedef uint32_t prog_uint32_t;
|
|
|
|
#endif // defined(__PROG_TYPES_COMPAT__)
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|