| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "scale8.h" | ||
| 4 | #include "../lib8tion/lib8static.h" | ||
| 5 | #include "intmap.h" | ||
| 6 | #include "../fl/namespace.h" | ||
| 7 | |||
| 8 | FASTLED_NAMESPACE_BEGIN | ||
| 9 | |||
| 10 | /// @file math8.h | ||
| 11 | /// Fast, efficient 8-bit math functions specifically | ||
| 12 | /// designed for high-performance LED programming. | ||
| 13 | |||
| 14 | /// @ingroup lib8tion | ||
| 15 | /// @{ | ||
| 16 | |||
| 17 | /// @defgroup Math Basic Math Operations | ||
| 18 | /// Fast, efficient 8-bit math functions specifically | ||
| 19 | /// designed for high-performance LED programming. | ||
| 20 | /// | ||
| 21 | /// Because of the AVR (Arduino) and ARM assembly language | ||
| 22 | /// implementations provided, using these functions often | ||
| 23 | /// results in smaller and faster code than the equivalent | ||
| 24 | /// program using plain "C" arithmetic and logic. | ||
| 25 | /// @{ | ||
| 26 | |||
| 27 | /// Add one byte to another, saturating at 0xFF | ||
| 28 | /// @param i first byte to add | ||
| 29 | /// @param j second byte to add | ||
| 30 | /// @returns the sum of i + j, capped at 0xFF | ||
| 31 | LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j) { | ||
| 32 | #if QADD8_C == 1 | ||
| 33 | 179127 | unsigned int t = i + j; | |
| 34 |
11/16✓ Branch 0 taken 1052 times.
✓ Branch 1 taken 53435 times.
✓ Branch 2 taken 290 times.
✓ Branch 3 taken 54197 times.
✓ Branch 4 taken 476 times.
✓ Branch 5 taken 54011 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4214 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 4214 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 6977 times.
✓ Branch 12 taken 104 times.
✓ Branch 13 taken 157 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
179127 | if (t > 255) |
| 35 | 1922 | t = 255; | |
| 36 | 179127 | return t; | |
| 37 | #elif QADD8_AVRASM == 1 | ||
| 38 | asm volatile( | ||
| 39 | /* First, add j to i, conditioning the C flag */ | ||
| 40 | "add %0, %1 \n\t" | ||
| 41 | |||
| 42 | /* Now test the C flag. | ||
| 43 | If C is clear, we branch around a load of 0xFF into i. | ||
| 44 | If C is set, we go ahead and load 0xFF into i. | ||
| 45 | */ | ||
| 46 | "brcc L_%= \n\t" | ||
| 47 | "ldi %0, 0xFF \n\t" | ||
| 48 | "L_%=: " | ||
| 49 | : "+d"(i) // r16-r31, restricted by ldi | ||
| 50 | : "r"(j)); | ||
| 51 | return i; | ||
| 52 | #elif QADD8_ARM_DSP_ASM == 1 | ||
| 53 | asm volatile("uqadd8 %0, %0, %1" : "+r"(i) : "r"(j)); | ||
| 54 | return i; | ||
| 55 | #else | ||
| 56 | #error "No implementation for qadd8 available." | ||
| 57 | #endif | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Add one byte to another, saturating at 0x7F and -0x80 | ||
| 61 | /// @param i first byte to add | ||
| 62 | /// @param j second byte to add | ||
| 63 | /// @returns the sum of i + j, capped at 0x7F and -0x80 | ||
| 64 | LIB8STATIC_ALWAYS_INLINE int8_t qadd7(int8_t i, int8_t j) { | ||
| 65 | #if QADD7_C == 1 | ||
| 66 | int16_t t = i + j; | ||
| 67 | if (t > 127) | ||
| 68 | t = 127; | ||
| 69 | else if (t < -128) | ||
| 70 | t = -128; | ||
| 71 | return t; | ||
| 72 | #elif QADD7_AVRASM == 1 | ||
| 73 | asm volatile( | ||
| 74 | /* First, add j to i, conditioning the V and C flags */ | ||
| 75 | "add %0, %1 \n\t" | ||
| 76 | |||
| 77 | /* Now test the V flag. | ||
| 78 | If V is clear, we branch to end. | ||
| 79 | If V is set, we go ahead and load 0x7F into i. | ||
| 80 | */ | ||
| 81 | "brvc L_%= \n\t" | ||
| 82 | "ldi %0, 0x7F \n\t" | ||
| 83 | |||
| 84 | /* When both numbers are negative, C is set. | ||
| 85 | Adding it to make result negative. */ | ||
| 86 | "adc %0, __zero_reg__\n\t" | ||
| 87 | "L_%=: " | ||
| 88 | : "+d"(i) // r16-r31, restricted by ldi | ||
| 89 | : "r"(j)); | ||
| 90 | return i; | ||
| 91 | #elif QADD7_ARM_DSP_ASM == 1 | ||
| 92 | asm volatile("qadd8 %0, %0, %1" : "+r"(i) : "r"(j)); | ||
| 93 | return i; | ||
| 94 | #else | ||
| 95 | #error "No implementation for qadd7 available." | ||
| 96 | #endif | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Subtract one byte from another, saturating at 0x00 | ||
| 100 | /// @param i byte to subtract from | ||
| 101 | /// @param j byte to subtract | ||
| 102 | /// @returns i - j with a floor of 0 | ||
| 103 | LIB8STATIC_ALWAYS_INLINE uint8_t qsub8(uint8_t i, uint8_t j) { | ||
| 104 | #if QSUB8_C == 1 | ||
| 105 | 31278 | int t = i - j; | |
| 106 |
11/16✓ Branch 0 taken 2347 times.
✓ Branch 1 taken 4414 times.
✓ Branch 2 taken 932 times.
✓ Branch 3 taken 6579 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 557 times.
✓ Branch 10 taken 2681 times.
✓ Branch 11 taken 1788 times.
✓ Branch 12 taken 2157 times.
✓ Branch 13 taken 2312 times.
✓ Branch 14 taken 6000 times.
✓ Branch 15 taken 1511 times.
|
31278 | if (t < 0) |
| 107 | 14117 | t = 0; | |
| 108 | 31278 | return t; | |
| 109 | #elif QSUB8_AVRASM == 1 | ||
| 110 | |||
| 111 | asm volatile( | ||
| 112 | /* First, subtract j from i, conditioning the C flag */ | ||
| 113 | "sub %0, %1 \n\t" | ||
| 114 | |||
| 115 | /* Now test the C flag. | ||
| 116 | If C is clear, we branch around a load of 0x00 into i. | ||
| 117 | If C is set, we go ahead and load 0x00 into i. | ||
| 118 | */ | ||
| 119 | "brcc L_%= \n\t" | ||
| 120 | "ldi %0, 0x00 \n\t" | ||
| 121 | "L_%=: " | ||
| 122 | : "+d"(i) // r16-r31, restricted by ldi | ||
| 123 | : "r"(j)); | ||
| 124 | return i; | ||
| 125 | #else | ||
| 126 | #error "No implementation for qsub8 available." | ||
| 127 | #endif | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Add one byte to another, with 8-bit result | ||
| 131 | /// @note This does not saturate and may overflow! | ||
| 132 | /// @param i first byte to add | ||
| 133 | /// @param j second byte to add | ||
| 134 | /// @returns the sum of i + j, 8-bit | ||
| 135 | LIB8STATIC_ALWAYS_INLINE uint8_t add8(uint8_t i, uint8_t j) { | ||
| 136 | #if ADD8_C == 1 | ||
| 137 | 19 | int t = i + j; | |
| 138 | 19 | return t; | |
| 139 | #elif ADD8_AVRASM == 1 | ||
| 140 | // Add j to i, period. | ||
| 141 | asm volatile("add %0, %1" : "+r"(i) : "r"(j)); | ||
| 142 | return i; | ||
| 143 | #else | ||
| 144 | #error "No implementation for add8 available." | ||
| 145 | #endif | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Add one byte to two bytes, with 16-bit result | ||
| 149 | /// @note This does not saturate and may overflow! | ||
| 150 | /// @param i first value to add, 8-bit | ||
| 151 | /// @param j second value to add, 16-bit | ||
| 152 | /// @returns the sum of i + j, 16-bit | ||
| 153 | LIB8STATIC_ALWAYS_INLINE uint16_t add8to16(uint8_t i, uint16_t j) { | ||
| 154 | #if ADD8_C == 1 | ||
| 155 | uint16_t t = i + j; | ||
| 156 | return t; | ||
| 157 | #elif ADD8_AVRASM == 1 | ||
| 158 | // Add i(one byte) to j(two bytes) | ||
| 159 | asm volatile("add %A[j], %[i] \n\t" | ||
| 160 | "adc %B[j], __zero_reg__ \n\t" | ||
| 161 | : [j] "+r"(j) | ||
| 162 | : [i] "r"(i)); | ||
| 163 | return i; | ||
| 164 | #else | ||
| 165 | #error "No implementation for add8to16 available." | ||
| 166 | #endif | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Subtract one byte from another, 8-bit result | ||
| 170 | /// @note This does not saturate and may overflow! | ||
| 171 | /// @param i byte to subtract from | ||
| 172 | /// @param j byte to subtract | ||
| 173 | /// @returns i - j | ||
| 174 | LIB8STATIC_ALWAYS_INLINE uint8_t sub8(uint8_t i, uint8_t j) { | ||
| 175 | #if SUB8_C == 1 | ||
| 176 | int t = i - j; | ||
| 177 | return t; | ||
| 178 | #elif SUB8_AVRASM == 1 | ||
| 179 | // Subtract j from i, period. | ||
| 180 | asm volatile("sub %0, %1" : "+r"(i) : "r"(j)); | ||
| 181 | return i; | ||
| 182 | #else | ||
| 183 | #error "No implementation for sub8 available." | ||
| 184 | #endif | ||
| 185 | } | ||
| 186 | |||
| 187 | /// Calculate an integer average of two unsigned | ||
| 188 | /// 8-bit integer values (uint8_t), rounded down. | ||
| 189 | /// Fractional results are rounded down, e.g. avg8(20,41) = 30 | ||
| 190 | /// @param i first value to average | ||
| 191 | /// @param j second value to average | ||
| 192 | /// @returns mean average of i and j, rounded down | ||
| 193 | LIB8STATIC_ALWAYS_INLINE uint8_t avg8(uint8_t i, uint8_t j) { | ||
| 194 | #if AVG8_C == 1 | ||
| 195 | return (i + j) >> 1; | ||
| 196 | #elif AVG8_AVRASM == 1 | ||
| 197 | asm volatile( | ||
| 198 | /* First, add j to i, 9th bit overflows into C flag */ | ||
| 199 | "add %0, %1 \n\t" | ||
| 200 | /* Divide by two, moving C flag into high 8th bit */ | ||
| 201 | "ror %0 \n\t" | ||
| 202 | : "+r"(i) | ||
| 203 | : "r"(j)); | ||
| 204 | return i; | ||
| 205 | #else | ||
| 206 | #error "No implementation for avg8 available." | ||
| 207 | #endif | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Calculate an integer average of two unsigned | ||
| 211 | /// 16-bit integer values (uint16_t), rounded down. | ||
| 212 | /// Fractional results are rounded down, e.g. avg16(20,41) = 30 | ||
| 213 | /// @param i first value to average | ||
| 214 | /// @param j second value to average | ||
| 215 | /// @returns mean average of i and j, rounded down | ||
| 216 | LIB8STATIC_ALWAYS_INLINE uint16_t avg16(uint16_t i, uint16_t j) { | ||
| 217 | #if AVG16_C == 1 | ||
| 218 | return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1; | ||
| 219 | #elif AVG16_AVRASM == 1 | ||
| 220 | asm volatile( | ||
| 221 | /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */ | ||
| 222 | "add %A[i], %A[j] \n\t" | ||
| 223 | /* Now, add C + jHi to iHi, 17th bit overflows into C flag */ | ||
| 224 | "adc %B[i], %B[j] \n\t" | ||
| 225 | /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now | ||
| 226 | in | ||
| 227 | C */ | ||
| 228 | "ror %B[i] \n\t" | ||
| 229 | /* Divide iLo by two, moving C flag into high 8th bit */ | ||
| 230 | "ror %A[i] \n\t" | ||
| 231 | : [i] "+r"(i) | ||
| 232 | : [j] "r"(j)); | ||
| 233 | return i; | ||
| 234 | #else | ||
| 235 | #error "No implementation for avg16 available." | ||
| 236 | #endif | ||
| 237 | } | ||
| 238 | |||
| 239 | /// Calculate an integer average of two unsigned | ||
| 240 | /// 8-bit integer values (uint8_t), rounded up. | ||
| 241 | /// Fractional results are rounded up, e.g. avg8r(20,41) = 31 | ||
| 242 | /// @param i first value to average | ||
| 243 | /// @param j second value to average | ||
| 244 | /// @returns mean average of i and j, rounded up | ||
| 245 | LIB8STATIC_ALWAYS_INLINE uint8_t avg8r(uint8_t i, uint8_t j) { | ||
| 246 | #if AVG8R_C == 1 | ||
| 247 | return (i + j + 1) >> 1; | ||
| 248 | #elif AVG8R_AVRASM == 1 | ||
| 249 | asm volatile( | ||
| 250 | /* First, add j to i, 9th bit overflows into C flag */ | ||
| 251 | "add %0, %1 \n\t" | ||
| 252 | /* Divide by two, moving C flag into high 8th bit, old 1st bit now in C | ||
| 253 | */ | ||
| 254 | "ror %0 \n\t" | ||
| 255 | /* Add C flag */ | ||
| 256 | "adc %0, __zero_reg__\n\t" | ||
| 257 | : "+r"(i) | ||
| 258 | : "r"(j)); | ||
| 259 | return i; | ||
| 260 | #else | ||
| 261 | #error "No implementation for avg8r available." | ||
| 262 | #endif | ||
| 263 | } | ||
| 264 | |||
| 265 | /// Calculate an integer average of two unsigned | ||
| 266 | /// 16-bit integer values (uint16_t), rounded up. | ||
| 267 | /// Fractional results are rounded up, e.g. avg16r(20,41) = 31 | ||
| 268 | /// @param i first value to average | ||
| 269 | /// @param j second value to average | ||
| 270 | /// @returns mean average of i and j, rounded up | ||
| 271 | LIB8STATIC_ALWAYS_INLINE uint16_t avg16r(uint16_t i, uint16_t j) { | ||
| 272 | #if AVG16R_C == 1 | ||
| 273 | return (uint32_t)((uint32_t)(i) + (uint32_t)(j) + 1) >> 1; | ||
| 274 | #elif AVG16R_AVRASM == 1 | ||
| 275 | asm volatile( | ||
| 276 | /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */ | ||
| 277 | "add %A[i], %A[j] \n\t" | ||
| 278 | /* Now, add C + jHi to iHi, 17th bit overflows into C flag */ | ||
| 279 | "adc %B[i], %B[j] \n\t" | ||
| 280 | /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now | ||
| 281 | in | ||
| 282 | C */ | ||
| 283 | "ror %B[i] \n\t" | ||
| 284 | /* Divide iLo by two, moving C flag into high 8th bit, old 1st bit now | ||
| 285 | in | ||
| 286 | C */ | ||
| 287 | "ror %A[i] \n\t" | ||
| 288 | /* Add C flag */ | ||
| 289 | "adc %A[i], __zero_reg__\n\t" | ||
| 290 | "adc %B[i], __zero_reg__\n\t" | ||
| 291 | : [i] "+r"(i) | ||
| 292 | : [j] "r"(j)); | ||
| 293 | return i; | ||
| 294 | #else | ||
| 295 | #error "No implementation for avg16r available." | ||
| 296 | #endif | ||
| 297 | } | ||
| 298 | |||
| 299 | /// Calculate an integer average of two signed 7-bit | ||
| 300 | /// integers (int8_t). | ||
| 301 | /// If the first argument is even, result is rounded down. | ||
| 302 | /// If the first argument is odd, result is rounded up. | ||
| 303 | /// @param i first value to average | ||
| 304 | /// @param j second value to average | ||
| 305 | /// @returns mean average of i and j, rounded | ||
| 306 | LIB8STATIC_ALWAYS_INLINE int8_t avg7(int8_t i, int8_t j) { | ||
| 307 | #if AVG7_C == 1 | ||
| 308 | ✗ | return (i >> 1) + (j >> 1) + (i & 0x1); | |
| 309 | #elif AVG7_AVRASM == 1 | ||
| 310 | asm volatile("asr %1 \n\t" | ||
| 311 | "asr %0 \n\t" | ||
| 312 | "adc %0, %1 \n\t" | ||
| 313 | : "+r"(i) | ||
| 314 | : "r"(j)); | ||
| 315 | return i; | ||
| 316 | #else | ||
| 317 | #error "No implementation for avg7 available." | ||
| 318 | #endif | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Calculate an integer average of two signed 15-bit | ||
| 322 | /// integers (int16_t). | ||
| 323 | /// If the first argument is even, result is rounded down. | ||
| 324 | /// If the first argument is odd, result is rounded up. | ||
| 325 | /// @param i first value to average | ||
| 326 | /// @param j second value to average | ||
| 327 | /// @returns mean average of i and j, rounded | ||
| 328 | LIB8STATIC_ALWAYS_INLINE int16_t avg15(int16_t i, int16_t j) { | ||
| 329 | #if AVG15_C == 1 | ||
| 330 | ✗ | return (i >> 1) + (j >> 1) + (i & 0x1); | |
| 331 | #elif AVG15_AVRASM == 1 | ||
| 332 | asm volatile( | ||
| 333 | /* first divide j by 2, throwing away lowest bit */ | ||
| 334 | "asr %B[j] \n\t" | ||
| 335 | "ror %A[j] \n\t" | ||
| 336 | /* now divide i by 2, with lowest bit going into C */ | ||
| 337 | "asr %B[i] \n\t" | ||
| 338 | "ror %A[i] \n\t" | ||
| 339 | /* add j + C to i */ | ||
| 340 | "adc %A[i], %A[j] \n\t" | ||
| 341 | "adc %B[i], %B[j] \n\t" | ||
| 342 | : [i] "+r"(i) | ||
| 343 | : [j] "r"(j)); | ||
| 344 | return i; | ||
| 345 | #else | ||
| 346 | #error "No implementation for avg15 available." | ||
| 347 | #endif | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Calculate the remainder of one unsigned 8-bit | ||
| 351 | /// value divided by anoter, aka A % M. | ||
| 352 | /// Implemented by repeated subtraction, which is | ||
| 353 | /// very compact, and very fast if A is "probably" | ||
| 354 | /// less than M. If A is a large multiple of M, | ||
| 355 | /// the loop has to execute multiple times. However, | ||
| 356 | /// even in that case, the loop is only two | ||
| 357 | /// instructions long on AVR, i.e., quick. | ||
| 358 | /// @param a dividend byte | ||
| 359 | /// @param m divisor byte | ||
| 360 | /// @returns remainder of a / m (i.e. a % m) | ||
| 361 | LIB8STATIC_ALWAYS_INLINE uint8_t mod8(uint8_t a, uint8_t m) { | ||
| 362 | #if defined(__AVR__) | ||
| 363 | asm volatile("L_%=: sub %[a],%[m] \n\t" | ||
| 364 | " brcc L_%= \n\t" | ||
| 365 | " add %[a],%[m] \n\t" | ||
| 366 | : [a] "+r"(a) | ||
| 367 | : [m] "r"(m)); | ||
| 368 | #else | ||
| 369 | while (a >= m) | ||
| 370 | a -= m; | ||
| 371 | #endif | ||
| 372 | return a; | ||
| 373 | } | ||
| 374 | |||
| 375 | /// Add two numbers, and calculate the modulo | ||
| 376 | /// of the sum and a third number, M. | ||
| 377 | /// In other words, it returns (A+B) % M. | ||
| 378 | /// It is designed as a compact mechanism for | ||
| 379 | /// incrementing a "mode" switch and wrapping | ||
| 380 | /// around back to "mode 0" when the switch | ||
| 381 | /// goes past the end of the available range. | ||
| 382 | /// e.g. if you have seven modes, this switches | ||
| 383 | /// to the next one and wraps around if needed: | ||
| 384 | /// @code{.cpp} | ||
| 385 | /// mode = addmod8( mode, 1, 7); | ||
| 386 | /// @endcode | ||
| 387 | /// @param a dividend byte | ||
| 388 | /// @param b value to add to the dividend | ||
| 389 | /// @param m divisor byte | ||
| 390 | /// @returns remainder of (a + b) / m | ||
| 391 | /// @see mod8() for notes on performance. | ||
| 392 | LIB8STATIC uint8_t addmod8(uint8_t a, uint8_t b, uint8_t m) { | ||
| 393 | #if defined(__AVR__) | ||
| 394 | asm volatile(" add %[a],%[b] \n\t" | ||
| 395 | "L_%=: sub %[a],%[m] \n\t" | ||
| 396 | " brcc L_%= \n\t" | ||
| 397 | " add %[a],%[m] \n\t" | ||
| 398 | : [a] "+r"(a) | ||
| 399 | : [b] "r"(b), [m] "r"(m)); | ||
| 400 | #else | ||
| 401 | a += b; | ||
| 402 | while (a >= m) | ||
| 403 | a -= m; | ||
| 404 | #endif | ||
| 405 | return a; | ||
| 406 | } | ||
| 407 | |||
| 408 | /// Subtract two numbers, and calculate the modulo | ||
| 409 | /// of the difference and a third number, M. | ||
| 410 | /// In other words, it returns (A-B) % M. | ||
| 411 | /// It is designed as a compact mechanism for | ||
| 412 | /// decrementing a "mode" switch and wrapping | ||
| 413 | /// around back to "mode 0" when the switch | ||
| 414 | /// goes past the start of the available range. | ||
| 415 | /// e.g. if you have seven modes, this switches | ||
| 416 | /// to the previous one and wraps around if needed: | ||
| 417 | /// @code{.cpp} | ||
| 418 | /// mode = submod8( mode, 1, 7); | ||
| 419 | /// @endcode | ||
| 420 | /// @param a dividend byte | ||
| 421 | /// @param b value to subtract from the dividend | ||
| 422 | /// @param m divisor byte | ||
| 423 | /// @returns remainder of (a - b) / m | ||
| 424 | /// @see mod8() for notes on performance. | ||
| 425 | LIB8STATIC uint8_t submod8(uint8_t a, uint8_t b, uint8_t m) { | ||
| 426 | #if defined(__AVR__) | ||
| 427 | asm volatile(" sub %[a],%[b] \n\t" | ||
| 428 | "L_%=: sub %[a],%[m] \n\t" | ||
| 429 | " brcc L_%= \n\t" | ||
| 430 | " add %[a],%[m] \n\t" | ||
| 431 | : [a] "+r"(a) | ||
| 432 | : [b] "r"(b), [m] "r"(m)); | ||
| 433 | #else | ||
| 434 | a -= b; | ||
| 435 | while (a >= m) | ||
| 436 | a -= m; | ||
| 437 | #endif | ||
| 438 | return a; | ||
| 439 | } | ||
| 440 | |||
| 441 | /// 8x8 bit multiplication, with 8-bit result. | ||
| 442 | /// @param i first byte to multiply | ||
| 443 | /// @param j second byte to multiply | ||
| 444 | /// @returns the product of i * j | ||
| 445 | /// @note This does not saturate and may overflow! | ||
| 446 | LIB8STATIC_ALWAYS_INLINE uint8_t mul8(uint8_t i, uint8_t j) { | ||
| 447 | #if MUL8_C == 1 | ||
| 448 | return ((int)i * (int)(j)) & 0xFF; | ||
| 449 | #elif MUL8_AVRASM == 1 | ||
| 450 | asm volatile( | ||
| 451 | /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ | ||
| 452 | "mul %0, %1 \n\t" | ||
| 453 | /* Extract the LOW 8-bits (r0) */ | ||
| 454 | "mov %0, r0 \n\t" | ||
| 455 | /* Restore r1 to "0"; it's expected to always be that */ | ||
| 456 | "clr __zero_reg__ \n\t" | ||
| 457 | : "+r"(i) | ||
| 458 | : "r"(j) | ||
| 459 | : "r0", "r1"); | ||
| 460 | return i; | ||
| 461 | #else | ||
| 462 | #error "No implementation for mul8 available." | ||
| 463 | #endif | ||
| 464 | } | ||
| 465 | |||
| 466 | /// 8x8 bit multiplication with 8-bit result, saturating at 0xFF. | ||
| 467 | /// @param i first byte to multiply | ||
| 468 | /// @param j second byte to multiply | ||
| 469 | /// @returns the product of i * j, capping at 0xFF | ||
| 470 | LIB8STATIC_ALWAYS_INLINE uint8_t qmul8(uint8_t i, uint8_t j) { | ||
| 471 | #if QMUL8_C == 1 | ||
| 472 | unsigned p = (unsigned)i * (unsigned)j; | ||
| 473 | if (p > 255) | ||
| 474 | p = 255; | ||
| 475 | return p; | ||
| 476 | #elif QMUL8_AVRASM == 1 | ||
| 477 | asm volatile( | ||
| 478 | /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */ | ||
| 479 | " mul %0, %1 \n\t" | ||
| 480 | /* Extract the LOW 8-bits (r0) */ | ||
| 481 | " mov %0, r0 \n\t" | ||
| 482 | /* If high byte of result is zero, all is well. */ | ||
| 483 | " tst r1 \n\t" | ||
| 484 | " breq Lnospill_%= \n\t" | ||
| 485 | /* If high byte of result > 0, saturate to 0xFF */ | ||
| 486 | " ldi %0, 0xFF \n\t" | ||
| 487 | "Lnospill_%=: \n\t" | ||
| 488 | /* Restore r1 to "0"; it's expected to always be that */ | ||
| 489 | " clr __zero_reg__ \n\t" | ||
| 490 | : "+d"(i) // r16-r31, restricted by ldi | ||
| 491 | : "r"(j) | ||
| 492 | : "r0", "r1"); | ||
| 493 | return i; | ||
| 494 | #else | ||
| 495 | #error "No implementation for qmul8 available." | ||
| 496 | #endif | ||
| 497 | } | ||
| 498 | |||
| 499 | /// Take the absolute value of a signed 8-bit uint8_t. | ||
| 500 | LIB8STATIC_ALWAYS_INLINE int8_t abs8(int8_t i) { | ||
| 501 | #if ABS8_C == 1 | ||
| 502 |
13/14✗ Branch 0 not taken.
✓ Branch 1 taken 6761 times.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 6698 times.
✓ Branch 4 taken 3426 times.
✓ Branch 5 taken 3335 times.
✓ Branch 6 taken 3533 times.
✓ Branch 7 taken 3228 times.
✓ Branch 8 taken 64 times.
✓ Branch 9 taken 104 times.
✓ Branch 10 taken 107 times.
✓ Branch 11 taken 51 times.
✓ Branch 12 taken 74 times.
✓ Branch 13 taken 84 times.
|
27528 | if (i < 0) |
| 503 | 7267 | i = -i; | |
| 504 | 27528 | return i; | |
| 505 | #elif ABS8_AVRASM == 1 | ||
| 506 | asm volatile( | ||
| 507 | /* First, check the high bit, and prepare to skip if it's clear */ | ||
| 508 | "sbrc %0, 7 \n" | ||
| 509 | |||
| 510 | /* Negate the value */ | ||
| 511 | "neg %0 \n" | ||
| 512 | |||
| 513 | : "+r"(i) | ||
| 514 | : "r"(i)); | ||
| 515 | return i; | ||
| 516 | #else | ||
| 517 | #error "No implementation for abs8 available." | ||
| 518 | #endif | ||
| 519 | } | ||
| 520 | |||
| 521 | /// Square root for 16-bit integers. | ||
| 522 | /// About three times faster and five times smaller | ||
| 523 | /// than Arduino's general `sqrt` on AVR. | ||
| 524 | ✗ | LIB8STATIC uint8_t sqrt16(uint16_t x) { | |
| 525 | ✗ | if (x <= 1) { | |
| 526 | ✗ | return x; | |
| 527 | } | ||
| 528 | |||
| 529 | ✗ | uint8_t low = 1; // lower bound | |
| 530 | uint8_t hi, mid; | ||
| 531 | |||
| 532 | ✗ | if (x > 7904) { | |
| 533 | ✗ | hi = 255; | |
| 534 | } else { | ||
| 535 | ✗ | hi = (x >> 5) + 8; // initial estimate for upper bound | |
| 536 | } | ||
| 537 | |||
| 538 | do { | ||
| 539 | ✗ | mid = (low + hi) >> 1; | |
| 540 | ✗ | if ((uint16_t)(mid * mid) > x) { | |
| 541 | ✗ | hi = mid - 1; | |
| 542 | } else { | ||
| 543 | ✗ | if (mid == 255) { | |
| 544 | ✗ | return 255; | |
| 545 | } | ||
| 546 | ✗ | low = mid + 1; | |
| 547 | } | ||
| 548 | ✗ | } while (hi >= low); | |
| 549 | |||
| 550 | ✗ | return low - 1; | |
| 551 | } | ||
| 552 | |||
| 553 | LIB8STATIC_ALWAYS_INLINE uint8_t sqrt8(uint8_t x) { | ||
| 554 | return sqrt16(map8_to_16(x)); | ||
| 555 | } | ||
| 556 | |||
| 557 | /// Blend a variable proportion (0-255) of one byte to another. | ||
| 558 | /// @param a the starting byte value | ||
| 559 | /// @param b the byte value to blend toward | ||
| 560 | /// @param amountOfB the proportion (0-255) of b to blend | ||
| 561 | /// @returns a byte value between a and b, inclusive | ||
| 562 | #if (FASTLED_BLEND_FIXED == 1) | ||
| 563 | LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB) { | ||
| 564 | |||
| 565 | // The BLEND_FIXED formula is | ||
| 566 | // | ||
| 567 | // result = ( A*(amountOfA) + B*(amountOfB) )/ 256 | ||
| 568 | // | ||
| 569 | // …where amountOfA = 255-amountOfB. | ||
| 570 | // | ||
| 571 | // This formula will never return 255, which is why the BLEND_FIXED + | ||
| 572 | // SCALE8_FIXED version is | ||
| 573 | // | ||
| 574 | // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 | ||
| 575 | // | ||
| 576 | // We can rearrange this formula for some great optimisations. | ||
| 577 | // | ||
| 578 | // result = ( A*(amountOfA) + A + B*(amountOfB) + B ) / 256 | ||
| 579 | // = ( A*(255-amountOfB) + A + B*(amountOfB) + B ) / 256 | ||
| 580 | // = ( A*(256-amountOfB) + B*(amountOfB) + B ) / 256 | ||
| 581 | // = ( A*256 + B + B*(amountOfB) - A*(amountOfB) ) / 256 // this | ||
| 582 | // is the version used in SCALE8_FIXED AVR below = ( A*256 + B + | ||
| 583 | // (B-A)*(amountOfB) ) / 256 // this is the version | ||
| 584 | // used in SCALE8_FIXED C below | ||
| 585 | |||
| 586 | uint16_t partial; | ||
| 587 | uint8_t result; | ||
| 588 | |||
| 589 | #if BLEND8_C == 1 | ||
| 590 | |||
| 591 | #if (FASTLED_SCALE8_FIXED == 1) | ||
| 592 | partial = (a << 8) | b; // A*256 + B | ||
| 593 | |||
| 594 | // on many platforms this compiles to a single multiply of (B-A) * amountOfB | ||
| 595 | partial += (b * amountOfB); | ||
| 596 | partial -= (a * amountOfB); | ||
| 597 | |||
| 598 | #else | ||
| 599 | uint8_t amountOfA = 255 - amountOfB; | ||
| 600 | |||
| 601 | // on the other hand, this compiles to two multiplies, and gives the "wrong" | ||
| 602 | // answer :] | ||
| 603 | partial = (a * amountOfA); | ||
| 604 | partial += (b * amountOfB); | ||
| 605 | #endif | ||
| 606 | |||
| 607 | result = partial >> 8; | ||
| 608 | |||
| 609 | return result; | ||
| 610 | |||
| 611 | #elif BLEND8_AVRASM == 1 | ||
| 612 | |||
| 613 | #if (FASTLED_SCALE8_FIXED == 1) | ||
| 614 | |||
| 615 | // 1 or 2 cycles depending on how the compiler optimises | ||
| 616 | partial = (a << 8) | b; | ||
| 617 | |||
| 618 | // 7 cycles | ||
| 619 | asm volatile(" mul %[a], %[amountOfB] \n\t" | ||
| 620 | " sub %A[partial], r0 \n\t" | ||
| 621 | " sbc %B[partial], r1 \n\t" | ||
| 622 | " mul %[b], %[amountOfB] \n\t" | ||
| 623 | " add %A[partial], r0 \n\t" | ||
| 624 | " adc %B[partial], r1 \n\t" | ||
| 625 | " clr __zero_reg__ \n\t" | ||
| 626 | : [partial] "+r"(partial) | ||
| 627 | : [amountOfB] "r"(amountOfB), [a] "r"(a), [b] "r"(b) | ||
| 628 | : "r0", "r1"); | ||
| 629 | |||
| 630 | #else | ||
| 631 | |||
| 632 | // non-SCALE8-fixed version | ||
| 633 | |||
| 634 | // 7 cycles | ||
| 635 | asm volatile( | ||
| 636 | /* partial = b * amountOfB */ | ||
| 637 | " mul %[b], %[amountOfB] \n\t" | ||
| 638 | " movw %A[partial], r0 \n\t" | ||
| 639 | |||
| 640 | /* amountOfB (aka amountOfA) = 255 - amountOfB */ | ||
| 641 | " com %[amountOfB] \n\t" | ||
| 642 | |||
| 643 | /* partial += a * amountOfB (aka amountOfA) */ | ||
| 644 | " mul %[a], %[amountOfB] \n\t" | ||
| 645 | |||
| 646 | " add %A[partial], r0 \n\t" | ||
| 647 | " adc %B[partial], r1 \n\t" | ||
| 648 | |||
| 649 | " clr __zero_reg__ \n\t" | ||
| 650 | |||
| 651 | : [partial] "=r"(partial), [amountOfB] "+r"(amountOfB) | ||
| 652 | : [a] "r"(a), [b] "r"(b) | ||
| 653 | : "r0", "r1"); | ||
| 654 | |||
| 655 | #endif | ||
| 656 | |||
| 657 | result = partial >> 8; | ||
| 658 | |||
| 659 | return result; | ||
| 660 | |||
| 661 | #else | ||
| 662 | #error "No implementation for blend8 available." | ||
| 663 | #endif | ||
| 664 | } | ||
| 665 | |||
| 666 | #else | ||
| 667 | ✗ | LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB) { | |
| 668 | // This version loses precision in the integer math | ||
| 669 | // and can actually return results outside of the range | ||
| 670 | // from a to b. Its use is not recommended. | ||
| 671 | uint8_t result; | ||
| 672 | ✗ | uint8_t amountOfA = 255 - amountOfB; | |
| 673 | ✗ | result = scale8_LEAVING_R1_DIRTY(a, amountOfA) + | |
| 674 | ✗ | scale8_LEAVING_R1_DIRTY(b, amountOfB); | |
| 675 | cleanup_R1(); | ||
| 676 | ✗ | return result; | |
| 677 | } | ||
| 678 | #endif | ||
| 679 | |||
| 680 | /// @} Math | ||
| 681 | /// @} lib8tion | ||
| 682 | |||
| 683 | FASTLED_NAMESPACE_END | ||
| 684 |