Skip to content

Commit abf6d69

Browse files
Allow byte access to PROGMEM without pgm_read macro
This PR pulls in code from @pvvx's esp8266web server at https://github.com/pvvx/esp8266web (licensed in the public domain) to allow reads of 8 and 16-bit values out of flash without the need for pgm_read_byte/word macros. It installs an exception handler that decodes the faulting instruction and simulates is properly and returns immediately to user code. That said, it is significantly slower (20x?) than using the pgm_read macros properly.
1 parent fa5040d commit abf6d69

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

cores/esp8266/core_esp8266_main.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ extern "C" {
3434
}
3535
#include <core_version.h>
3636
#include "gdb_hooks.h"
37+
#include <core_esp8266_unaligned.h>
38+
3739

3840
#define LOOP_TASK_PRIORITY 1
3941
#define LOOP_QUEUE_SIZE 1
@@ -320,6 +322,8 @@ extern "C" void user_init(void) {
320322

321323
cont_init(g_pcont);
322324

325+
install_unaligned_exception_handler();
326+
323327
preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable.
324328

325329
ets_task(loop_task,
+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/* Unaligned exception handler, allows for byte accesses to PROGMEM to
2+
* succeed without causing a crash. It is still preferred to use the
3+
* xxx_P macros whenever possible, since they are probably 30x faster than
4+
* this exception handler method.
5+
*
6+
* Code taken directly from @pvvx's public domain code in
7+
* https://github.com/pvvx/esp8266web/blob/master/app/sdklib/system/app_main.c
8+
*/
9+
10+
#include <Arduino.h>
11+
#include <core_esp8266_unaligned.h>
12+
13+
extern "C" {
14+
15+
#define LOAD_MASK 0x00f00fu
16+
#define L8UI_MATCH 0x000002u
17+
#define L16UI_MATCH 0x001002u
18+
#define L16SI_MATCH 0x009002u
19+
20+
#define EXCCAUSE_LOAD_STORE_ERROR 3 // Unaligned or NULL error
21+
22+
struct exception_frame
23+
{
24+
uint32_t epc;
25+
uint32_t ps;
26+
uint32_t sar;
27+
uint32_t unused;
28+
union {
29+
struct {
30+
uint32_t a0;
31+
// note: no a1 here!
32+
uint32_t a2;
33+
uint32_t a3;
34+
uint32_t a4;
35+
uint32_t a5;
36+
uint32_t a6;
37+
uint32_t a7;
38+
uint32_t a8;
39+
uint32_t a9;
40+
uint32_t a10;
41+
uint32_t a11;
42+
uint32_t a12;
43+
uint32_t a13;
44+
uint32_t a14;
45+
uint32_t a15;
46+
};
47+
uint32_t a_reg[15];
48+
};
49+
uint32_t cause;
50+
};
51+
52+
extern void _xtos_set_exception_handler(uint32_t reason, void (*fn)(struct exception_frame *ef, uint32_t cause));
53+
extern void _xtos_unhandled_exception(struct exception_frame *ef, uint32_t cause);
54+
55+
static ICACHE_RAM_ATTR void read_align_exception_handler(struct exception_frame *ef, uint32_t cause)
56+
{
57+
/* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */
58+
(void)cause;
59+
60+
uint32_t epc1 = ef->epc;
61+
uint32_t excvaddr;
62+
uint32_t insn;
63+
asm (
64+
"rsr %0, EXCVADDR;" /* read out the faulting address */
65+
"movi a4, ~3;" /* prepare a mask for the EPC */
66+
"and a4, a4, %2;" /* apply mask for 32bit aligned base */
67+
"l32i a5, a4, 0;" /* load part 1 */
68+
"l32i a6, a4, 4;" /* load part 2 */
69+
"ssa8l %2;" /* set up shift register for src op */
70+
"src %1, a6, a5;" /* right shift to get faulting instruction */
71+
:"=r"(excvaddr), "=r"(insn)
72+
:"r"(epc1)
73+
:"a4", "a5", "a6"
74+
);
75+
76+
uint32_t valmask = 0;
77+
uint32_t what = insn & LOAD_MASK;
78+
79+
if (what == L8UI_MATCH)
80+
valmask = 0xffu;
81+
else if (what == L16UI_MATCH || what == L16SI_MATCH)
82+
valmask = 0xffffu;
83+
else
84+
{
85+
die:
86+
/* Turns out we couldn't fix this, trigger a system break instead
87+
* and hang if the break doesn't get handled. This is effectively
88+
* what would happen if the default handler was installed. */
89+
_xtos_unhandled_exception(ef, cause);
90+
asm ("break 1, 1");
91+
while (1) {}
92+
}
93+
94+
/* Load, shift and mask down to correct size */
95+
uint32_t val = (*(uint32_t *)(excvaddr & ~0x3));
96+
val >>= (excvaddr & 0x3) * 8;
97+
val &= valmask;
98+
99+
/* Sign-extend for L16SI, if applicable */
100+
if (what == L16SI_MATCH && (val & 0x8000))
101+
val |= 0xffff0000;
102+
103+
int regno = (insn & 0x0000f0u) >> 4;
104+
if (regno == 1)
105+
goto die; /* we can't support loading into a1, just die */
106+
else if (regno != 0)
107+
--regno; /* account for skipped a1 in exception_frame */
108+
109+
ef->a_reg[regno] = val; /* carry out the load */
110+
ef->epc += 3; /* resume at following instruction */
111+
}
112+
113+
114+
115+
void install_unaligned_exception_handler()
116+
{
117+
_xtos_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, read_align_exception_handler);
118+
}
119+
120+
121+
};
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifdef __cplusplus
2+
extern "C" {
3+
#endif
4+
5+
extern void install_unaligned_exception_handler();
6+
7+
8+
#ifdef __cplusplus
9+
};
10+
#endif
11+

0 commit comments

Comments
 (0)