-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable native PROGMEM for format and parameters #4
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#ifndef ARDUINO | ||
|
||
#ifndef __PGMSPACE__ | ||
#define __PGMSPACE__ | ||
|
||
#include <stdint.h> | ||
|
||
#define PROGMEM __attribute__((section(".irom.text"))) | ||
|
||
// flash memory must be read using 32 bit aligned addresses else a processor | ||
// exception will be triggered | ||
// order within the 32 bit values are | ||
// -------------- | ||
// b3, b2, b1, b0 | ||
// w1, w0 | ||
|
||
#define pgm_read_with_offset(addr, res) \ | ||
asm("extui %0, %1, 0, 2\n" /* Extract offset within word (in bytes) */ \ | ||
"sub %1, %1, %0\n" /* Subtract offset from addr, yielding an aligned address */ \ | ||
"l32i.n %1, %1, 0x0\n" /* Load word from aligned address */ \ | ||
"slli %0, %0, 3\n" /* Mulitiply offset by 8, yielding an offset in bits */ \ | ||
"ssr %0\n" /* Prepare to shift by offset (in bits) */ \ | ||
"srl %0, %1\n" /* Shift right; now the requested byte is the first one */ \ | ||
:"=r"(res), "=r"(addr) \ | ||
:"1"(addr) \ | ||
:); | ||
|
||
static inline uint8_t pgm_read_byte_inlined(const void* addr) { | ||
register uint32_t res; | ||
pgm_read_with_offset(addr, res); | ||
return (uint8_t) res; /* This 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) { | ||
register uint32_t res; | ||
pgm_read_with_offset(addr, res); | ||
return (uint16_t) res; /* This masks the lower half-word from the returned word */ | ||
} | ||
|
||
// Make sure, that libraries checking existence of this macro are not failing | ||
#define pgm_read_byte(addr) pgm_read_byte_inlined(addr) | ||
#define pgm_read_word(addr) pgm_read_word_inlined(addr) | ||
|
||
#endif //__PGMSPACE__ | ||
|
||
#endif // ARDUINO | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -167,6 +167,7 @@ static char *rcsid = "$Id$"; | |
#include "fvwrite.h" | ||
#include "vfieeefp.h" | ||
#include "nano-vfprintf_local.h" | ||
#include "../machine/xtensa/pgmspace.h" | ||
|
||
/* The __ssputs_r function is shared between all versions of vfprintf | ||
and vfwprintf. */ | ||
|
@@ -229,7 +230,7 @@ _DEFUN(__ssputs_r, (ptr, fp, buf, len), | |
if (len < w) | ||
w = len; | ||
|
||
(void)memmove ((_PTR) fp->_p, (_PTR) buf, (size_t) (w)); | ||
(void)memcpy_P ((_PTR) fp->_p, (_PTR) buf, (size_t) (w)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not, and is linked from the Arduino SDK libs. Cut-n-pasting that version somewhere in newlib's not difficult, but I imagine it's not good code hygiene. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about moving the definition into newlib then (adding at here and removing from Arduino)? As mentioned below, I would like libc to stay more or less independent of the application which is built on top of it, if this is reasonably easy to do. In this case, i think it is. (We do implement some of the syscalls which newlib depends on in Arduino; but i think syscalls are a special case.) |
||
fp->_w -= w; | ||
fp->_p += w; | ||
return 0; | ||
|
@@ -321,7 +322,7 @@ _DEFUN(__ssprint_r, (ptr, fp, uio), | |
if (len < w) | ||
w = len; | ||
|
||
(void)memmove ((_PTR) fp->_p, (_PTR) p, (size_t) (w)); | ||
(void)memcpy_P ((_PTR) fp->_p, (_PTR) p, (size_t) (w)); | ||
fp->_w -= w; | ||
fp->_p += w; | ||
/* Pretend we copied all. */ | ||
|
@@ -523,15 +524,15 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), | |
for (;;) | ||
{ | ||
cp = fmt; | ||
while (*fmt != '\0' && *fmt != '%') | ||
while (pgm_read_byte(fmt) != '\0' && pgm_read_byte(fmt) != '%') | ||
fmt += 1; | ||
|
||
if ((m = fmt - cp) != 0) | ||
{ | ||
PRINT (cp, m); | ||
prt_data.ret += m; | ||
} | ||
if (*fmt == '\0') | ||
if (pgm_read_byte(fmt) == '\0') | ||
goto done; | ||
|
||
fmt++; /* Skip over '%'. */ | ||
|
@@ -551,7 +552,7 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), | |
* -- ANSI X3J11 | ||
*/ | ||
flag_chars = "#-0+ "; | ||
for (; cp = memchr (flag_chars, *fmt, 5); fmt++) | ||
for (; cp = memchr (flag_chars, pgm_read_byte(fmt), 5); fmt++) | ||
prt_data.flags |= (1 << (cp - flag_chars)); | ||
|
||
if (prt_data.flags & SPACESGN) | ||
|
@@ -566,7 +567,7 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), | |
prt_data.l_buf[0] = '+'; | ||
|
||
/* The width. */ | ||
if (*fmt == '*') | ||
if (pgm_read_byte(fmt) == '*') | ||
{ | ||
/* | ||
* ``A negative field width argument is taken as a | ||
|
@@ -584,15 +585,15 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), | |
} | ||
else | ||
{ | ||
for (; is_digit (*fmt); fmt++) | ||
prt_data.width = 10 * prt_data.width + to_digit (*fmt); | ||
for (; is_digit (pgm_read_byte(fmt)); fmt++) | ||
prt_data.width = 10 * prt_data.width + to_digit (pgm_read_byte(fmt)); | ||
} | ||
|
||
/* The precision. */ | ||
if (*fmt == '.') | ||
if (pgm_read_byte(fmt) == '.') | ||
{ | ||
fmt++; | ||
if (*fmt == '*') | ||
if (pgm_read_byte(fmt) == '*') | ||
{ | ||
fmt++; | ||
prt_data.prec = GET_ARG (n, ap, int); | ||
|
@@ -602,21 +603,21 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap), | |
else | ||
{ | ||
prt_data.prec = 0; | ||
for (; is_digit (*fmt); fmt++) | ||
prt_data.prec = 10 * prt_data.prec + to_digit (*fmt); | ||
for (; is_digit (pgm_read_byte(fmt)); fmt++) | ||
prt_data.prec = 10 * prt_data.prec + to_digit (pgm_read_byte(fmt)); | ||
} | ||
} | ||
|
||
/* The length modifiers. */ | ||
flag_chars = "hlL"; | ||
if ((cp = memchr (flag_chars, *fmt, 3)) != NULL) | ||
if ((cp = memchr (flag_chars, pgm_read_byte(fmt), 3)) != NULL) | ||
{ | ||
prt_data.flags |= (SHORTINT << (cp - flag_chars)); | ||
fmt++; | ||
} | ||
|
||
/* The conversion specifiers. */ | ||
prt_data.code = *fmt++; | ||
prt_data.code = pgm_read_byte(fmt); fmt++; | ||
cp = memchr ("efgEFG", prt_data.code, 6); | ||
#ifdef FLOATING_POINT | ||
/* If cp is not NULL, we are facing FLOATING POINT NUMBER. */ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ | |
#include "fvwrite.h" | ||
#include "vfieeefp.h" | ||
#include "nano-vfprintf_local.h" | ||
#include "../machine/xtensa/pgmspace.h" | ||
|
||
/* Decode and print non-floating point data. */ | ||
int | ||
|
@@ -104,6 +105,20 @@ _printf_common (struct _reent *data, | |
error: | ||
return -1; | ||
} | ||
|
||
// Not defined in the ESP8266 Arduino | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the reference to Arduino might not be appropriate, as this project is not arduino-specific. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, so newlib is also used by the SDK as well? Just checking the reasoning as I'm only testing w/Arduino SDK now (but can't imagine any meaningful differences). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not, but i have a few personal projects which do not use Arduino and use this newlib fork, so I'd like to keep it generic, if reasonable. |
||
static const char *memchr_P(const char *src, int c, size_t length) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The dummy definition (or an alias to memchr) can be put in the same file where memchr is defined. Edit: however, i have not looked at the code generated by GCC for this. Perhaps it optimizes out unneeded loads... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think GCC looks into the assembly itself as the __asm blocks have to manually define what registers they trounce/etc. Optimization's fine and not too complicated, but for starters I wanted something I could reasonably prove was correct. :) This is the actual memchr routine built in the lib (modulo s/*x/pgm_read_byte(&x)/g). The data should come from the I$ anyway given the tight look, so it's not going to cause 4 SPI reads, at least. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, it's more of a thing to look into later. I am okay with merging the obviously correct version. |
||
{ | ||
while (length--) | ||
{ | ||
if (pgm_read_byte(src) == c) | ||
return (void *) src; | ||
src++; | ||
} | ||
|
||
return NULL; | ||
} | ||
|
||
int | ||
_printf_i (struct _reent *data, struct _prt_data_t *pdata, FILE *fp, | ||
int (*pfunc)(struct _reent *, FILE *, _CONST char *, size_t len), | ||
|
@@ -216,7 +231,7 @@ _printf_i (struct _reent *data, struct _prt_data_t *pdata, FILE *fp, | |
string, and take prec == -1 into consideration. | ||
Use normal Newlib approach here to support case where cp is not | ||
nul-terminated. */ | ||
char *p = memchr (cp, 0, pdata->prec); | ||
char *p = memchr_P (cp, 0, pdata->prec); | ||
|
||
if (p != NULL) | ||
pdata->prec = p - cp; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General note, for all 4 MRs: common (non-machine-specific) code in newlib typically does not include machine-specific files directly.
Instead, what you can do is provide dummy definitions of pgm_read_byte in stdio/string.h, which will work for all platforms, and then add machine-specific definition of pgm_read_byte to sys/string.h under xtensa machine (path in this repository would be newlib/libc/sys/xtensa/sys/string.h).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to avoid any chance of namespace collisions by putting it into a directory that is not installed to either the SDK or Arduino SDK, and to minimize duplication. Wouldn't placing it there end up causing there to be two definitions in the tree (one in the SDK, one in the libc)? It's not a big deal to rename, but then you've got two identical functions with the same name in a real build tree, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are adding these functions into libc, we can remove them from Arduino.