diff --git a/bootloader/SOURCES b/bootloader/SOURCES index 849ed17c40..e775fbe246 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES @@ -1,4 +1,6 @@ common.c +format.c +snprintf.c #if defined(IPOD_NANO2G) ipodnano2g.c diff --git a/bootloader/format.c b/bootloader/format.c new file mode 100644 index 0000000000..8f28444b8c --- /dev/null +++ b/bootloader/format.c @@ -0,0 +1,324 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Gary Czvitkovicz + * Copyright (C) 2017 by William Wilgus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include +#include +#include +#include +#include "file.h" +#include "format.h" + +static const char hexdigit[] = "0123456789ABCDEF"; + +/* smaller compressed binary without inline but 18% slower */ +#define FMT_DECL static inline + +FMT_DECL int fmt_width_precision(int *ch, const char **fmt, char **str, va_list *ap) +{ + int value = 0; + (void) str; + (void) ap; + + while (*ch >= '0' && *ch <= '9') + { + value = 10 * value + (*ch - '0'); + *ch = *(*fmt)++; + } + return value; +} + +FMT_DECL int fmt_integer_signed(int *ch, const char **fmt, char **str, va_list *ap) +{ + int val, rem, sign; + (void) ch; + (void) fmt; + + val = sign = va_arg(*ap, int); + if (val < 0) + val = -val; + do { + rem = val % 10; + val /= 10; + *--(*str) = rem + '0'; + + } while (val > 0); + + if (sign < 0) + *--(*str) = '-'; + return 0; +} + +FMT_DECL int fmt_integer_unsigned(int *ch, const char **fmt, char **str, va_list *ap) +{ + unsigned int uval, urem; + (void) ch; + (void) fmt; + + uval = va_arg(*ap, unsigned int); + do { + urem = uval % 10; + uval /= 10; + *--(*str) = urem + '0'; + } while (uval > 0); + return 0; +} + +FMT_DECL int fmt_long(int *ch, const char **fmt, char **str, va_list *ap) +{ + int pad = 0; + long lval, lrem, lsign = 0; + unsigned long ulval, ulrem; + char ch_l = *ch; + + *ch = *(*fmt)++; + if (*ch == 'd') { + lval = lsign = va_arg(*ap, long); + + if (lval < 0) + lval = -lval; + do { + lrem = lval % 10; + lval /= 10; + *--(*str) = lrem + '0'; + } while (lval > 0); + + if (lsign < 0) + *--(*str) = '-'; + } + else if (*ch == 'u') { + ulval = va_arg(*ap, unsigned long); + do { + ulrem = ulval % 10; + ulval /= 10; + *--(*str) = ulrem + '0'; + } while (ulval > 0); + } + else if (*ch == 'x' || *ch == 'X') { + pad++; + ulval = va_arg(*ap, long); + do { + *--(*str) = hexdigit[ulval & 0xf]; + ulval >>= 4; + } while (ulval > 0); + } + else { + *--(*str) = ch_l; + *--(*str) = *ch; + } + return pad; +} + +FMT_DECL int fmt_character(int *ch, const char **fmt, char **str, va_list *ap) +{ + (void) ch; + (void) fmt; + + *--(*str) = va_arg(*ap, int); + return 0; +} + +FMT_DECL int fmt_string(int *ch, const char **fmt, char **str, va_list *ap) +{ + (void) ch; + (void) fmt; + + *str = va_arg (*ap, char*); + return 0; +} + +FMT_DECL int fmt_hex_unsigned(int *ch, const char **fmt, char **str, va_list *ap) +{ + unsigned int uval; + (void) ch; + (void) fmt; + + uval = va_arg(*ap, int); + do { + *--(*str) = hexdigit[uval & 0xf]; + uval >>= 4; + } while (uval > 0); + return 1; +} + +FMT_DECL int fmt_pointer(int *ch, const char **fmt, char **str, va_list *ap) +{ + int pad = fmt_hex_unsigned(ch, fmt, str, ap); + /* for pointers prepend 0x and act like 'X' */ + *--(*str) = 'x'; + *--(*str) = '0'; + return pad; +} + +FMT_DECL int fmt_sizet(int *ch, const char **fmt, char **str, va_list *ap) +{ + size_t uszval, uszrem; + ssize_t szval, szrem, szsign; + char ch_z = *ch; + *ch = *(*fmt)++; + + if (*ch == 'd') { + szval = szsign = va_arg(*ap, ssize_t); + if (szval < 0) + szval = -szval; + do { + szrem = szval % 10; + szval /= 10; + *--(*str) = szrem + '0'; + } while (szval > 0); + + if (szsign < 0) + *--(*str) = '-'; + } + else if (*ch == 'u') { + uszval = va_arg(*ap, size_t); + do { + uszrem = uszval % 10; + uszval /= 10; + *--(*str) = uszrem + '0'; + } while (uszval > 0); + } + else { + *--(*str) = ch_z; + *--(*str) = *ch; + } + return 0; +} + +static inline int fmt_next_char(int *ch, const char **fmt, char **str, va_list *ap) +{ + (void) fmt; + (void) ap; + + *--(*str) = *ch; + return 0; +} + + + +void format( + /* call 'push()' for each output letter */ + int (*push)(void *userp, unsigned char data), + void *userp, + const char *fmt, + va_list ap) +{ + bool ok = true; + char *str; + char tmpbuf[12], pad; + int ch, width, precision, padded; + + + ch = *fmt++; + tmpbuf[sizeof tmpbuf - 1] = '\0'; + + do + { + if (ch == '%') + { + str = tmpbuf + sizeof tmpbuf - 1; + ch = *fmt++; + padded = (ch == '0' ? 1 : 0); + width = fmt_width_precision(&ch, &fmt, &str, &ap); + + precision = INT_MAX; + if(ch == '.') + { + ch = *fmt++; + precision = fmt_width_precision(&ch, &fmt, &str, &ap); + } + + if (ch == 'd') + fmt_integer_signed(&ch, &fmt, &str, &ap); + else if (ch == 'u') + fmt_integer_unsigned(&ch, &fmt, &str, &ap); + else if (ch == 'l') + padded += fmt_long(&ch, &fmt, &str, &ap); + else if (ch == 'c') + fmt_character(&ch, &fmt, &str, &ap); + else if (ch == 's') + fmt_string(&ch, &fmt, &str, &ap); + else if (ch == 'x' || ch == 'X') + padded += fmt_hex_unsigned(&ch, &fmt, &str, &ap); + else if (ch == 'p' || ch == 'P') + padded += fmt_pointer(&ch, &fmt, &str, &ap); +#if 0 + else if (ch == 'z') + fmt_sizet(&ch, &fmt, &str, &ap); +#endif + else + fmt_next_char(&ch, &fmt, &str, &ap); + + width -= strlen (str); + if (width > 0) + { + pad = (padded ? '0' : ' '); + while (width-- > 0 && ok) + ok=push(userp, pad); + } + while(*str != '\0' && ok && precision--) + ok=push(userp, *str++); + } + else + ok=push(userp, ch); + + } while ((ch = *fmt++) != '\0' && ok); +} + +struct for_fprintf { + int fd; /* where to store it */ + int bytes; /* amount stored */ +}; + +static int fprfunc(void *pr, unsigned char letter) +{ + struct for_fprintf *fpr = (struct for_fprintf *)pr; + int rc = write(fpr->fd, &letter, 1); + + if(rc > 0) { + fpr->bytes++; /* count them */ + return true; /* we are ok */ + } + + return false; /* failure */ +} + + +int fdprintf(int fd, const char *fmt, ...) +{ + va_list ap; + struct for_fprintf fpr; + + fpr.fd=fd; + fpr.bytes=0; + + va_start(ap, fmt); + format(fprfunc, &fpr, fmt, ap); + va_end(ap); + + return fpr.bytes; /* return 0 on error */ +} + +void vuprintf(int (*push)(void *userp, unsigned char data), void *userp, const char *fmt, va_list ap) +{ + format(push, userp, fmt, ap); +} diff --git a/bootloader/format.h b/bootloader/format.h new file mode 100644 index 0000000000..30a072aca8 --- /dev/null +++ b/bootloader/format.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Felix Arends + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __FORMAT_H__ +#define __FORMAT_H__ + +void format( + /* call 'push()' for each output letter */ + int (*push)(void *userp, unsigned char data), + void *userp, + const char *fmt, + va_list ap); + +/* callback function is called for every output character (byte) with userp and + * should return 0 when ch is a char other than '\0' that should stop printing */ +void vuprintf(int (*push)(void *userp, unsigned char data), + void *userp, const char *fmt, va_list ap); + +#endif /* __FORMAT_H__ */ diff --git a/bootloader/snprintf.c b/bootloader/snprintf.c new file mode 100644 index 0000000000..d64bb32447 --- /dev/null +++ b/bootloader/snprintf.c @@ -0,0 +1,80 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Gary Czvitkovicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* + * Minimal printf and snprintf formatting functions + * + * These support %c %s %l %d %u and %x(%p) + * Field width and zero-padding flag only + */ + +#include +#include +#include +#include +#include "format.h" + +struct for_snprintf { + unsigned char *ptr; /* where to store it */ + size_t bytes; /* amount already stored */ + size_t max; /* max amount to store */ +}; + +static int sprfunc(void *ptr, unsigned char letter) +{ + struct for_snprintf *pr = (struct for_snprintf *)ptr; + if(pr->bytes < pr->max) { + *pr->ptr = letter; + pr->ptr++; + pr->bytes++; + return true; + } + return false; /* filled buffer */ +} + + +int snprintf(char *buf, size_t size, const char *fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + return len; +} + +int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) +{ + struct for_snprintf pr; + + pr.ptr = (unsigned char *)buf; + pr.bytes = 0; + pr.max = size; + + format(sprfunc, &pr, fmt, ap); + + /* make sure it ends with a trailing zero */ + pr.ptr[(pr.bytes < pr.max) ? 0 : -1] = '\0'; + + return pr.bytes; +} diff --git a/firmware/common/diacritic.c b/firmware/common/diacritic.c index 563028cab8..92c2400203 100644 --- a/firmware/common/diacritic.c +++ b/firmware/common/diacritic.c @@ -194,7 +194,7 @@ static const struct diac_range diac_ranges[] = }; #define MRU_MAX_LEN 32 - +#ifndef BOOTLOADER bool is_diacritic(const unsigned short char_code, bool *is_rtl) { static uint8_t mru_len = 0; @@ -248,4 +248,12 @@ Found: return (char_code < diac->base + (info & DIAC_CNT)); } - +#else /*BOOTLOADER*/ +inline bool is_diacritic(const unsigned short char_code, bool *is_rtl) +{ + (void)char_code; + if (is_rtl) + *is_rtl = false; + return false; +} +#endif /* ndef BOOTLOADER*/ diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c index 22430d4e50..8c38e513c6 100644 --- a/firmware/drivers/lcd-bitmap-common.c +++ b/firmware/drivers/lcd-bitmap-common.c @@ -118,6 +118,7 @@ void LCDFN(update_viewport_rect)(int x, int y, int width, int height) LCDFN(update_rect)(current_vp->x + x, current_vp->y + y, width, height); } +#ifndef BOOTLOADER /* put a string at a given pixel position, skipping first ofs pixel columns */ static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str) { @@ -257,6 +258,72 @@ static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str) } font_lock(current_vp->font, false); } +#else /* BOOTLOADER */ +/* put a string at a given pixel position, skipping first ofs pixel columns */ +static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str) +{ + unsigned short *ucs; + struct font* pf = font_get(current_vp->font); + int vp_flags = current_vp->flags; + const unsigned char *bits; + int width; + + if ((vp_flags & VP_FLAG_ALIGNMENT_MASK) != 0) + { + int w; + + LCDFN(getstringsize)(str, &w, NULL); + /* center takes precedence */ + if (vp_flags & VP_FLAG_ALIGN_CENTER) + { + x = ((current_vp->width - w)/ 2) + x; + if (x < 0) + x = 0; + } + else + { + x = current_vp->width - w - x; + x += ofs; + ofs = 0; + } + } + + /* allow utf but no diacritics or rtl lang */ + for (ucs = bidi_l2v(str, 1); *ucs; ucs++) + { + const unsigned short next_ch = ucs[1]; + + if (x >= current_vp->width) + break; + + /* Get proportional width and glyph bits */ + width = font_get_width(pf, *ucs); + + if (ofs > width) + { + ofs -= width; + continue; + } + + bits = font_get_bits(pf, *ucs); + +#if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR) + if (pf->depth) + lcd_alpha_bitmap_part(bits, ofs, 0, width, x, y, + width - ofs, pf->height); + else +#endif + LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x, + y, width - ofs, pf->height); + if (next_ch) + { + x += width - ofs; + ofs = 0; + } + } +} +#endif + /*** pixel oriented text output ***/ diff --git a/firmware/scroll_engine.c b/firmware/scroll_engine.c index b584345a9a..91f9d1f868 100644 --- a/firmware/scroll_engine.c +++ b/firmware/scroll_engine.c @@ -52,7 +52,6 @@ static const char scroll_tick_table[18] = { }; static void scroll_thread(void); -static char scroll_stack[DEFAULT_STACK_SIZE*3]; static const char scroll_name[] = "scroll"; #include "drivers/lcd-scroll.c" @@ -195,8 +194,11 @@ static void scroll_thread(void) } #endif /* HAVE_REMOTE_LCD */ + +#ifndef BOOTLOADER void scroll_init(void) { + static char scroll_stack[DEFAULT_STACK_SIZE*3]; #ifdef HAVE_REMOTE_LCD queue_init(&scroll_queue, true); #endif @@ -205,3 +207,9 @@ void scroll_init(void) IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU)); } +#else +void scroll_init(void) +{ + /* DUMMY */ +} +#endif /* ndef BOOTLOADER*/