diff options
-rw-r--r-- | firmware/SOURCES | 3 | ||||
-rw-r--r-- | firmware/common/ap_int.c | 266 | ||||
-rw-r--r-- | firmware/common/vuprintf.c | 475 | ||||
-rw-r--r-- | firmware/include/ap_int.h | 59 |
4 files changed, 793 insertions, 10 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 16ae4cc350..89f4f52895 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -199,6 +199,9 @@ libc/gmtime.c | |||
199 | #endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */ | 199 | #endif /* CONFIG_PLATFORM || HAVE_ROCKBOX_C_LIBRARY */ |
200 | 200 | ||
201 | /* Common */ | 201 | /* Common */ |
202 | #ifndef BOOTLOADER | ||
203 | common/ap_int.c | ||
204 | #endif | ||
202 | common/version.c | 205 | common/version.c |
203 | common/config.c | 206 | common/config.c |
204 | common/crc32.c | 207 | common/crc32.c |
diff --git a/firmware/common/ap_int.c b/firmware/common/ap_int.c new file mode 100644 index 0000000000..12214534dd --- /dev/null +++ b/firmware/common/ap_int.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2018 by Michael A. Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "ap_int.h" | ||
22 | #include "fixedpoint.h" | ||
23 | |||
24 | /* Miscellaneous large-sized integer functions */ | ||
25 | |||
26 | /* round string, base 10 */ | ||
27 | bool round_number_string10(char *p_rdig, long len) | ||
28 | { | ||
29 | /* | ||
30 | * * p should point to the digit that determines if rounding should occur | ||
31 | * * buffer is updated in reverse | ||
32 | * * an additional '1' may be added to the beginning: eg. 9.9 => 10.0 | ||
33 | */ | ||
34 | #if 1 /* nearest */ | ||
35 | bool round = p_rdig[0] >= '5'; | ||
36 | #else /* even */ | ||
37 | bool round = p_rdig[0] >= '5' && (p_rdig[-1] & 1); | ||
38 | #endif | ||
39 | |||
40 | while (round && len-- > 0) { | ||
41 | int d = *--p_rdig; | ||
42 | round = ++d > '9'; | ||
43 | *p_rdig = round ? '0' : d; | ||
44 | } | ||
45 | |||
46 | if (round) { | ||
47 | /* carry to the next place */ | ||
48 | *--p_rdig = '1'; | ||
49 | } | ||
50 | |||
51 | return round; | ||
52 | } | ||
53 | |||
54 | /* format arbitrary-precision base 10 integer */ | ||
55 | char * format_ap_int10(struct ap_int *a, | ||
56 | char *p_end) | ||
57 | { | ||
58 | /* | ||
59 | * * chunks are in least-to-most-significant order | ||
60 | * * chunk array is used for intermediate division results | ||
61 | * * digit string buffer is written high-to-low address order | ||
62 | */ | ||
63 | long numchunks = a->numchunks; | ||
64 | char *p = p_end; | ||
65 | |||
66 | if (numchunks == 0) { | ||
67 | /* fast formatting */ | ||
68 | uint64_t val = a->val; | ||
69 | |||
70 | do { | ||
71 | *--p = val % 10 + '0'; | ||
72 | val /= 10; | ||
73 | } while (val); | ||
74 | |||
75 | a->len = p_end - p; | ||
76 | return p; | ||
77 | } | ||
78 | |||
79 | uint32_t *chunks = a->chunks; | ||
80 | long topchunk = numchunks - 1; | ||
81 | |||
82 | /* if top chunk(s) are zero, ignore */ | ||
83 | while (topchunk >= 0 && chunks[topchunk] == 0) { | ||
84 | topchunk--; | ||
85 | } | ||
86 | |||
87 | /* optimized to divide number by the biggest 10^x a uint32_t can hold | ||
88 | so that r_part holds the remainder (x % 1000000000) at the end of | ||
89 | the division */ | ||
90 | do { | ||
91 | uint64_t r_part = 0; | ||
92 | |||
93 | for (long i = topchunk; i >= 0; i--) { | ||
94 | /* | ||
95 | * Testing showed 29 bits as a sweet spot: | ||
96 | * * Is a 32-bit constant (good for 32-bit hardware) | ||
97 | * * No more normalization is required than with 30 and 31 | ||
98 | * (32 bits requires the least but also a large constant) | ||
99 | * * Doesn't need to be reduced before hand by subtracting the | ||
100 | * divisor in order to keep it 32-bits which obviates the need | ||
101 | * to correct with another term of the remainder after | ||
102 | * multiplying | ||
103 | * | ||
104 | * 2305843009 = floor(ldexp(1, 29) / 1000000000.0 * ldexp(1, 32)) | ||
105 | */ | ||
106 | static const unsigned long c = 2305843009; /* .213693952 */ | ||
107 | uint64_t q_part = r_part*c >> 29; | ||
108 | r_part = (r_part << 32) | chunks[i]; | ||
109 | r_part -= q_part*1000000000; | ||
110 | |||
111 | /* if remainder is still out of modular range, normalize it | ||
112 | and carry over into quotient */ | ||
113 | while (r_part >= 1000000000) { | ||
114 | r_part -= 1000000000; | ||
115 | q_part++; | ||
116 | } | ||
117 | |||
118 | chunks[i] = q_part; | ||
119 | } | ||
120 | |||
121 | /* if top chunk(s) became zero, ignore from now on */ | ||
122 | while (topchunk >= 0 && chunks[topchunk] == 0) { | ||
123 | topchunk--; | ||
124 | } | ||
125 | |||
126 | /* format each digit chunk, padded to width 9 if not the leading one */ | ||
127 | uint32_t val = r_part; | ||
128 | int len = 8*(topchunk >= 0); | ||
129 | |||
130 | while (len-- >= 0 || val) { | ||
131 | *--p = (val % 10) + '0'; | ||
132 | val /= 10; | ||
133 | } | ||
134 | } while (topchunk >= 0); | ||
135 | |||
136 | a->len = p_end - p; | ||
137 | return p; | ||
138 | } | ||
139 | |||
140 | /* format arbitrary-precision base 10 fraction */ | ||
141 | char * format_ap_frac10(struct ap_int *a, | ||
142 | char *p_start, | ||
143 | long precision) | ||
144 | { | ||
145 | /* | ||
146 | * * chunks are in least-to-most-significant order | ||
147 | * * chunk array is used for intermediate multiplication results | ||
148 | * * digit string buffer is written low-to-high address order | ||
149 | * * high bit of fraction must be left-justified to a chunk | ||
150 | * boundary | ||
151 | */ | ||
152 | long numchunks = a->numchunks; | ||
153 | bool trimlz = precision < 0; | ||
154 | char *p = p_start; | ||
155 | |||
156 | if (trimlz) { | ||
157 | /* trim leading zeros and provide <precision> digits; a->len | ||
158 | will end up greater than the specified precision unless the | ||
159 | value is zero */ | ||
160 | precision = -precision; | ||
161 | } | ||
162 | |||
163 | a->len = precision; | ||
164 | |||
165 | if (numchunks == 0) { | ||
166 | /* fast formatting; shift must be <= 60 as top four bits are used | ||
167 | for digit carryout */ | ||
168 | if (trimlz && !a->val) { | ||
169 | /* value is zero */ | ||
170 | trimlz = false; | ||
171 | } | ||
172 | |||
173 | uint64_t val = a->val << (60 - a->shift); | ||
174 | |||
175 | while (precision > 0) { | ||
176 | val *= 10; | ||
177 | uint32_t c_part = val >> 60; | ||
178 | |||
179 | if (trimlz) { | ||
180 | if (!c_part) { | ||
181 | a->len++; | ||
182 | continue; | ||
183 | } | ||
184 | |||
185 | trimlz = false; | ||
186 | } | ||
187 | |||
188 | *p++ = c_part + '0'; | ||
189 | val ^= (uint64_t)c_part << 60; | ||
190 | precision--; | ||
191 | } | ||
192 | |||
193 | return p; | ||
194 | } | ||
195 | |||
196 | uint32_t *chunks = a->chunks; | ||
197 | long bottomchunk = 0, topchunk = numchunks; | ||
198 | |||
199 | while (topchunk > 0 && chunks[topchunk - 1] == 0) { | ||
200 | topchunk--; | ||
201 | } | ||
202 | |||
203 | /* optimized to multiply number by the biggest 10^x a uint32_t can hold | ||
204 | so that c_part holds the carryover into the integer part at the end | ||
205 | of the multiplication */ | ||
206 | while (precision > 0) { | ||
207 | /* if bottom chunk(s) are or became zero, skip them */ | ||
208 | while (bottomchunk < numchunks && chunks[bottomchunk] == 0) { | ||
209 | bottomchunk++; | ||
210 | } | ||
211 | |||
212 | uint32_t c_part = 0; | ||
213 | |||
214 | for (long i = bottomchunk; i < topchunk; i++) { | ||
215 | uint64_t p_part = chunks[i]; | ||
216 | |||
217 | p_part = p_part * 1000000000 + c_part; | ||
218 | c_part = p_part >> 32; | ||
219 | |||
220 | chunks[i] = p_part; | ||
221 | } | ||
222 | |||
223 | if (topchunk < numchunks && c_part) { | ||
224 | chunks[topchunk++] = c_part; | ||
225 | c_part = 0; | ||
226 | } | ||
227 | |||
228 | int len = 9; | ||
229 | |||
230 | if (trimlz && bottomchunk < numchunks) { | ||
231 | if (!c_part) { | ||
232 | a->len += 9; | ||
233 | continue; | ||
234 | } | ||
235 | |||
236 | /* first non-zero chunk has leading zeros? */ | ||
237 | for (uint32_t val = c_part; val < 100000000; val *= 10) { | ||
238 | len--; | ||
239 | } | ||
240 | |||
241 | a->len += 9 - len; | ||
242 | trimlz = false; | ||
243 | } | ||
244 | |||
245 | /* format each digit chunk, padded to width 9 if not exceeding | ||
246 | precision */ | ||
247 | precision -= len; | ||
248 | |||
249 | if (precision < 0) { | ||
250 | /* remove extra digits */ | ||
251 | c_part /= ipow(10, -precision); | ||
252 | len += precision; | ||
253 | } | ||
254 | |||
255 | p += len; | ||
256 | |||
257 | char *p2 = p; | ||
258 | |||
259 | while (len-- > 0) { | ||
260 | *--p2 = (c_part % 10) + '0'; | ||
261 | c_part /= 10; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | return p; | ||
266 | } | ||
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c index 7dd9449e56..8bb9451662 100644 --- a/firmware/common/vuprintf.c +++ b/firmware/common/vuprintf.c | |||
@@ -23,8 +23,10 @@ | |||
23 | #include <limits.h> | 23 | #include <limits.h> |
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <stddef.h> | 25 | #include <stddef.h> |
26 | #include <stdlib.h> | ||
26 | #include "system.h" | 27 | #include "system.h" |
27 | #include "vuprintf.h" | 28 | #include "vuprintf.h" |
29 | #include "ap_int.h" | ||
28 | 30 | ||
29 | #ifndef BOOTLOADER | 31 | #ifndef BOOTLOADER |
30 | /* turn everything on */ | 32 | /* turn everything on */ |
@@ -58,7 +60,11 @@ | |||
58 | #define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */ | 60 | #define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */ |
59 | #define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */ | 61 | #define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */ |
60 | #define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */ | 62 | #define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */ |
63 | #if 0 | ||
61 | #define FMT_LENMOD_L 0x080 /* long double (instead of double) */ | 64 | #define FMT_LENMOD_L 0x080 /* long double (instead of double) */ |
65 | #else | ||
66 | #define FMT_LENMOD_L 0x000 | ||
67 | #endif | ||
62 | 68 | ||
63 | /* compulsory radixes: c, d, i, u, s */ | 69 | /* compulsory radixes: c, d, i, u, s */ |
64 | #define FMT_RADIX_c 0x001 /* single character (%c) */ | 70 | #define FMT_RADIX_c 0x001 /* single character (%c) */ |
@@ -75,6 +81,18 @@ | |||
75 | #define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending | 81 | #define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending |
76 | upon value and precision */ | 82 | upon value and precision */ |
77 | 83 | ||
84 | /* TODO: 'a' 'A' */ | ||
85 | #define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g) | ||
86 | |||
87 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
88 | /* Assumes IEEE 754 double-precision, native-endian; replace to parse and init | ||
89 | for some other format */ | ||
90 | #define parse_double parse_ieee754_double | ||
91 | #define init_double_chunks init_ieee754_double_chunks | ||
92 | #define format_double_int10 format_ap_int10 | ||
93 | #define format_double_frac10 format_ap_frac10 | ||
94 | #endif | ||
95 | |||
78 | /* avoid defining redundant functions if two or more types can use the same | 96 | /* avoid defining redundant functions if two or more types can use the same |
79 | * something not getting a macro means it gets assigned its own value and | 97 | * something not getting a macro means it gets assigned its own value and |
80 | * formatter */ | 98 | * formatter */ |
@@ -374,8 +392,24 @@ struct fmt_buf { | |||
374 | or prefix (numeric) */ | 392 | or prefix (numeric) */ |
375 | char buf[24]; /* work buffer */ | 393 | char buf[24]; /* work buffer */ |
376 | char bufend[1]; /* buffer end marker and guard '0' */ | 394 | char bufend[1]; /* buffer end marker and guard '0' */ |
395 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
396 | int lenmod; | ||
397 | int radixchar; | ||
398 | int signchar; | ||
399 | int alignchar; | ||
400 | int width; | ||
401 | int precision; | ||
402 | char *p; | ||
403 | #endif | ||
377 | }; | 404 | }; |
378 | 405 | ||
406 | #define PUSHCHAR(ch) \ | ||
407 | ({ int __rc = push(userp, (ch)); \ | ||
408 | count += __rc >= 0; \ | ||
409 | if (__rc <= 0) { \ | ||
410 | goto done; \ | ||
411 | } }) | ||
412 | |||
379 | /* %d %i */ | 413 | /* %d %i */ |
380 | static inline const char * format_d(int val, | 414 | static inline const char * format_d(int val, |
381 | struct fmt_buf *fmt_buf, | 415 | struct fmt_buf *fmt_buf, |
@@ -530,6 +564,400 @@ static inline const char * format_p(const void *p, | |||
530 | } | 564 | } |
531 | #endif /* FMT_RADIX_p */ | 565 | #endif /* FMT_RADIX_p */ |
532 | 566 | ||
567 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
568 | /* find out how many uint32_t chunks need to be allocated, if any | ||
569 | * if none are needed, finish the init for the number here */ | ||
570 | static long parse_ieee754_double(double f, | ||
571 | struct ap_int *ia, | ||
572 | struct ap_int *fa, | ||
573 | struct fmt_buf *fmt_buf) | ||
574 | { | ||
575 | long rc = 0; | ||
576 | |||
577 | union { | ||
578 | double f; | ||
579 | uint64_t f64; | ||
580 | } u = { .f = f }; | ||
581 | |||
582 | int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */ | ||
583 | uint64_t mantissa = u.f64 & 0x000fffffffffffffull; | ||
584 | |||
585 | if (u.f64 >> 63) { | ||
586 | fmt_buf->signchar = '-'; | ||
587 | } | ||
588 | |||
589 | if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */ | ||
590 | /* integer, fraction and manipulations fit in uint64_t */ | ||
591 | mantissa |= 0x0010000000000000ull; | ||
592 | ia->numchunks = 0; | ||
593 | ia->shift = 0; | ||
594 | fa->numchunks = 0; | ||
595 | |||
596 | if (e < 0) { /* -8 to -1 - fraction */ | ||
597 | long fracbits = 52 - e; | ||
598 | /* int - none */ | ||
599 | ia->len = 0; | ||
600 | ia->val = 0; | ||
601 | /* frac */ | ||
602 | fa->len = fracbits - __builtin_ctzll(mantissa); | ||
603 | fa->shift = fracbits; | ||
604 | fa->val = mantissa; | ||
605 | } | ||
606 | else if (e <= 51) { /* 0 to +51 - integer|fraction */ | ||
607 | long fracbits = 52 - e; | ||
608 | /* int */ | ||
609 | ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */ | ||
610 | ia->val = mantissa >> fracbits; | ||
611 | /* frac */ | ||
612 | fa->shift = fracbits; | ||
613 | fa->val = mantissa ^ (ia->val << fracbits); | ||
614 | fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0; | ||
615 | } | ||
616 | else { /* +52 to +63 - integer */ | ||
617 | /* int */ | ||
618 | ia->len = base10exp(e) + 2; | ||
619 | ia->val = mantissa << (e - 52); | ||
620 | /* frac - none */ | ||
621 | fa->len = 0; | ||
622 | fa->shift = 0; | ||
623 | fa->val = 0; | ||
624 | } | ||
625 | } | ||
626 | else if (e < 0) { /* -1023 to -9 - fraction */ | ||
627 | /* int - none */ | ||
628 | ia->numchunks = 0; | ||
629 | ia->len = 0; | ||
630 | ia->shift = 0; | ||
631 | ia->val = 0; | ||
632 | /* frac - left-justify on bit 31 of the chunk of the MSb */ | ||
633 | if (e >= -1022) { /* normal */ | ||
634 | mantissa |= 0x0010000000000000ull; | ||
635 | } | ||
636 | else { /* subnormal (including zero) */ | ||
637 | e = -1022; | ||
638 | } | ||
639 | |||
640 | if (mantissa) { | ||
641 | long fracbits = 52 - e; | ||
642 | fa->len = fracbits - __builtin_ctzll(mantissa); | ||
643 | fa->shift = 31 - ((51 - e) % 32); | ||
644 | fa->val = mantissa; | ||
645 | fa->basechunk = (fa->shift + 52) / 32; | ||
646 | fa->numchunks = (51 - e + fa->shift) / 32 + 1; | ||
647 | rc = fa->numchunks; | ||
648 | } | ||
649 | else { /* zero */ | ||
650 | fa->numchunks = 0; | ||
651 | fa->len = 0; | ||
652 | fa->shift = 0; | ||
653 | fa->val = 0; | ||
654 | } | ||
655 | } | ||
656 | else if (e <= 1023) { /* +64 to +1023 - integer */ | ||
657 | /* int - right-justify on bit 0 of the first chunk */ | ||
658 | ia->val = mantissa | 0x0010000000000000ull; | ||
659 | ia->len = base10exp(e) + 2; | ||
660 | ia->shift = (e - 52) % 32; | ||
661 | ia->basechunk = e / 32; | ||
662 | ia->numchunks = ia->basechunk + 1; | ||
663 | rc = ia->numchunks; | ||
664 | /* frac - none */ | ||
665 | fa->numchunks = 0; | ||
666 | fa->len = 0; | ||
667 | fa->shift = 0; | ||
668 | fa->val = 0; | ||
669 | } | ||
670 | else { /* +1024: INF, NAN */ | ||
671 | rc = -1 - !!mantissa; | ||
672 | } | ||
673 | |||
674 | return rc; | ||
675 | } | ||
676 | |||
677 | /* construct the arbitrary-precision value in the provided allocation */ | ||
678 | static void init_ieee754_double_chunks(struct ap_int *a, | ||
679 | uint32_t *a_chunks) | ||
680 | { | ||
681 | long basechunk = a->basechunk; | ||
682 | long shift = a->shift; | ||
683 | uint64_t val = a->val; | ||
684 | |||
685 | a->chunks = a_chunks; | ||
686 | |||
687 | memset(a_chunks, 0, a->numchunks*sizeof (uint32_t)); | ||
688 | |||
689 | if (shift < 12) { | ||
690 | a_chunks[basechunk - 1] = val << shift; | ||
691 | a_chunks[basechunk - 0] = val >> (32 - shift); | ||
692 | } | ||
693 | else { | ||
694 | a_chunks[basechunk - 2] = val << shift; | ||
695 | a_chunks[basechunk - 1] = val >> (32 - shift); | ||
696 | a_chunks[basechunk - 0] = val >> (64 - shift); | ||
697 | } | ||
698 | } | ||
699 | |||
700 | /* format inf, nan strings */ | ||
701 | static void format_inf_nan(struct fmt_buf *fmt_buf, long type) | ||
702 | { | ||
703 | /* certain special values */ | ||
704 | static const char text[2][2][3] = | ||
705 | { | ||
706 | { { 'I', 'N', 'F' }, { 'i', 'n', 'f' } }, | ||
707 | { { 'N', 'A', 'N' }, { 'n', 'a', 'n' } }, | ||
708 | }; | ||
709 | |||
710 | char *p = fmt_buf->buf; | ||
711 | fmt_buf->p = p; | ||
712 | fmt_buf->length = 3; | ||
713 | |||
714 | /* they also have a sign */ | ||
715 | if (fmt_buf->signchar) { | ||
716 | *p++ = fmt_buf->signchar; | ||
717 | fmt_buf->length++; | ||
718 | } | ||
719 | |||
720 | memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3); | ||
721 | } | ||
722 | |||
723 | /* %e %E %f %F %g %G */ | ||
724 | static int format_double_radix(double f, | ||
725 | struct fmt_buf *fmt_buf, | ||
726 | vuprintf_push_cb push, | ||
727 | void *userp) | ||
728 | { | ||
729 | struct ap_int ia, fa; | ||
730 | long rc = parse_double(f, &ia, &fa, fmt_buf); | ||
731 | |||
732 | if (UNLIKELY(rc < 0)) { | ||
733 | format_inf_nan(fmt_buf, -rc - 1); | ||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | int count = 0; | ||
738 | |||
739 | /* default precision is 6 for all formats */ | ||
740 | int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision; | ||
741 | |||
742 | int exp = exp; | ||
743 | int explen = 0; | ||
744 | |||
745 | switch (fmt_buf->radixchar & 0x3) | ||
746 | { | ||
747 | case 3: /* %g, %G */ | ||
748 | fmt_buf->precision = prec_rem; | ||
749 | if (prec_rem) { | ||
750 | prec_rem--; | ||
751 | } | ||
752 | case 1: /* %e, %E */ | ||
753 | explen = 2; | ||
754 | break; | ||
755 | default: | ||
756 | break; | ||
757 | } | ||
758 | |||
759 | if (rc > 0 && ia.numchunks > 0) { | ||
760 | /* large integer required */ | ||
761 | init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks))); | ||
762 | rc = 0; | ||
763 | } | ||
764 | |||
765 | const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */ | ||
766 | long f_prec = MIN(fa.len, prec_rem + 1); | ||
767 | char buf[bufoffs + ia.len + f_prec + 1]; | ||
768 | char *p_last = &buf[bufoffs + ia.len]; | ||
769 | char *p_dec = p_last; | ||
770 | char *p_first = format_double_int10(&ia, p_last); | ||
771 | |||
772 | if (explen) { | ||
773 | if (!ia.val && fa.len) { | ||
774 | p_first = p_last = &buf[bufoffs]; | ||
775 | f_prec = -f_prec - 1; /* no lead zeros */ | ||
776 | } | ||
777 | else { /* handles 0e+0 too */ | ||
778 | exp = ia.len - 1; | ||
779 | |||
780 | if (exp) { | ||
781 | prec_rem -= exp; | ||
782 | |||
783 | if (prec_rem < 0) { | ||
784 | p_last += prec_rem + 1; | ||
785 | f_prec = 0; | ||
786 | } | ||
787 | else { | ||
788 | f_prec = MIN(fa.len, prec_rem + 1); | ||
789 | } | ||
790 | } | ||
791 | } | ||
792 | |||
793 | p_dec = p_first + 1; | ||
794 | } | ||
795 | |||
796 | if (f_prec) { | ||
797 | if (rc > 0) { | ||
798 | /* large integer required */ | ||
799 | init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks))); | ||
800 | } | ||
801 | |||
802 | p_last = format_double_frac10(&fa, p_last, f_prec); | ||
803 | |||
804 | if (f_prec < 0) { | ||
805 | f_prec = -f_prec - 1; | ||
806 | exp = f_prec - fa.len; | ||
807 | } | ||
808 | |||
809 | prec_rem -= f_prec; | ||
810 | } | ||
811 | |||
812 | if (prec_rem < 0) { | ||
813 | prec_rem = 0; | ||
814 | p_last--; | ||
815 | |||
816 | if (round_number_string10(p_last, p_last - p_first)) { | ||
817 | /* carried left */ | ||
818 | p_first--; | ||
819 | |||
820 | if (explen) { | ||
821 | /* slide everything left by 1 */ | ||
822 | exp++; | ||
823 | p_dec--; | ||
824 | p_last--; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | |||
829 | if (explen) { | ||
830 | if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */ | ||
831 | /* 'g' is some weird crap */ | ||
832 | /* now that the final exponent is known and everything rounded, | ||
833 | it is possible to decide whether to format similarly to | ||
834 | 'e' or 'f' */ | ||
835 | if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */ | ||
836 | if (exp >= 0) { | ||
837 | /* integer digits will be in the buffer */ | ||
838 | p_dec = p_first + exp + 1; | ||
839 | } | ||
840 | else { | ||
841 | /* we didn't keep leading zeros and need to regenerate | ||
842 | them; space was reserved just in case */ | ||
843 | p_first = memset(p_dec + exp - 1, '0', -exp); | ||
844 | p_dec = p_first + 1; | ||
845 | } | ||
846 | |||
847 | /* suppress exponent */ | ||
848 | explen = 0; | ||
849 | } | ||
850 | |||
851 | if (!fmt_buf->length) { | ||
852 | /* strip any trailing zeros from the fraction */ | ||
853 | while (p_last > p_dec && p_last[-1] == '0') { | ||
854 | p_last--; | ||
855 | } | ||
856 | |||
857 | /* suppress trailing precision fill */ | ||
858 | prec_rem = 0; | ||
859 | } | ||
860 | } | ||
861 | |||
862 | if (explen) { | ||
863 | /* build exponent string: 'eħdd' */ | ||
864 | char *p = fmt_buf->bufend; | ||
865 | int signchar = '+'; | ||
866 | |||
867 | if (exp < 0) { | ||
868 | signchar = '-'; | ||
869 | exp = -exp; | ||
870 | } | ||
871 | |||
872 | while (exp || explen < 4) { | ||
873 | *--p = exp % 10 + '0'; | ||
874 | exp /= 10; | ||
875 | explen++; | ||
876 | } | ||
877 | |||
878 | *--p = signchar; | ||
879 | *--p = fmt_buf->radixchar & ~0x2; | ||
880 | } | ||
881 | } | ||
882 | |||
883 | int width = fmt_buf->width; | ||
884 | int point = p_last > p_dec || prec_rem || fmt_buf->length; | ||
885 | int length = p_last - p_first + !!fmt_buf->signchar + point + explen; | ||
886 | |||
887 | if (width) { | ||
888 | if (width - length <= prec_rem) { | ||
889 | width = 0; | ||
890 | } | ||
891 | else { | ||
892 | width -= length + prec_rem; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | rc = -1; | ||
897 | |||
898 | /* left padding */ | ||
899 | if (fmt_buf->alignchar > '0') { | ||
900 | /* space-padded width -- before sign */ | ||
901 | while (width > 0) { | ||
902 | PUSHCHAR(' '); | ||
903 | width--; | ||
904 | } | ||
905 | } | ||
906 | |||
907 | if (fmt_buf->signchar) { | ||
908 | PUSHCHAR(fmt_buf->signchar); | ||
909 | } | ||
910 | |||
911 | if (fmt_buf->alignchar == '0') { | ||
912 | /* zero-padded width -- after sign */ | ||
913 | while (width > 0) { | ||
914 | PUSHCHAR('0'); | ||
915 | width--; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | /* integer part */ | ||
920 | while (p_first < p_dec) { | ||
921 | PUSHCHAR(*p_first++); | ||
922 | } | ||
923 | |||
924 | /* decimal point */ | ||
925 | if (point) { | ||
926 | PUSHCHAR('.'); | ||
927 | } | ||
928 | |||
929 | /* fractional part */ | ||
930 | while (p_first < p_last) { | ||
931 | PUSHCHAR(*p_first++); | ||
932 | } | ||
933 | |||
934 | /* precision 0-padding */ | ||
935 | while (prec_rem > 0) { | ||
936 | PUSHCHAR('0'); | ||
937 | prec_rem--; | ||
938 | } | ||
939 | |||
940 | /* exponent */ | ||
941 | if (explen > 0) { | ||
942 | char *p = fmt_buf->bufend; | ||
943 | while (explen > 0) { | ||
944 | PUSHCHAR(p[-explen--]); | ||
945 | } | ||
946 | } | ||
947 | |||
948 | /* right padding */ | ||
949 | while (width > 0) { | ||
950 | PUSHCHAR(' '); | ||
951 | width--; | ||
952 | } | ||
953 | |||
954 | rc = 1; | ||
955 | done: | ||
956 | fmt_buf->length = count; | ||
957 | return rc; | ||
958 | } | ||
959 | #endif /* FMT_RADIX_floats */ | ||
960 | |||
533 | /* parse fixed width or precision field */ | 961 | /* parse fixed width or precision field */ |
534 | static const char * parse_number_spec(const char *fmt, | 962 | static const char * parse_number_spec(const char *fmt, |
535 | int ch, | 963 | int ch, |
@@ -558,16 +986,6 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
558 | const char *fmt, | 986 | const char *fmt, |
559 | va_list ap) | 987 | va_list ap) |
560 | { | 988 | { |
561 | #define PUSHCHAR(ch) \ | ||
562 | do { \ | ||
563 | int __ch = (ch); \ | ||
564 | int __rc = push(userp, __ch); \ | ||
565 | count += __rc >= 0; \ | ||
566 | if (__rc <= 0) { \ | ||
567 | goto done; \ | ||
568 | } \ | ||
569 | } while (0) | ||
570 | |||
571 | int count = 0; | 989 | int count = 0; |
572 | int ch; | 990 | int ch; |
573 | 991 | ||
@@ -706,6 +1124,9 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
706 | #if (FMT_LENMOD & FMT_LENMOD_z) | 1124 | #if (FMT_LENMOD & FMT_LENMOD_z) |
707 | case 'z': | 1125 | case 'z': |
708 | #endif | 1126 | #endif |
1127 | #if (FMT_LENMOD & FMT_LENMOD_L) | ||
1128 | case 'L': | ||
1129 | #endif | ||
709 | lenmod = ch; | 1130 | lenmod = ch; |
710 | ch = *fmt++; | 1131 | ch = *fmt++; |
711 | #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll)) | 1132 | #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll)) |
@@ -747,6 +1168,40 @@ int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */ | |||
747 | break; | 1168 | break; |
748 | #endif | 1169 | #endif |
749 | 1170 | ||
1171 | #if (FMT_RADIX & FMT_RADIX_floats) | ||
1172 | /* any floats gets all of them (except with 'L' and %a, %A for now) */ | ||
1173 | case 'e': | ||
1174 | case 'E': | ||
1175 | case 'f': | ||
1176 | case 'F': | ||
1177 | case 'g': | ||
1178 | case 'G': | ||
1179 | /* LENMOD_L isn't supported for now and will be rejected automatically */ | ||
1180 | |||
1181 | /* floating point has very different spec interpretations to other | ||
1182 | formats and requires special handling */ | ||
1183 | fmt_buf.length = pfxlen; | ||
1184 | fmt_buf.lenmod = lenmod; | ||
1185 | fmt_buf.radixchar = ch; | ||
1186 | fmt_buf.signchar = signchar; | ||
1187 | fmt_buf.alignchar = alignchar; | ||
1188 | fmt_buf.width = width; | ||
1189 | fmt_buf.precision = precision; | ||
1190 | |||
1191 | ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp); | ||
1192 | if (ch) { | ||
1193 | count += fmt_buf.length; | ||
1194 | if (ch > 0) { | ||
1195 | continue; | ||
1196 | } | ||
1197 | |||
1198 | goto done; | ||
1199 | } | ||
1200 | |||
1201 | buf = fmt_buf.p; | ||
1202 | break; | ||
1203 | #endif | ||
1204 | |||
750 | /** signed integer **/ | 1205 | /** signed integer **/ |
751 | case 'd': | 1206 | case 'd': |
752 | case 'i': | 1207 | case 'i': |
diff --git a/firmware/include/ap_int.h b/firmware/include/ap_int.h new file mode 100644 index 0000000000..68cbb2fb71 --- /dev/null +++ b/firmware/include/ap_int.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2018 by Michael A. Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef AP_INT_H | ||
22 | #define AP_INT_H | ||
23 | |||
24 | /* Miscellaneous large-sized integer functions */ | ||
25 | |||
26 | #include <stdbool.h> | ||
27 | #include <stdint.h> | ||
28 | |||
29 | /* return floor(log(2)*base2exp) - assists in estimating buffer sizes | ||
30 | * when converting to decimal */ | ||
31 | static inline int base10exp(int base2exp) | ||
32 | { | ||
33 | /* 1292913986 = floor(2^32*log(2)) */ | ||
34 | static const long log10of2 = 1292913986L; | ||
35 | return log10of2 * (int64_t)base2exp >> 32; | ||
36 | } | ||
37 | |||
38 | struct ap_int | ||
39 | { | ||
40 | long numchunks; /* number of uint32_t chunks or zero */ | ||
41 | long basechunk; /* chunk of start of value bits */ | ||
42 | uint32_t *chunks; /* pointer to chunk array (caller alloced) */ | ||
43 | long len; /* length of output */ | ||
44 | long shift; /* number of fractional bits */ | ||
45 | uint64_t val; /* value, if it fits and numchunks is zero */ | ||
46 | }; | ||
47 | |||
48 | bool round_number_string10(char *p_rdig, long len); | ||
49 | |||
50 | /* format arbitrary-precision base 10 integer */ | ||
51 | char * format_ap_int10(struct ap_int *a, | ||
52 | char *p_end); | ||
53 | |||
54 | /* format arbitrary-precision base 10 fraction */ | ||
55 | char * format_ap_frac10(struct ap_int *a, | ||
56 | char *p_start, | ||
57 | long precision); | ||
58 | |||
59 | #endif /* AP_INT_H */ | ||