FastLED 3.9.15
Loading...
Searching...
No Matches
math8.h
Go to the documentation of this file.
1#pragma once
2
3#include "lib8tion/config.h"
4#include "scale8.h"
6#include "intmap.h"
7#include "fl/namespace.h"
8
10
11
16
18
22
25
35
40LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j) {
41#if QADD8_C == 1
42 unsigned int t = i + j;
43 if (t > 255)
44 t = 255;
45 return static_cast<uint8_t>(t);
46#elif QADD8_AVRASM == 1
47 asm volatile(
48 /* First, add j to i, conditioning the C flag */
49 "add %0, %1 \n\t"
50
51 /* Now test the C flag.
52 If C is clear, we branch around a load of 0xFF into i.
53 If C is set, we go ahead and load 0xFF into i.
54 */
55 "brcc L_%= \n\t"
56 "ldi %0, 0xFF \n\t"
57 "L_%=: "
58 : "+d"(i) // r16-r31, restricted by ldi
59 : "r"(j));
60 return i;
61#elif QADD8_ARM_DSP_ASM == 1
62 asm volatile("uqadd8 %0, %0, %1" : "+r"(i) : "r"(j));
63 return i;
64#else
65#error "No implementation for qadd8 available."
66#endif
67}
68
73LIB8STATIC_ALWAYS_INLINE int8_t qadd7(int8_t i, int8_t j) {
74#if QADD7_C == 1
75 int16_t t = i + j;
76 if (t > 127)
77 t = 127;
78 else if (t < -128)
79 t = -128;
80 return static_cast<int8_t>(t);
81#elif QADD7_AVRASM == 1
82 asm volatile(
83 /* First, add j to i, conditioning the V and C flags */
84 "add %0, %1 \n\t"
85
86 /* Now test the V flag.
87 If V is clear, we branch to end.
88 If V is set, we go ahead and load 0x7F into i.
89 */
90 "brvc L_%= \n\t"
91 "ldi %0, 0x7F \n\t"
92
93 /* When both numbers are negative, C is set.
94 Adding it to make result negative. */
95 "adc %0, __zero_reg__\n\t"
96 "L_%=: "
97 : "+d"(i) // r16-r31, restricted by ldi
98 : "r"(j));
99 return i;
100#elif QADD7_ARM_DSP_ASM == 1
101 asm volatile("qadd8 %0, %0, %1" : "+r"(i) : "r"(j));
102 return i;
103#else
104#error "No implementation for qadd7 available."
105#endif
106}
107
112LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j) {
113#if QSUB8_C == 1
114 int t = i - j;
115 if (t < 0)
116 t = 0;
117 return static_cast<uint8_t>(t);
118#elif QSUB8_AVRASM == 1
119
120 asm volatile(
121 /* First, subtract j from i, conditioning the C flag */
122 "sub %0, %1 \n\t"
123
124 /* Now test the C flag.
125 If C is clear, we branch around a load of 0x00 into i.
126 If C is set, we go ahead and load 0x00 into i.
127 */
128 "brcc L_%= \n\t"
129 "ldi %0, 0x00 \n\t"
130 "L_%=: "
131 : "+d"(i) // r16-r31, restricted by ldi
132 : "r"(j));
133 return i;
134#else
135#error "No implementation for qsub8 available."
136#endif
137}
138
144LIB8STATIC_ALWAYS_INLINE uint8_t add8(uint8_t i, uint8_t j) {
145#if ADD8_C == 1
146 int t = i + j;
147 return static_cast<uint8_t>(t);
148#elif ADD8_AVRASM == 1
149 // Add j to i, period.
150 asm volatile("add %0, %1" : "+r"(i) : "r"(j));
151 return i;
152#else
153#error "No implementation for add8 available."
154#endif
155}
156
162LIB8STATIC_ALWAYS_INLINE uint16_t add8to16(uint8_t i, uint16_t j) {
163#if ADD8_C == 1
164 uint16_t t = i + j;
165 return t;
166#elif ADD8_AVRASM == 1
167 // Add i(one byte) to j(two bytes)
168 asm volatile("add %A[j], %[i] \n\t"
169 "adc %B[j], __zero_reg__ \n\t"
170 : [j] "+r"(j)
171 : [i] "r"(i));
172 return i;
173#else
174#error "No implementation for add8to16 available."
175#endif
176}
177
183LIB8STATIC_ALWAYS_INLINE uint8_t sub8(uint8_t i, uint8_t j) {
184#if SUB8_C == 1
185 int t = i - j;
186 return static_cast<uint8_t>(t);
187#elif SUB8_AVRASM == 1
188 // Subtract j from i, period.
189 asm volatile("sub %0, %1" : "+r"(i) : "r"(j));
190 return i;
191#else
192#error "No implementation for sub8 available."
193#endif
194}
195
202LIB8STATIC_ALWAYS_INLINE uint8_t avg8(uint8_t i, uint8_t j) {
203#if AVG8_C == 1
204 return (i + j) >> 1;
205#elif AVG8_AVRASM == 1
206 asm volatile(
207 /* First, add j to i, 9th bit overflows into C flag */
208 "add %0, %1 \n\t"
209 /* Divide by two, moving C flag into high 8th bit */
210 "ror %0 \n\t"
211 : "+r"(i)
212 : "r"(j));
213 return i;
214#else
215#error "No implementation for avg8 available."
216#endif
217}
218
225LIB8STATIC_ALWAYS_INLINE uint16_t avg16(uint16_t i, uint16_t j) {
226#if AVG16_C == 1
227 // return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;
228 uint32_t tmp = i;
229 tmp += j;
230 return static_cast<uint16_t>(tmp >> 1);
231#elif AVG16_AVRASM == 1
232 asm volatile(
233 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
234 "add %A[i], %A[j] \n\t"
235 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */
236 "adc %B[i], %B[j] \n\t"
237 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now
238 in
239 C */
240 "ror %B[i] \n\t"
241 /* Divide iLo by two, moving C flag into high 8th bit */
242 "ror %A[i] \n\t"
243 : [i] "+r"(i)
244 : [j] "r"(j));
245 return i;
246#else
247#error "No implementation for avg16 available."
248#endif
249}
250
257LIB8STATIC_ALWAYS_INLINE uint8_t avg8r(uint8_t i, uint8_t j) {
258#if AVG8R_C == 1
259 return (i + j + 1) >> 1;
260#elif AVG8R_AVRASM == 1
261 asm volatile(
262 /* First, add j to i, 9th bit overflows into C flag */
263 "add %0, %1 \n\t"
264 /* Divide by two, moving C flag into high 8th bit, old 1st bit now in C
265 */
266 "ror %0 \n\t"
267 /* Add C flag */
268 "adc %0, __zero_reg__\n\t"
269 : "+r"(i)
270 : "r"(j));
271 return i;
272#else
273#error "No implementation for avg8r available."
274#endif
275}
276
283LIB8STATIC_ALWAYS_INLINE uint16_t avg16r(uint16_t i, uint16_t j) {
284#if AVG16R_C == 1
285 // return (uint32_t)((uint32_t)(i) + (uint32_t)(j) + 1) >> 1;
286 uint32_t tmp = i;
287 tmp += j;
288 tmp += 1;
289 return static_cast<uint16_t>(tmp >> 1);
290#elif AVG16R_AVRASM == 1
291 asm volatile(
292 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
293 "add %A[i], %A[j] \n\t"
294 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */
295 "adc %B[i], %B[j] \n\t"
296 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now
297 in
298 C */
299 "ror %B[i] \n\t"
300 /* Divide iLo by two, moving C flag into high 8th bit, old 1st bit now
301 in
302 C */
303 "ror %A[i] \n\t"
304 /* Add C flag */
305 "adc %A[i], __zero_reg__\n\t"
306 "adc %B[i], __zero_reg__\n\t"
307 : [i] "+r"(i)
308 : [j] "r"(j));
309 return i;
310#else
311#error "No implementation for avg16r available."
312#endif
313}
314
322LIB8STATIC_ALWAYS_INLINE int8_t avg7(int8_t i, int8_t j) {
323#if AVG7_C == 1
324 return (i >> 1) + (j >> 1) + (i & 0x1);
325#elif AVG7_AVRASM == 1
326 asm volatile("asr %1 \n\t"
327 "asr %0 \n\t"
328 "adc %0, %1 \n\t"
329 : "+r"(i)
330 : "r"(j));
331 return i;
332#else
333#error "No implementation for avg7 available."
334#endif
335}
336
344LIB8STATIC_ALWAYS_INLINE int16_t avg15(int16_t i, int16_t j) {
345#if AVG15_C == 1
346 return (i >> 1) + (j >> 1) + (i & 0x1);
347#elif AVG15_AVRASM == 1
348 asm volatile(
349 /* first divide j by 2, throwing away lowest bit */
350 "asr %B[j] \n\t"
351 "ror %A[j] \n\t"
352 /* now divide i by 2, with lowest bit going into C */
353 "asr %B[i] \n\t"
354 "ror %A[i] \n\t"
355 /* add j + C to i */
356 "adc %A[i], %A[j] \n\t"
357 "adc %B[i], %B[j] \n\t"
358 : [i] "+r"(i)
359 : [j] "r"(j));
360 return i;
361#else
362#error "No implementation for avg15 available."
363#endif
364}
365
377LIB8STATIC_ALWAYS_INLINE uint8_t mod8(uint8_t a, uint8_t m) {
378#if defined(__AVR__)
379 asm volatile("L_%=: sub %[a],%[m] \n\t"
380 " brcc L_%= \n\t"
381 " add %[a],%[m] \n\t"
382 : [a] "+r"(a)
383 : [m] "r"(m));
384#else
385 while (a >= m)
386 a -= m;
387#endif
388 return a;
389}
390
408LIB8STATIC uint8_t addmod8(uint8_t a, uint8_t b, uint8_t m) {
409#if defined(__AVR__)
410 asm volatile(" add %[a],%[b] \n\t"
411 "L_%=: sub %[a],%[m] \n\t"
412 " brcc L_%= \n\t"
413 " add %[a],%[m] \n\t"
414 : [a] "+r"(a)
415 : [b] "r"(b), [m] "r"(m));
416#else
417 a += b;
418 while (a >= m)
419 a -= m;
420#endif
421 return a;
422}
423
441LIB8STATIC uint8_t submod8(uint8_t a, uint8_t b, uint8_t m) {
442#if defined(__AVR__)
443 asm volatile(" sub %[a],%[b] \n\t"
444 "L_%=: sub %[a],%[m] \n\t"
445 " brcc L_%= \n\t"
446 " add %[a],%[m] \n\t"
447 : [a] "+r"(a)
448 : [b] "r"(b), [m] "r"(m));
449#else
450 a -= b;
451 while (a >= m)
452 a -= m;
453#endif
454 return a;
455}
456
462LIB8STATIC_ALWAYS_INLINE uint8_t mul8(uint8_t i, uint8_t j) {
463#if MUL8_C == 1
464 return ((int)i * (int)(j)) & 0xFF;
465#elif MUL8_AVRASM == 1
466 asm volatile(
467 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
468 "mul %0, %1 \n\t"
469 /* Extract the LOW 8-bits (r0) */
470 "mov %0, r0 \n\t"
471 /* Restore r1 to "0"; it's expected to always be that */
472 "clr __zero_reg__ \n\t"
473 : "+r"(i)
474 : "r"(j)
475 : "r0", "r1");
476 return i;
477#else
478#error "No implementation for mul8 available."
479#endif
480}
481
486LIB8STATIC_ALWAYS_INLINE uint8_t qmul8(uint8_t i, uint8_t j) {
487#if QMUL8_C == 1
488 unsigned p = (unsigned)i * (unsigned)j;
489 if (p > 255)
490 p = 255;
491 return p;
492#elif QMUL8_AVRASM == 1
493 asm volatile(
494 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
495 " mul %0, %1 \n\t"
496 /* Extract the LOW 8-bits (r0) */
497 " mov %0, r0 \n\t"
498 /* If high byte of result is zero, all is well. */
499 " tst r1 \n\t"
500 " breq Lnospill_%= \n\t"
501 /* If high byte of result > 0, saturate to 0xFF */
502 " ldi %0, 0xFF \n\t"
503 "Lnospill_%=: \n\t"
504 /* Restore r1 to "0"; it's expected to always be that */
505 " clr __zero_reg__ \n\t"
506 : "+d"(i) // r16-r31, restricted by ldi
507 : "r"(j)
508 : "r0", "r1");
509 return i;
510#else
511#error "No implementation for qmul8 available."
512#endif
513}
514
517#if ABS8_C == 1
518 if (i < 0)
519 i = -i;
520 return i;
521#elif ABS8_AVRASM == 1
522 asm volatile(
523 /* First, check the high bit, and prepare to skip if it's clear */
524 "sbrc %0, 7 \n"
525
526 /* Negate the value */
527 "neg %0 \n"
528
529 : "+r"(i)
530 : "r"(i));
531 return i;
532#else
533#error "No implementation for abs8 available."
534#endif
535}
536
540LIB8STATIC uint8_t sqrt16(uint16_t x) {
541 if (x <= 1) {
542 return x;
543 }
544
545 uint8_t low = 1; // lower bound
546 uint8_t hi, mid;
547
548 if (x > 7904) {
549 hi = 255;
550 } else {
551 hi = (x >> 5) + 8; // initial estimate for upper bound
552 }
553
554 do {
555 mid = (low + hi) >> 1;
556 if ((uint16_t)(mid * mid) > x) {
557 hi = mid - 1;
558 } else {
559 if (mid == 255) {
560 return 255;
561 }
562 low = mid + 1;
563 }
564 } while (hi >= low);
565
566 return low - 1;
567}
568
570 return sqrt16(map8_to_16(x));
571}
572
578#if (FASTLED_BLEND_FIXED == 1)
579LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB) {
580
581 // The BLEND_FIXED formula is
582 //
583 // result = ( A*(amountOfA) + B*(amountOfB) )/ 256
584 //
585 // …where amountOfA = 255-amountOfB.
586 //
587 // This formula will never return 255, which is why the BLEND_FIXED +
588 // SCALE8_FIXED version is
589 //
590 // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256
591 //
592 // We can rearrange this formula for some great optimisations.
593 //
594 // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256
595 // = ( A*(255-amountOfB) + A + B*(amountOfB) + B ) / 256
596 // = ( A*(256-amountOfB) + B*(amountOfB) + B ) / 256
597 // = ( A*256 + B + B*(amountOfB) - A*(amountOfB) ) / 256 // this
598 // is the version used in SCALE8_FIXED AVR below = ( A*256 + B +
599 // (B-A)*(amountOfB) ) / 256 // this is the version
600 // used in SCALE8_FIXED C below
601
602 uint16_t partial;
603 uint8_t result;
604
605#if BLEND8_C == 1
606
607#if (FASTLED_SCALE8_FIXED == 1)
608 partial = (a << 8) | b; // A*256 + B
609
610 // on many platforms this compiles to a single multiply of (B-A) * amountOfB
611 partial += (b * amountOfB);
612 partial -= (a * amountOfB);
613
614#else
615 uint8_t amountOfA = 255 - amountOfB;
616
617 // on the other hand, this compiles to two multiplies, and gives the "wrong"
618 // answer :]
619 partial = (a * amountOfA);
620 partial += (b * amountOfB);
621#endif
622
623 result = partial >> 8;
624
625 return result;
626
627#elif BLEND8_AVRASM == 1
628
629#if (FASTLED_SCALE8_FIXED == 1)
630
631 // 1 or 2 cycles depending on how the compiler optimises
632 partial = (a << 8) | b;
633
634 // 7 cycles
635 asm volatile(" mul %[a], %[amountOfB] \n\t"
636 " sub %A[partial], r0 \n\t"
637 " sbc %B[partial], r1 \n\t"
638 " mul %[b], %[amountOfB] \n\t"
639 " add %A[partial], r0 \n\t"
640 " adc %B[partial], r1 \n\t"
641 " clr __zero_reg__ \n\t"
642 : [partial] "+r"(partial)
643 : [amountOfB] "r"(amountOfB), [a] "r"(a), [b] "r"(b)
644 : "r0", "r1");
645
646#else
647
648 // non-SCALE8-fixed version
649
650 // 7 cycles
651 asm volatile(
652 /* partial = b * amountOfB */
653 " mul %[b], %[amountOfB] \n\t"
654 " movw %A[partial], r0 \n\t"
655
656 /* amountOfB (aka amountOfA) = 255 - amountOfB */
657 " com %[amountOfB] \n\t"
658
659 /* partial += a * amountOfB (aka amountOfA) */
660 " mul %[a], %[amountOfB] \n\t"
661
662 " add %A[partial], r0 \n\t"
663 " adc %B[partial], r1 \n\t"
664
665 " clr __zero_reg__ \n\t"
666
667 : [partial] "=r"(partial), [amountOfB] "+r"(amountOfB)
668 : [a] "r"(a), [b] "r"(b)
669 : "r0", "r1");
670
671#endif
672
673 result = partial >> 8;
674
675 return result;
676
677#else
678#error "No implementation for blend8 available."
679#endif
680}
681
682#else
683LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB) {
684 // This version loses precision in the integer math
685 // and can actually return results outside of the range
686 // from a to b. Its use is not recommended.
687 uint8_t result;
688 uint8_t amountOfA = 255 - amountOfB;
689 result = scale8_LEAVING_R1_DIRTY(a, amountOfA) +
690 scale8_LEAVING_R1_DIRTY(b, amountOfB);
691 cleanup_R1();
692 return result;
693}
694#endif
695
698
700
int x
Definition simple.h:92
#define FL_DISABLE_WARNING_RETURN_TYPE
#define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP
#define FL_DISABLE_WARNING_UNUSED_PARAMETER
static uint32_t t
Definition Luminova.h:54
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
Add one byte to another, saturating at 0xFF.
Definition math8.h:40
LIB8STATIC_ALWAYS_INLINE int8_t abs8(int8_t i)
Take the absolute value of a signed 8-bit uint8_t.
Definition math8.h:516
LIB8STATIC_ALWAYS_INLINE uint8_t qmul8(uint8_t i, uint8_t j)
8x8 bit multiplication with 8-bit result, saturating at 0xFF.
Definition math8.h:486
LIB8STATIC_ALWAYS_INLINE uint16_t avg16(uint16_t i, uint16_t j)
Calculate an integer average of two unsigned 16-bit integer values (uint16_t), rounded down.
Definition math8.h:225
LIB8STATIC_ALWAYS_INLINE int16_t avg15(int16_t i, int16_t j)
Calculate an integer average of two signed 15-bit integers (int16_t).
Definition math8.h:344
LIB8STATIC uint8_t addmod8(uint8_t a, uint8_t b, uint8_t m)
Add two numbers, and calculate the modulo of the sum and a third number, M.
Definition math8.h:408
LIB8STATIC_ALWAYS_INLINE uint16_t avg16r(uint16_t i, uint16_t j)
Calculate an integer average of two unsigned 16-bit integer values (uint16_t), rounded up.
Definition math8.h:283
LIB8STATIC_ALWAYS_INLINE int8_t qadd7(int8_t i, int8_t j)
Add one byte to another, saturating at 0x7F and -0x80.
Definition math8.h:73
LIB8STATIC_ALWAYS_INLINE uint8_t avg8(uint8_t i, uint8_t j)
Calculate an integer average of two unsigned 8-bit integer values (uint8_t), rounded down.
Definition math8.h:202
LIB8STATIC uint8_t sqrt16(uint16_t x)
Square root for 16-bit integers.
Definition math8.h:540
LIB8STATIC_ALWAYS_INLINE uint8_t add8(uint8_t i, uint8_t j)
Add one byte to another, with 8-bit result.
Definition math8.h:144
LIB8STATIC_ALWAYS_INLINE uint8_t avg8r(uint8_t i, uint8_t j)
Calculate an integer average of two unsigned 8-bit integer values (uint8_t), rounded up.
Definition math8.h:257
LIB8STATIC uint8_t submod8(uint8_t a, uint8_t b, uint8_t m)
Subtract two numbers, and calculate the modulo of the difference and a third number,...
Definition math8.h:441
LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB)
Blend a variable proportion (0-255) of one byte to another.
Definition math8.h:683
LIB8STATIC_ALWAYS_INLINE uint8_t mod8(uint8_t a, uint8_t m)
Calculate the remainder of one unsigned 8-bit value divided by anoter, aka A % M.
Definition math8.h:377
LIB8STATIC_ALWAYS_INLINE uint8_t sqrt8(uint8_t x)
Definition math8.h:569
LIB8STATIC_ALWAYS_INLINE uint16_t add8to16(uint8_t i, uint16_t j)
Add one byte to two bytes, with 16-bit result.
Definition math8.h:162
LIB8STATIC_ALWAYS_INLINE uint8_t sub8(uint8_t i, uint8_t j)
Subtract one byte from another, 8-bit result.
Definition math8.h:183
LIB8STATIC_ALWAYS_INLINE int8_t avg7(int8_t i, int8_t j)
Calculate an integer average of two signed 7-bit integers (int8_t).
Definition math8.h:322
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j)
Subtract one byte from another, saturating at 0x00.
Definition math8.h:112
LIB8STATIC_ALWAYS_INLINE uint8_t mul8(uint8_t i, uint8_t j)
8x8 bit multiplication, with 8-bit result.
Definition math8.h:462
LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls.
Definition scale8.h:343
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY(uint8_t i, fract8 scale)
This version of scale8() does not clean up the R1 register on AVR.
Definition scale8.h:180
LIB8STATIC_ALWAYS_INLINE uint16_t map8_to_16(uint8_t x)
Definition intmap.h:25
#define LIB8STATIC
Define a LIB8TION member function as static inline with an "unused" attribute.
Definition lib8static.h:10
#define LIB8STATIC_ALWAYS_INLINE
Define a LIB8TION member function as always static inline.
Definition lib8static.h:12
Defines integer mapping functions.
Defines static inlining macros for lib8tion functions.
#define FASTLED_NAMESPACE_END
Definition namespace.h:23
#define FASTLED_NAMESPACE_BEGIN
Definition namespace.h:22
Implements the FastLED namespace macros.
Fast, efficient 8-bit scaling functions specifically designed for high-performance LED programming.