PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

398 lines
9.8KB

  1. /*
  2. Print.cpp - Base class that provides print() and println()
  3. Copyright (c) 2008 David A. Mellis. All right reserved.
  4. many modifications, by Paul Stoffregen <paul@pjrc.com>
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with this library; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. Modified 23 November 2006 by David A. Mellis
  17. */
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <inttypes.h>
  21. #include <math.h>
  22. #include <avr/pgmspace.h>
  23. #include "wiring.h"
  24. #include "Print.h"
  25. size_t Print::write(const uint8_t *buffer, size_t size)
  26. {
  27. size_t count = 0;
  28. while (size--) count += write(*buffer++);
  29. return count;
  30. }
  31. size_t Print::print(const String &s)
  32. {
  33. uint8_t buffer[33];
  34. size_t count = 0;
  35. unsigned int index = 0;
  36. unsigned int len = s.length();
  37. while (len > 0) {
  38. s.getBytes(buffer, sizeof(buffer), index);
  39. unsigned int nbytes = len;
  40. if (nbytes > sizeof(buffer)-1) nbytes = sizeof(buffer)-1;
  41. index += nbytes;
  42. len -= nbytes;
  43. count += write(buffer, nbytes);
  44. }
  45. return count;
  46. }
  47. size_t Print::print(const __FlashStringHelper *ifsh)
  48. {
  49. uint8_t buffer[32];
  50. size_t count = 0;
  51. const char *p = (const char *)ifsh;
  52. unsigned int len = strlen_P(p);
  53. while (len > 0) {
  54. unsigned int nbytes = len;
  55. if (nbytes > sizeof(buffer)) nbytes = sizeof(buffer);
  56. memcpy_P(buffer, p, nbytes);
  57. p += nbytes;
  58. len -= nbytes;
  59. count += write(buffer, nbytes);
  60. }
  61. return count;
  62. }
  63. size_t Print::print(long n)
  64. {
  65. uint8_t sign=0;
  66. if (n < 0) {
  67. sign = 1;
  68. n = -n;
  69. }
  70. return printNumber(n, sign, 10);
  71. }
  72. size_t Print::println(void)
  73. {
  74. uint8_t buf[2]={'\r', '\n'};
  75. return write(buf, 2);
  76. }
  77. static int printf_putchar(char c, FILE *fp)
  78. {
  79. ((class Print *)(fdev_get_udata(fp)))->write((uint8_t)c);
  80. return 0;
  81. }
  82. int Print::printf(const char *format, ...)
  83. {
  84. FILE f;
  85. va_list ap;
  86. fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
  87. fdev_set_udata(&f, this);
  88. va_start(ap, format);
  89. return vfprintf(&f, format, ap);
  90. }
  91. int Print::printf(const __FlashStringHelper *format, ...)
  92. {
  93. FILE f;
  94. va_list ap;
  95. fdev_setup_stream(&f, printf_putchar, NULL, _FDEV_SETUP_WRITE);
  96. fdev_set_udata(&f, this);
  97. va_start(ap, format);
  98. return vfprintf_P(&f, (const char *)format, ap);
  99. }
  100. //#define USE_HACKER_DELIGHT_OPTIMIZATION
  101. #define USE_STIMMER_OPTIMIZATION
  102. //#define USE_BENCHMARK_CODE
  103. #ifdef USE_HACKER_DELIGHT_OPTIMIZATION
  104. // Adapted from Hacker's Delight (Henry Warren, ISBN 0321842685) www.hackersdelight.org
  105. // by Rob Tillaart, Tom Carpenter, "genom2" with input from others...
  106. // http://forum.arduino.cc/index.php?topic=167414.0
  107. //
  108. #define divmod10_asm(in32, tmp32, mod8) \
  109. asm volatile ( \
  110. "mov %2, %A0 \n\t" /* mod = in */ \
  111. "ori %A0, 1 \n\t" /* q = in | 1 */ \
  112. "movw %A1, %A0 \n\t" /* x = q */ \
  113. "movw %C1, %C0 \n\t" \
  114. "lsr %D1 \n\t" /* x = x >> 2 */ \
  115. "ror %C1 \n\t" \
  116. "ror %B1 \n\t" \
  117. "ror %A1 \n\t" \
  118. "lsr %D1 \n\t" \
  119. "ror %C1 \n\t" \
  120. "ror %B1 \n\t" \
  121. "ror %A1 \n\t" \
  122. "sub %A0, %A1 \n\t" /* q = q - x */ \
  123. "sbc %B0, %B1 \n\t" \
  124. "sbc %C0, %C1 \n\t" \
  125. "sbc %D0, %D1 \n\t" \
  126. "movw %A1, %A0 \n\t" /* x = q */ \
  127. "movw %C1, %C0 \n\t" \
  128. "lsr %D1 \n\t" /* x = x >> 4 */ \
  129. "ror %C1 \n\t" \
  130. "ror %B1 \n\t" \
  131. "ror %A1 \n\t" \
  132. "lsr %D1 \n\t" \
  133. "ror %C1 \n\t" \
  134. "ror %B1 \n\t" \
  135. "ror %A1 \n\t" \
  136. "lsr %D1 \n\t" \
  137. "ror %C1 \n\t" \
  138. "ror %B1 \n\t" \
  139. "ror %A1 \n\t" \
  140. "lsr %D1 \n\t" \
  141. "ror %C1 \n\t" \
  142. "ror %B1 \n\t" \
  143. "ror %A1 \n\t" \
  144. "add %A1, %A0 \n\t" /* x = x + q */ \
  145. "adc %B1, %B0 \n\t" \
  146. "adc %C1, %C0 \n\t" \
  147. "adc %D1, %D0 \n\t" \
  148. "movw %A0, %A1 \n\t" /* q = x */ \
  149. "movw %C0, %C1 \n\t" \
  150. "add %A0, %B1 \n\t" /* q = q + (x >> 8) */ \
  151. "adc %B0, %C1 \n\t" \
  152. "adc %C0, %D1 \n\t" \
  153. "adc %D0, r1 \n\t" \
  154. "mov %A0, %B0 \n\t" /* q = q >> 8 */ \
  155. "mov %B0, %C0 \n\t" \
  156. "mov %C0, %D0 \n\t" \
  157. "eor %D0, %D0 \n\t" \
  158. "add %A0, %A1 \n\t" /* q = q + x */ \
  159. "adc %B0, %B1 \n\t" \
  160. "adc %C0, %C1 \n\t" \
  161. "adc %D0, %D1 \n\t" \
  162. "mov %A0, %B0 \n\t" /* q = q >> 8 */ \
  163. "mov %B0, %C0 \n\t" \
  164. "mov %C0, %D0 \n\t" \
  165. "eor %D0, %D0 \n\t" \
  166. "add %A0, %A1 \n\t" /* q = q + x */ \
  167. "adc %B0, %B1 \n\t" \
  168. "adc %C0, %C1 \n\t" \
  169. "adc %D0, %D1 \n\t" \
  170. "mov %A0, %B0 \n\t" /* q = q >> 8 */ \
  171. "mov %B0, %C0 \n\t" \
  172. "mov %C0, %D0 \n\t" \
  173. "eor %D0, %D0 \n\t" \
  174. "add %A0, %A1 \n\t" /* q = q + x */ \
  175. "adc %B0, %B1 \n\t" \
  176. "adc %C0, %C1 \n\t" \
  177. "adc %D0, %D1 \n\t" \
  178. "andi %A0, 0xF8 \n\t" /* q = q & ~0x7 */ \
  179. "sub %2, %A0 \n\t" /* mod = mod - q */ \
  180. "lsr %D0 \n\t" /* q = q >> 2 */ \
  181. "ror %C0 \n\t" \
  182. "ror %B0 \n\t" \
  183. "ror %A0 \n\t" \
  184. "lsr %D0 \n\t" \
  185. "ror %C0 \n\t" \
  186. "ror %B0 \n\t" \
  187. "ror %A0 \n\t" \
  188. "sub %2, %A0 \n\t" /* mod = mod - q */ \
  189. "lsr %D0 \n\t" /* q = q >> 1 */ \
  190. "ror %C0 \n\t" \
  191. "ror %B0 \n\t" \
  192. "ror %A0 \n\t" \
  193. : "+d" (in32), "=r" (tmp32), "=r" (mod8) : : "r0" \
  194. )
  195. #endif // USE_HACKER_DELIGHT_OPTIMIZATION
  196. #ifdef USE_STIMMER_OPTIMIZATION
  197. // http://forum.arduino.cc/index.php?topic=167414.msg1293679#msg1293679
  198. // http://forum.arduino.cc/index.php?topic=167414.msg1309482#msg1309482
  199. // equivelant code:
  200. // mod8 = in32 % 10;
  201. // in32 = in32 / 10;
  202. // tmp8 = 10;
  203. #define divmod10_asm(in32, mod8, tmp8) \
  204. asm volatile ( \
  205. " ldi %2,51 \n\t" \
  206. " mul %A0,%2 \n\t" \
  207. " clr %A0 \n\t" \
  208. " add r0,%2 \n\t" \
  209. " adc %A0,r1 \n\t" \
  210. " mov %1,r0 \n\t" \
  211. " mul %B0,%2 \n\t" \
  212. " clr %B0 \n\t" \
  213. " add %A0,r0 \n\t" \
  214. " adc %B0,r1 \n\t" \
  215. " mul %C0,%2 \n\t" \
  216. " clr %C0 \n\t" \
  217. " add %B0,r0 \n\t" \
  218. " adc %C0,r1 \n\t" \
  219. " mul %D0,%2 \n\t" \
  220. " clr %D0 \n\t" \
  221. " add %C0,r0 \n\t" \
  222. " adc %D0,r1 \n\t" \
  223. " clr r1 \n\t" \
  224. " add %1,%A0 \n\t" \
  225. " adc %A0,%B0 \n\t" \
  226. " adc %B0,%C0 \n\t" \
  227. " adc %C0,%D0 \n\t" \
  228. " adc %D0,r1 \n\t" \
  229. " add %1,%B0 \n\t" \
  230. " adc %A0,%C0 \n\t" \
  231. " adc %B0,%D0 \n\t" \
  232. " adc %C0,r1 \n\t" \
  233. " adc %D0,r1 \n\t" \
  234. " add %1,%D0 \n\t" \
  235. " adc %A0,r1 \n\t" \
  236. " adc %B0,r1 \n\t" \
  237. " adc %C0,r1 \n\t" \
  238. " adc %D0,r1 \n\t" \
  239. " lsr %D0 \n\t" \
  240. " ror %C0 \n\t" \
  241. " ror %B0 \n\t" \
  242. " ror %A0 \n\t" \
  243. " ror %1 \n\t" \
  244. " ldi %2,10 \n\t" \
  245. " mul %1,%2 \n\t" \
  246. " mov %1,r1 \n\t" \
  247. " clr r1 \n\t" \
  248. :"+r"(in32),"=d"(mod8),"=d"(tmp8) : : "r0")
  249. #endif // USE_STIMMER_OPTIMIZATION
  250. #ifdef USE_BENCHMARK_CODE
  251. uint32_t usec_print = 0;
  252. #endif
  253. size_t Print::printNumberDec(unsigned long n, uint8_t sign)
  254. {
  255. uint8_t digit, buf[11], *p;
  256. #if defined(USE_HACKER_DELIGHT_OPTIMIZATION)
  257. uint32_t tmp32;
  258. #elif defined(USE_STIMMER_OPTIMIZATION)
  259. uint8_t tmp8;
  260. #endif
  261. #ifdef USE_BENCHMARK_CODE
  262. uint32_t usec = micros();
  263. #endif
  264. p = buf + (sizeof(buf));
  265. do {
  266. #if defined(USE_HACKER_DELIGHT_OPTIMIZATION)
  267. divmod10_asm(n, tmp32, digit);
  268. #elif defined(USE_STIMMER_OPTIMIZATION)
  269. divmod10_asm(n, digit, tmp8);
  270. #else
  271. tmp32 = n;
  272. n = n / 10;
  273. digit = tmp32 - n * 10;
  274. #endif
  275. *--p = digit + '0';
  276. } while (n);
  277. if (sign) *--p = '-';
  278. #ifdef USE_BENCHMARK_CODE
  279. usec_print += micros() - usec;
  280. #endif
  281. return write(p, sizeof(buf) - (p - buf));
  282. }
  283. size_t Print::printNumberHex(unsigned long n)
  284. {
  285. uint8_t digit, buf[8], *p;
  286. p = buf + (sizeof(buf));
  287. do {
  288. digit = n & 15;
  289. *--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
  290. n >>= 4;
  291. } while (n);
  292. return write(p, sizeof(buf) - (p - buf));
  293. }
  294. size_t Print::printNumberBin(unsigned long n)
  295. {
  296. uint8_t buf[32], *p;
  297. p = buf + (sizeof(buf));
  298. do {
  299. *--p = '0' + ((uint8_t)n & 1);
  300. n >>= 1;
  301. } while (n);
  302. return write(p, sizeof(buf) - (p - buf));
  303. }
  304. size_t Print::printNumberAny(unsigned long n, uint8_t base)
  305. {
  306. uint8_t digit, buf[21], *p;
  307. uint32_t tmp;
  308. //uint32_t usec;
  309. //usec = micros();
  310. p = buf + sizeof(buf);
  311. do {
  312. tmp = n;
  313. n = n / base;
  314. digit = tmp - n * base;
  315. *--p = (digit < 10) ? '0' + digit : 'A' + digit - 10;
  316. } while (n);
  317. //usec_print += micros() - usec;
  318. return write(p, sizeof(buf) - (p - buf));
  319. }
  320. size_t Print::printFloat(double number, uint8_t digits)
  321. {
  322. uint8_t sign=0;
  323. size_t count=0;
  324. // Handle negative numbers
  325. if (number < 0.0) {
  326. sign = 1;
  327. number = -number;
  328. }
  329. // Round correctly so that print(1.999, 2) prints as "2.00"
  330. double rounding = 0.5;
  331. for (uint8_t i=0; i<digits; ++i) {
  332. rounding *= 0.1;
  333. }
  334. number += rounding;
  335. // Extract the integer part of the number and print it
  336. unsigned long int_part = (unsigned long)number;
  337. double remainder = number - (double)int_part;
  338. count += printNumber(int_part, sign, 10);
  339. // Print the decimal point, but only if there are digits beyond
  340. if (digits > 0) {
  341. uint8_t n, buf[8], count=1;
  342. buf[0] = '.';
  343. // Extract digits from the remainder one at a time
  344. if (digits > sizeof(buf) - 1) digits = sizeof(buf) - 1;
  345. while (digits-- > 0) {
  346. remainder *= 10.0;
  347. n = (uint8_t)(remainder);
  348. buf[count++] = '0' + n;
  349. remainder -= n;
  350. }
  351. count += write(buf, count);
  352. }
  353. return count;
  354. }