FastLED 3.6.0
Loading...
Searching...
No Matches
math8.h
Go to the documentation of this file.
1#ifndef __INC_LIB8TION_MATH_H
2#define __INC_LIB8TION_MATH_H
3
4#include "scale8.h"
5
6
10
13
23
28LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j)
29{
30#if QADD8_C == 1
31 unsigned int t = i + j;
32 if( t > 255) t = 255;
33 return t;
34#elif QADD8_AVRASM == 1
35 asm volatile(
36 /* First, add j to i, conditioning the C flag */
37 "add %0, %1 \n\t"
38
39 /* Now test the C flag.
40 If C is clear, we branch around a load of 0xFF into i.
41 If C is set, we go ahead and load 0xFF into i.
42 */
43 "brcc L_%= \n\t"
44 "ldi %0, 0xFF \n\t"
45 "L_%=: "
46 : "+a" (i)
47 : "a" (j)
48 );
49 return i;
50#elif QADD8_ARM_DSP_ASM == 1
51 asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
52 return i;
53#else
54#error "No implementation for qadd8 available."
55#endif
56}
57
62LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j)
63{
64#if QADD7_C == 1
65 int16_t t = i + j;
66 if( t > 127) t = 127;
67 else if( t < -128) t = -128;
68 return t;
69#elif QADD7_AVRASM == 1
70 asm volatile(
71 /* First, add j to i, conditioning the V and C flags */
72 "add %0, %1 \n\t"
73
74 /* Now test the V flag.
75 If V is clear, we branch to end.
76 If V is set, we go ahead and load 0x7F into i.
77 */
78 "brvc L_%= \n\t"
79 "ldi %0, 0x7F \n\t"
80
81 /* When both numbers are negative, C is set.
82 Adding it to make result negative. */
83 "adc %0, __zero_reg__\n\t"
84 "L_%=: "
85 : "+a" (i)
86 : "a" (j)
87 );
88 return i;
89#elif QADD7_ARM_DSP_ASM == 1
90 asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
91 return i;
92#else
93#error "No implementation for qadd7 available."
94#endif
95}
96
101LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j)
102{
103#if QSUB8_C == 1
104 int t = i - j;
105 if( t < 0) t = 0;
106 return t;
107#elif QSUB8_AVRASM == 1
108
109 asm volatile(
110 /* First, subtract j from i, conditioning the C flag */
111 "sub %0, %1 \n\t"
112
113 /* Now test the C flag.
114 If C is clear, we branch around a load of 0x00 into i.
115 If C is set, we go ahead and load 0x00 into i.
116 */
117 "brcc L_%= \n\t"
118 "ldi %0, 0x00 \n\t"
119 "L_%=: "
120 : "+a" (i)
121 : "a" (j)
122 );
123 return i;
124#else
125#error "No implementation for qsub8 available."
126#endif
127}
128
134LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j)
135{
136#if ADD8_C == 1
137 int t = i + j;
138 return t;
139#elif ADD8_AVRASM == 1
140 // Add j to i, period.
141 asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
142 return i;
143#else
144#error "No implementation for add8 available."
145#endif
146}
147
153LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j)
154{
155#if ADD8_C == 1
156 uint16_t t = i + j;
157 return t;
158#elif ADD8_AVRASM == 1
159 // Add i(one byte) to j(two bytes)
160 asm volatile(
161 "add %A[j], %[i] \n\t"
162 "adc %B[j], __zero_reg__ \n\t"
163 : [j] "+a" (j)
164 : [i] "a" (i)
165 );
166 return i;
167#else
168#error "No implementation for add8to16 available."
169#endif
170}
171
172
178LIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j)
179{
180#if SUB8_C == 1
181 int t = i - j;
182 return t;
183#elif SUB8_AVRASM == 1
184 // Subtract j from i, period.
185 asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
186 return i;
187#else
188#error "No implementation for sub8 available."
189#endif
190}
191
198LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j)
199{
200#if AVG8_C == 1
201 return (i + j) >> 1;
202#elif AVG8_AVRASM == 1
203 asm volatile(
204 /* First, add j to i, 9th bit overflows into C flag */
205 "add %0, %1 \n\t"
206 /* Divide by two, moving C flag into high 8th bit */
207 "ror %0 \n\t"
208 : "+a" (i)
209 : "a" (j)
210 );
211 return i;
212#else
213#error "No implementation for avg8 available."
214#endif
215}
216
223LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j)
224{
225#if AVG16_C == 1
226 return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;
227#elif AVG16_AVRASM == 1
228 asm volatile(
229 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
230 "add %A[i], %A[j] \n\t"
231 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */
232 "adc %B[i], %B[j] \n\t"
233 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */
234 "ror %B[i] \n\t"
235 /* Divide iLo by two, moving C flag into high 8th bit */
236 "ror %A[i] \n\t"
237 : [i] "+a" (i)
238 : [j] "a" (j)
239 );
240 return i;
241#else
242#error "No implementation for avg16 available."
243#endif
244}
245
252LIB8STATIC_ALWAYS_INLINE uint8_t avg8r( uint8_t i, uint8_t j)
253{
254#if AVG8R_C == 1
255 return (i + j + 1) >> 1;
256#elif AVG8R_AVRASM == 1
257 asm volatile(
258 /* First, add j to i, 9th bit overflows into C flag */
259 "add %0, %1 \n\t"
260 /* Divide by two, moving C flag into high 8th bit, old 1st bit now in C */
261 "ror %0 \n\t"
262 /* Add C flag */
263 "adc %0, __zero_reg__\n\t"
264 : "+a" (i)
265 : "a" (j)
266 );
267 return i;
268#else
269#error "No implementation for avg8r available."
270#endif
271}
272
279LIB8STATIC_ALWAYS_INLINE uint16_t avg16r( uint16_t i, uint16_t j)
280{
281#if AVG16R_C == 1
282 return (uint32_t)((uint32_t)(i) + (uint32_t)(j) + 1) >> 1;
283#elif AVG16R_AVRASM == 1
284 asm volatile(
285 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
286 "add %A[i], %A[j] \n\t"
287 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */
288 "adc %B[i], %B[j] \n\t"
289 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */
290 "ror %B[i] \n\t"
291 /* Divide iLo by two, moving C flag into high 8th bit, old 1st bit now in C */
292 "ror %A[i] \n\t"
293 /* Add C flag */
294 "adc %A[i], __zero_reg__\n\t"
295 "adc %B[i], __zero_reg__\n\t"
296 : [i] "+a" (i)
297 : [j] "a" (j)
298 );
299 return i;
300#else
301#error "No implementation for avg16r available."
302#endif
303}
304
305
313LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j)
314{
315#if AVG7_C == 1
316 return (i>>1) + (j>>1) + (i & 0x1);
317#elif AVG7_AVRASM == 1
318 asm volatile(
319 "asr %1 \n\t"
320 "asr %0 \n\t"
321 "adc %0, %1 \n\t"
322 : "+a" (i)
323 : "a" (j)
324 );
325 return i;
326#else
327#error "No implementation for avg7 available."
328#endif
329}
330
338LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j)
339{
340#if AVG15_C == 1
341 return (i>>1) + (j>>1) + (i & 0x1);
342#elif AVG15_AVRASM == 1
343 asm volatile(
344 /* first divide j by 2, throwing away lowest bit */
345 "asr %B[j] \n\t"
346 "ror %A[j] \n\t"
347 /* now divide i by 2, with lowest bit going into C */
348 "asr %B[i] \n\t"
349 "ror %A[i] \n\t"
350 /* add j + C to i */
351 "adc %A[i], %A[j] \n\t"
352 "adc %B[i], %B[j] \n\t"
353 : [i] "+a" (i)
354 : [j] "a" (j)
355 );
356 return i;
357#else
358#error "No implementation for avg15 available."
359#endif
360}
361
362
374LIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m)
375{
376#if defined(__AVR__)
377 asm volatile (
378 "L_%=: sub %[a],%[m] \n\t"
379 " brcc L_%= \n\t"
380 " add %[a],%[m] \n\t"
381 : [a] "+r" (a)
382 : [m] "r" (m)
383 );
384#else
385 while( a >= m) a -= m;
386#endif
387 return a;
388}
389
407LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)
408{
409#if defined(__AVR__)
410 asm volatile (
411 " add %[a],%[b] \n\t"
412 "L_%=: sub %[a],%[m] \n\t"
413 " brcc L_%= \n\t"
414 " add %[a],%[m] \n\t"
415 : [a] "+r" (a)
416 : [b] "r" (b), [m] "r" (m)
417 );
418#else
419 a += b;
420 while( a >= m) a -= m;
421#endif
422 return a;
423}
424
442LIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m)
443{
444#if defined(__AVR__)
445 asm volatile (
446 " sub %[a],%[b] \n\t"
447 "L_%=: sub %[a],%[m] \n\t"
448 " brcc L_%= \n\t"
449 " add %[a],%[m] \n\t"
450 : [a] "+r" (a)
451 : [b] "r" (b), [m] "r" (m)
452 );
453#else
454 a -= b;
455 while( a >= m) a -= m;
456#endif
457 return a;
458}
459
465LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j)
466{
467#if MUL8_C == 1
468 return ((int)i * (int)(j) ) & 0xFF;
469#elif MUL8_AVRASM == 1
470 asm volatile(
471 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
472 "mul %0, %1 \n\t"
473 /* Extract the LOW 8-bits (r0) */
474 "mov %0, r0 \n\t"
475 /* Restore r1 to "0"; it's expected to always be that */
476 "clr __zero_reg__ \n\t"
477 : "+a" (i)
478 : "a" (j)
479 : "r0", "r1"
480 );
481 return i;
482#else
483#error "No implementation for mul8 available."
484#endif
485}
486
487
492LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j)
493{
494#if QMUL8_C == 1
495 unsigned p = (unsigned)i * (unsigned)j;
496 if( p > 255) p = 255;
497 return p;
498#elif QMUL8_AVRASM == 1
499 asm volatile(
500 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
501 " mul %0, %1 \n\t"
502 /* Extract the LOW 8-bits (r0) */
503 " mov %0, r0 \n\t"
504 /* If high byte of result is zero, all is well. */
505 " tst r1 \n\t"
506 " breq Lnospill_%= \n\t"
507 /* If high byte of result > 0, saturate to 0xFF */
508 " ldi %0, 0xFF \n\t"
509 "Lnospill_%=: \n\t"
510 /* Restore r1 to "0"; it's expected to always be that */
511 " clr __zero_reg__ \n\t"
512 : "+a" (i)
513 : "a" (j)
514 : "r0", "r1"
515 );
516 return i;
517#else
518#error "No implementation for qmul8 available."
519#endif
520}
521
522
525{
526#if ABS8_C == 1
527 if( i < 0) i = -i;
528 return i;
529#elif ABS8_AVRASM == 1
530 asm volatile(
531 /* First, check the high bit, and prepare to skip if it's clear */
532 "sbrc %0, 7 \n"
533
534 /* Negate the value */
535 "neg %0 \n"
536
537 : "+r" (i) : "r" (i)
538 );
539 return i;
540#else
541#error "No implementation for abs8 available."
542#endif
543}
544
548LIB8STATIC uint8_t sqrt16(uint16_t x)
549{
550 if( x <= 1) {
551 return x;
552 }
553
554 uint8_t low = 1; // lower bound
555 uint8_t hi, mid;
556
557 if( x > 7904) {
558 hi = 255;
559 } else {
560 hi = (x >> 5) + 8; // initial estimate for upper bound
561 }
562
563 do {
564 mid = (low + hi) >> 1;
565 if ((uint16_t)(mid * mid) > x) {
566 hi = mid - 1;
567 } else {
568 if( mid == 255) {
569 return 255;
570 }
571 low = mid + 1;
572 }
573 } while (hi >= low);
574
575 return low - 1;
576}
577
583#if (FASTLED_BLEND_FIXED == 1)
584LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
585{
586
587 // The BLEND_FIXED formula is
588 //
589 // result = ( A*(amountOfA) + B*(amountOfB) )/ 256
590 //
591 // …where amountOfA = 255-amountOfB.
592 //
593 // This formula will never return 255, which is why the BLEND_FIXED + SCALE8_FIXED version is
594 //
595 // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256
596 //
597 // We can rearrange this formula for some great optimisations.
598 //
599 // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256
600 // = ( A*(255-amountOfB) + A + B*(amountOfB) + B ) / 256
601 // = ( A*(256-amountOfB) + B*(amountOfB) + B ) / 256
602 // = ( A*256 + B + B*(amountOfB) - A*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED AVR below
603 // = ( A*256 + B + (B-A)*(amountOfB) ) / 256 // this is the version used in SCALE8_FIXED C below
604
605 uint16_t partial;
606 uint8_t result;
607
608#if BLEND8_C == 1
609
610# if (FASTLED_SCALE8_FIXED == 1)
611 partial = (a << 8) | b; // A*256 + B
612
613 // on many platforms this compiles to a single multiply of (B-A) * amountOfB
614 partial += (b * amountOfB);
615 partial -= (a * amountOfB);
616
617# else
618 uint8_t amountOfA = 255 - amountOfB;
619
620 // on the other hand, this compiles to two multiplies, and gives the "wrong" answer :]
621 partial = (a * amountOfA);
622 partial += (b * amountOfB);
623# endif
624
625 result = partial >> 8;
626
627 return result;
628
629#elif BLEND8_AVRASM == 1
630
631# if (FASTLED_SCALE8_FIXED == 1)
632
633 // 1 or 2 cycles depending on how the compiler optimises
634 partial = (a << 8) | b;
635
636 // 7 cycles
637 asm volatile (
638 " mul %[a], %[amountOfB] \n\t"
639 " sub %A[partial], r0 \n\t"
640 " sbc %B[partial], r1 \n\t"
641 " mul %[b], %[amountOfB] \n\t"
642 " add %A[partial], r0 \n\t"
643 " adc %B[partial], r1 \n\t"
644 " clr __zero_reg__ \n\t"
645 : [partial] "+r" (partial)
646 : [amountOfB] "r" (amountOfB),
647 [a] "r" (a),
648 [b] "r" (b)
649 : "r0", "r1"
650 );
651
652# else
653
654 // non-SCALE8-fixed version
655
656 // 7 cycles
657 asm volatile (
658 /* partial = b * amountOfB */
659 " mul %[b], %[amountOfB] \n\t"
660 " movw %A[partial], r0 \n\t"
661
662 /* amountOfB (aka amountOfA) = 255 - amountOfB */
663 " com %[amountOfB] \n\t"
664
665 /* partial += a * amountOfB (aka amountOfA) */
666 " mul %[a], %[amountOfB] \n\t"
667
668 " add %A[partial], r0 \n\t"
669 " adc %B[partial], r1 \n\t"
670
671 " clr __zero_reg__ \n\t"
672
673 : [partial] "=r" (partial),
674 [amountOfB] "+a" (amountOfB)
675 : [a] "a" (a),
676 [b] "a" (b)
677 : "r0", "r1"
678 );
679
680# endif
681
682 result = partial >> 8;
683
684 return result;
685
686#else
687# error "No implementation for blend8 available."
688#endif
689}
690
691#else
692LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
693{
694 // This version loses precision in the integer math
695 // and can actually return results outside of the range
696 // from a to b. Its use is not recommended.
697 uint8_t result;
698 uint8_t amountOfA = 255 - amountOfB;
699 result = scale8_LEAVING_R1_DIRTY( a, amountOfA)
700 + scale8_LEAVING_R1_DIRTY( b, amountOfB);
701 cleanup_R1();
702 return result;
703}
704#endif
705
706
709
710#endif
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
Add one byte to another, saturating at 0xFF.
Definition math8.h:28
LIB8STATIC_ALWAYS_INLINE int8_t abs8(int8_t i)
Take the absolute value of a signed 8-bit uint8_t.
Definition math8.h:524
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:492
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:223
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:338
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:407
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:279
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:62
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:198
LIB8STATIC uint8_t sqrt16(uint16_t x)
Square root for 16-bit integers.
Definition math8.h:548
LIB8STATIC_ALWAYS_INLINE uint8_t add8(uint8_t i, uint8_t j)
Add one byte to another, with 8-bit result.
Definition math8.h:134
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:252
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:442
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:692
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:374
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:153
LIB8STATIC_ALWAYS_INLINE uint8_t sub8(uint8_t i, uint8_t j)
Subtract one byte from another, 8-bit result.
Definition math8.h:178
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:313
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j)
Subtract one byte from another, saturating at 0x00.
Definition math8.h:101
LIB8STATIC_ALWAYS_INLINE uint8_t mul8(uint8_t i, uint8_t j)
8x8 bit multiplication, with 8-bit result.
Definition math8.h:465
LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls.
Definition scale8.h:336
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:169
#define LIB8STATIC
Define a LIB8TION member function as static inline with an "unused" attribute.
Definition lib8tion.h:21
#define LIB8STATIC_ALWAYS_INLINE
Define a LIB8TION member function as always static inline.
Definition lib8tion.h:23
Fast, efficient 8-bit scaling functions specifically designed for high-performance LED programming.