// IWYU pragma: private

#ifndef __INC_CLOCKLESS_ATTINY_H
#define __INC_CLOCKLESS_ATTINY_H

#include "controller.h"
#include "lib8tion.h"
// IWYU pragma: begin_keep
#include <avr/interrupt.h> // for cli/se definitions
// IWYU pragma: end_keep
#include "fl/stl/compiler_control.h"
#include "fl/stl/static_assert.h"
#include "fl/chipsets/led_timing.h"
#include "fl/chipsets/timing_traits.h"
#include "fastled_delay.h"
#include "platforms/avr/is_avr.h"
#include "fl/stl/noexcept.h"

FL_DISABLE_WARNING_PUSH
FL_DISABLE_WARNING_DEPRECATED_REGISTER

namespace fl {

#if defined(FASTLED_AVR)

// Scaling macro choice
#ifndef FASTLED_AVR_SCALE
#define FASTLED_AVR_SCALE 1
// whether or not to use dithering
#define DITHER 1
#endif

#if (F_CPU==8000000)
#define FASTLED_SLOW_CLOCK_ADJUST // asm __volatile__ ("mov r0,r0\n\t");
#else
#define FASTLED_SLOW_CLOCK_ADJUST
#endif

#define US_PER_TICK (64 / (F_CPU/1000000))

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Due to the tight timing specifications of WS2811 and friends on AVR, interrupts are disabled by default to keep timings exact.
// However, many WS2811 or WS2812 strips are surprisingly tolerant of jittery timings (such as those caused by interrupts), on the
// condition that the shortest pulse in the specification (representing a 0 bit) is kept under a certain length. After exceeding
// that length it would be interpreted as a 1, causing a glitch. 
// 
// If you set FASTLED_ALLOW_INTERRUPTS to 1, interrupts will only be disabled for a few cycles at a time, when necessary to keep
// this signal pulse short.
// 
// Beware: even with FASTLED_ALLOW_INTERRUPTS enabled, you must ensure that your interrupt handlers are *very* fast. If they take
// longer than 5µs, which is 80 clock cycles on a 16MHz AVR, the strip might latch partway through rendering, and you will see big
// glitches.
// 
// Remember to account for the interrupt overhead when writing your ISR. This accounts for at least 10 cycles, often 20+.
//
// If you are using multiple timers with interrupts, you can set them out of phase so they only fire one at a time. 
//
// TODO: it would be possible to support longer interrupts providing that they only fire during the "on" pulse - holding the
//       signal high indefinitely will never latch, although it would affect the framerate. Maybe the subject of a future PR…
//
// See https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ for more information on the
// tolerances.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//#define FASTLED_ALLOW_INTERRUPTS 1


// Variations on the functions in delay.h - w/a loop var passed in to preserve registers across calls by the optimizer/compiler
template<int CYCLES> inline void _dc(FASTLED_REGISTER u8 & loopvar) FL_NOEXCEPT;

template<int _LOOP, int PAD> FASTLED_FORCE_INLINE void _dc_AVR(FASTLED_REGISTER u8 & loopvar) FL_NOEXCEPT {
	_dc<PAD>(loopvar);
	// The convolution in here is to ensure that the state of the carry flag coming into the delay loop is preserved
	// Use a const variable to allow "any register" constraint
	const u8 loop_count = _LOOP;
	asm __volatile__ (  "BRCS L_PC%=\n\t"
						"        MOV %[loopvar], %[loop_count]\n\tL_%=: DEC %[loopvar]\n\t BRNE L_%=\n\tBREQ L_DONE%=\n\t"
						"L_PC%=: MOV %[loopvar], %[loop_count]\n\tLL_%=: DEC %[loopvar]\n\t BRNE LL_%=\n\tBSET 0\n\t"
						"L_DONE%=:\n\t"
						:
							[loopvar] "+r" (loopvar) : [loop_count] "r" (loop_count) : );
}

template<int CYCLES> FASTLED_FORCE_INLINE void _dc(FASTLED_REGISTER u8 & loopvar) FL_NOEXCEPT {
	_dc_AVR<CYCLES/6,CYCLES%6>(loopvar);
}
template<> FASTLED_FORCE_INLINE void _dc<-6>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc<-5>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc<-4>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc<-3>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc<-2>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc<-1>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc< 0>(FASTLED_REGISTER u8 & ) {}
template<> FASTLED_FORCE_INLINE void _dc< 1>(FASTLED_REGISTER u8 & ) {asm __volatile__("mov r0,r0":::);}
#if defined(__LGT8F__) 
template<> FASTLED_FORCE_INLINE void _dc< 2>(FASTLED_REGISTER u8 & loopvar) { _dc<1>(loopvar); _dc<1>(loopvar); }
#else
template<> FASTLED_FORCE_INLINE void _dc< 2>(FASTLED_REGISTER u8 & ) {asm __volatile__("rjmp .+0":::);}
#endif
template<> FASTLED_FORCE_INLINE void _dc< 3>(FASTLED_REGISTER u8 & loopvar) { _dc<2>(loopvar); _dc<1>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc< 4>(FASTLED_REGISTER u8 & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc< 5>(FASTLED_REGISTER u8 & loopvar) { _dc<2>(loopvar); _dc<3>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc< 6>(FASTLED_REGISTER u8 & loopvar) { _dc<2>(loopvar); _dc<2>(loopvar); _dc<2>(loopvar);}
template<> FASTLED_FORCE_INLINE void _dc< 7>(FASTLED_REGISTER u8 & loopvar) { _dc<4>(loopvar); _dc<3>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc< 8>(FASTLED_REGISTER u8 & loopvar) { _dc<4>(loopvar); _dc<4>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc< 9>(FASTLED_REGISTER u8 & loopvar) { _dc<5>(loopvar); _dc<4>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<10>(FASTLED_REGISTER u8 & loopvar) { _dc<6>(loopvar); _dc<4>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<11>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<1>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<12>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<2>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<13>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<3>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<14>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<4>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<15>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<5>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<16>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<6>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<17>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<7>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<18>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<8>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<19>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<9>(loopvar); }
template<> FASTLED_FORCE_INLINE void _dc<20>(FASTLED_REGISTER u8 & loopvar) { _dc<10>(loopvar); _dc<10>(loopvar); }

#if (FASTLED_ALLOW_INTERRUPTS == 1)
// If interrupts are enabled, HI1 actually takes 2 clocks due to cli().
// To keep the timings exact, D3 (which precedes it) must be 1 clock less.
// The same adjustment must be made for D2, due the corresponding sei().
#define D_INT_ADJ 1
#else
#define D_INT_ADJ 0
#endif

#define DINTPIN(T,ADJ,PINADJ) (T-(PINADJ+ADJ)>0) ? _dc<T-(PINADJ+ADJ)>(loopvar) : _dc<0>(loopvar);
#define DINT(T,ADJ) if(AVR_PIN_CYCLES(DATA_PIN)==1) { DINTPIN(T,ADJ,1) } else { DINTPIN(T,ADJ,2); }
#define _D1(ADJ) DINT(T1,ADJ)
#define _D2(ADJ) DINT(T2,ADJ+D_INT_ADJ)
#define _D3(ADJ) DINT(T3,ADJ+D_INT_ADJ)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Base template for clockless controllers.  These controllers have 3 control points in their cycle for each bit.  The first point
// is where the line is raised hi.  The second point is where the line is dropped low for a zero.  The third point is where the
// line is dropped low for a one.  T1, T2, and T3 correspond to the timings for those three in clock cycles.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if ((FASTLED_ALLOW_INTERRUPTS == 0) && defined(NO_CORRECTION) && (NO_CORRECTION == 1) && !(defined(NO_CLOCK_CORRECTION)))
// we hit this if you were trying to turn off clock correction without also trying to enable interrupts.
#	pragma message "In older versions of FastLED defining NO_CORRECTION 1 would mistakenly turn off color correction as well as clock correction."
#	pragma message "define NO_CLOCK_CORRECTION 1 to fix this warning."
#	define NO_CLOCK_CORRECTION 1
#endif

#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
static u8 gTimeErrorAccum256ths;
#endif

#define FASTLED_HAS_CLOCKLESS 1

template <u8 DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 10>
class ClocklessController : public CPixelLEDController<RGB_ORDER> {
	FL_STATIC_ASSERT(T1 >= 2 && T2 >= 2 && T3 >= 3, "Not enough cycles - use a higher clock speed");

	typedef typename FastPin<DATA_PIN>::port_ptr_t data_ptr_t;
	typedef typename FastPin<DATA_PIN>::port_t data_t;

	CMinWait<WAIT_TIME> mWait;

public:
	virtual void init() FL_NOEXCEPT {
		FastPin<DATA_PIN>::setOutput();
	}

	virtual u16 getMaxRefreshRate() const { return 400; }

protected:
	virtual void showPixels(PixelController<RGB_ORDER> & pixels) FL_NOEXCEPT {

		mWait.wait();
#if (!defined(FASTLED_ALLOW_INTERRUPTS) || FASTLED_ALLOW_INTERRUPTS == 0)
		cli();
#endif

		if(pixels.mLen > 0) {
			showRGBInternal(pixels);
		}

		// Adjust the timer
#if (!defined(NO_CLOCK_CORRECTION) || (NO_CLOCK_CORRECTION == 0)) && (FASTLED_ALLOW_INTERRUPTS == 0)
		u32 microsTaken = pixels.size();
		microsTaken *= CLKS_TO_MICROS(24 * (T1 + T2 + T3));

        // adust for approximate observed actal runtime (as of January 2015)
        // roughly 9.6 cycles per pixel, which is 0.6us/pixel at 16MHz
        // microsTaken += nLeds * 0.6 * CLKS_TO_MICROS(16);
        microsTaken += scale16by8(pixels.size(),(0.6 * 256) + 1) * CLKS_TO_MICROS(16);

        // if less than 1000us, there is NO timer impact,
        // this is because the ONE interrupt that might come in while interrupts
        // are disabled is queued up, and it will be serviced as soon as
        // interrupts are re-enabled.
        // This actually should technically also account for the runtime of the
        // interrupt handler itself, but we're just not going to worry about that.
        if( microsTaken > 1000) {

            // Since up to one timer tick will be queued, we don't need
            // to adjust the MS_COUNTER for that one.
            microsTaken -= 1000;

            // Now convert microseconds to 256ths of a second, approximately like this:
            // 250ths = (us/4)
            // 256ths = 250ths * (263/256);
            u16 x256ths = microsTaken >> 2;
            x256ths += scale16by8(x256ths,7);

            x256ths += gTimeErrorAccum256ths;
            MS_COUNTER += (x256ths >> 8);
            gTimeErrorAccum256ths = x256ths & 0xFF;
        }

#if 0
        // For pixel counts of 30 and under at 16Mhz, no correction is necessary.
        // For pixel counts of 15 and under at 8Mhz, no correction is necessary.
        //
        // This code, below, is smaller, and quicker clock correction, which drifts much
        // more significantly, but is a few bytes smaller.  Presented here for consideration
        // as an alternate on the ATtiny, which can't have more than about 150 pixels MAX
        // anyway, meaning that microsTaken will never be more than about 4,500, which fits in
        // a 16-bit variable.  The difference between /1000 and /1024 only starts showing
        // up in the range of about 100 pixels, so many ATtiny projects won't even
        // see a clock difference due to the approximation there.
		u32 microsTaken = nLeds;
		microsTaken *= CLKS_TO_MICROS(24 * (T1 + T2 + T3));
		//uint16_t microsTaken = (uint32_t)nLeds * (uint32_t)CLKS_TO_MICROS((24) * (T1 + T2 + T3));
        MS_COUNTER += static_cast<u16>(microsTaken >> 10);
#endif

#endif

#if (!defined(FASTLED_ALLOW_INTERRUPTS) || FASTLED_ALLOW_INTERRUPTS == 0)
		sei();
#endif
		mWait.mark();
	}
#define USE_ASM_MACROS


#if defined(FL_IS_AVR_MEGAAVR)
// Not used - place holder so existing ASM_VARS macro can remain the same
#define ASM_VAR_PORT "r" (*FastPin<DATA_PIN>::port())

#elif defined(FL_IS_AVR_ATTINY_MODERN)
// Probably unused - place holder so existing ASM_VARS macro can remain the same
#define ASM_VAR_PORT "r" (((PORT_t*)FastPin<DATA_PIN>::port())->OUT)

#else
// existing ASM_VARS macro for other AVR platforms
#define ASM_VAR_PORT "M" (FastPin<DATA_PIN>::port() - 0x20)

#endif // ASM_VAR_PORT


// The variables that our various asm statements use.  The same block of variables needs to be declared for
// all the asm blocks because GCC is pretty stupid and it would clobber variables happily or optimize code away too aggressively

// The existing code led to the generation of spurios move operations in the asm statements
// that threw of timing when optimizing for debugging with -Og.
// With the constraint "[b1] "=&a" (b1)", this is not any longer the case.
// The new code appears to work in other circumstances as well, but it will only 
// be activated when FASTLED_AVR_LEGACY_CODE == 0.
// If FASTLED_AVR_LEGACY_CODE is not defined, then it will be set to 1 only
// if the -Os (__OPTIMIZE_SIZE__) option has not been chosen.

#ifndef FASTLED_AVR_LEGACY_CODE
#ifdef __OPTIMIZE_SIZE__
#define FASTLED_AVR_LEGACY_CODE 1
#else
#define FASTLED_AVR_LEGACY_CODE 0
#endif
#endif
#if FASTLED_AVR_LEGACY_CODE
#define ASM_VARS : /* write variables */				\
				[count] "+x" (count),					\
				[data] "+z" (data),						\
				[b1] "+a" (b1),							\
				[d0] "+r" (d0),							\
				[d1] "+r" (d1),							\
				[d2] "+r" (d2),							\
				[loopvar] "+a" (loopvar),				\
				[scale_base] "+a" (scale_base)			\
				: /* use variables */					\
				[ADV] "r" (advanceBy),					\
				[b0] "a" (b0),							\
				[hi] "r" (hi),							\
				[lo] "r" (lo),							\
				[s0] "r" (s0),					  		\
				[s1] "r" (s1),							\
				[s2] "r" (s2),							\
				[e0] "r" (e0),							\
				[e1] "r" (e1),							\
				[e2] "r" (e2),							\
				[PORT] ASM_VAR_PORT,                    \
				[O0] "M" (RGB_BYTE0(RGB_ORDER)),		\
				[O1] "M" (RGB_BYTE1(RGB_ORDER)),		\
				[O2] "M" (RGB_BYTE2(RGB_ORDER))		\
				: "cc" /* clobber registers */
#else
#warning "Experimental code to support debugging! Define FASTLED_AVR_LEGACY_CODE as 1 to force using legacy code."
#define ASM_VARS : /* write variables */				\
				[count] "+x" (count),					\
				[data] "+z" (data),						\
				[b1] "=&a" (b1),							\
	                        [d0] "+r" (d0),							\
				[d1] "+r" (d1),							\
				[d2] "+r" (d2),							\
				[loopvar] "+a" (loopvar),				\
				[scale_base] "+a" (scale_base)			\
				: /* use variables */					\
				[ADV] "r" (advanceBy),					\
				[b0] "a" (b0),							\
				[hi] "r" (hi),							\
				[lo] "r" (lo),							\
				[s0] "r" (s0),					  		\
				[s1] "r" (s1),							\
				[s2] "r" (s2),							\
				[e0] "r" (e0),							\
				[e1] "r" (e1),							\
				[e2] "r" (e2),							\
				[PORT] ASM_VAR_PORT,                    \
				[O0] "M" (RGB_BYTE0(RGB_ORDER)),		\
				[O1] "M" (RGB_BYTE1(RGB_ORDER)),		\
				[O2] "M" (RGB_BYTE2(RGB_ORDER))		\
				: "cc" /* clobber registers */
#endif
	
#if (FASTLED_ALLOW_INTERRUPTS == 1)
#define HI1CLI cli()
#define QLO2SEI sei()
#else
#define HI1CLI
#define QLO2SEI
#endif
#if defined(FL_IS_AVR_MEGAAVR)

// 1 cycle, write hi to the port
#define HI1 HI1CLI; do {*FastPin<DATA_PIN>::port()=hi;} while(0);
// 1 cycle, write lo to the port
#define LO1 do {*FastPin<DATA_PIN>::port()=lo;} while(0);

#else

// Note: the code in the else in HI1/LO1 will be turned into an sts (2 cycle, 2 word)
// 1 cycle, write hi to the port
#define HI1 FASTLED_SLOW_CLOCK_ADJUST HI1CLI; if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[hi]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=hi; }
// 1 cycle, write lo to the port
#define LO1 if((int)(FastPin<DATA_PIN>::port())-0x20 < 64) { asm __volatile__("out %[PORT], %[lo]" ASM_VARS ); } else { *FastPin<DATA_PIN>::port()=lo; }

#endif

// 2 cycles, sbrs on flipping the line to lo if we're pushing out a 0
#define QLO2(B, N) asm __volatile__("sbrs %[" #B "], " #N ASM_VARS ); LO1; QLO2SEI;
// load a byte from ram into the given var with the given offset
#define LD2(B,O) asm __volatile__("ldd %[" #B "], Z + %[" #O "]\n\t" ASM_VARS );
// 4 cycles - load a byte from ram into the scaling scratch space with the given offset, clear the target var, clear carry
#define LDSCL4(B,O) asm __volatile__("ldd %[scale_base], Z + %[" #O "]\n\tclr %[" #B "]\n\tclc\n\t" ASM_VARS );

#if (DITHER==1)
// apply dithering value  before we do anything with scale_base
#define PRESCALE4(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\tbrcc L_%=\n\tser %[scale_base]\n\tL_%=:\n\t" ASM_VARS);

// Do the add for the prescale
#define PRESCALEA2(D) asm __volatile__("cpse %[scale_base], __zero_reg__\n\t add %[scale_base],%[" #D "]\n\t" ASM_VARS);

// Do the clamp for the prescale, clear carry when we're done - NOTE: Must ensure carry flag state is preserved!
#define PRESCALEB4(D) asm __volatile__("brcc L_%=\n\tser %[scale_base]\n\tL_%=:\n\tneg %[" #D "]\n\tCLC" ASM_VARS);

// Clamp for prescale, increment data, since we won't ever wrap 65k, this also effectively clears carry for us
#define PSBIDATA4(D) asm __volatile__("brcc L_%=\n\tser %[scale_base]\n\tL_%=:\n\tadd %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" ASM_VARS);

#else
#define PRESCALE4(D) _dc<4>(loopvar);
#define PRESCALEA2(D) _dc<2>(loopvar);
#define PRESCALEB4(D) _dc<4>(loopvar);
#define PSBIDATA4(D) asm __volatile__( "add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\trjmp .+0\n\t" ASM_VARS );
#endif

// 2 cycles - perform one step of the scaling (if a given bit is set in scale, add scale-base to the scratch space)
#define _SCALE02(B, N) "sbrc %[s0], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
#define _SCALE12(B, N) "sbrc %[s1], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
#define _SCALE22(B, N) "sbrc %[s2], " #N "\n\tadd %[" #B "], %[scale_base]\n\t"
#define SCALE02(B,N) asm __volatile__( _SCALE02(B,N) ASM_VARS );
#define SCALE12(B,N) asm __volatile__( _SCALE12(B,N) ASM_VARS );
#define SCALE22(B,N) asm __volatile__( _SCALE22(B,N) ASM_VARS );

// 1 cycle - rotate right, pulling in from carry
#define _ROR1(B) "ror %[" #B "]\n\t"
#define ROR1(B) asm __volatile__( _ROR1(B) ASM_VARS);

// 1 cycle, clear the carry bit
#define _CLC1 "clc\n\t"
#define CLC1 asm __volatile__( _CLC1 ASM_VARS );

// 2 cycles, rortate right, pulling in from carry then clear the carry bit
#define RORCLC2(B) asm __volatile__( _ROR1(B) _CLC1 ASM_VARS );

// 4 cycles, rotate, clear carry, scale next bit
#define RORSC04(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE02(B, N) ASM_VARS );
#define RORSC14(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE12(B, N) ASM_VARS );
#define RORSC24(B, N) asm __volatile__( _ROR1(B) _CLC1 _SCALE22(B, N) ASM_VARS );

// 4 cycles, scale bit, rotate, clear carry
#define SCROR04(B, N) asm __volatile__( _SCALE02(B,N) _ROR1(B) _CLC1 ASM_VARS );
#define SCROR14(B, N) asm __volatile__( _SCALE12(B,N) _ROR1(B) _CLC1 ASM_VARS );
#define SCROR24(B, N) asm __volatile__( _SCALE22(B,N) _ROR1(B) _CLC1 ASM_VARS );

/////////////////////////////////////////////////////////////////////////////////////
// Loop life cycle

// dither adjustment macro - should be kept in sync w/what's in stepDithering
// #define ADJDITHER2(D, E) D = E - D;
#define _NEGD1(D) "neg %[" #D "]\n\t"
#define _ADJD1(D,E) "add %[" #D "], %[" #E "]\n\t"
#define ADJDITHER2(D, E) asm __volatile__ ( _NEGD1(D) _ADJD1(D, E) ASM_VARS);
#define ADDDE1(D, E) asm __volatile__ ( _ADJD1(D, E) ASM_VARS );

// #define xstr(a) str(a)
// #define str(a) #a
// #define ADJDITHER2(D,E) asm __volatile__("subi %[" #D "], " xstr(DUSE) "\n\tand %[" #D "], %[" #E "]\n\t" ASM_VARS);

// define the beginning of the loop
#define LOOP asm __volatile__("1:" ASM_VARS );
// define the end of the loop
#define DONE asm __volatile__("2:" ASM_VARS );

// 2 cycles - increment the data pointer
#define IDATA2 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t"  ASM_VARS );
#define IDATACLC3 asm __volatile__("add %A[data], %A[ADV]\n\tadc %B[data], %B[ADV]\n\t" _CLC1  ASM_VARS );

// 1 cycle mov
#define _MOV1(B1, B2) "mov %[" #B1 "], %[" #B2 "]\n\t"

#define MOV1(B1, B2) asm __volatile__( _MOV1(B1,B2) ASM_VARS );

// 3 cycle mov - skip if scale fix is happening
#if (FASTLED_SCALE8_FIXED == 1)
#define _MOV_FIX03(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s0], __zero_reg__\n\t" _MOV1(B1, B2)
#define _MOV_FIX13(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s1], __zero_reg__\n\t" _MOV1(B1, B2)
#define _MOV_FIX23(B1, B2) "mov %[" #B1 "], %[scale_base]\n\tcpse %[s2], __zero_reg__\n\t" _MOV1(B1, B2)
#else
// if we haven't fixed scale8, just do the move and nop the 2 cycles that would be used to
// do the fixed adjustment
#define _MOV_FIX03(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
#define _MOV_FIX13(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
#define _MOV_FIX23(B1, B2) _MOV1(B1, B2) "rjmp .+0\n\t"
#endif

// 3 cycle mov + negate D for dither adjustment
#define MOV_NEGD04(B1, B2, D) asm __volatile( _MOV_FIX03(B1, B2) _NEGD1(D) ASM_VARS );
#define MOV_ADDDE04(B1, B2, D, E) asm __volatile( _MOV_FIX03(B1, B2) _ADJD1(D, E) ASM_VARS );
#define MOV_NEGD14(B1, B2, D) asm __volatile( _MOV_FIX13(B1, B2) _NEGD1(D) ASM_VARS );
#define MOV_ADDDE14(B1, B2, D, E) asm __volatile( _MOV_FIX13(B1, B2) _ADJD1(D, E) ASM_VARS );
#define MOV_NEGD24(B1, B2, D) asm __volatile( _MOV_FIX23(B1, B2) _NEGD1(D) ASM_VARS );

// 2 cycles - decrement the counter
#define DCOUNT2 asm __volatile__("sbiw %[count], 1" ASM_VARS );
// 2 cycles - jump to the beginning of the loop
#define JMPLOOP2 asm __volatile__("rjmp 1b" ASM_VARS );
// 2 cycles - jump out of the loop
#define BRLOOP1 asm __volatile__("brne 3\n\trjmp 2f\n\t3:" ASM_VARS );

// 5 cycles 2 sbiw, 3 for the breq/rjmp
#define ENDLOOP5 asm __volatile__("sbiw %[count], 1\n\tbreq L_%=\n\trjmp 1b\n\tL_%=:\n\t" ASM_VARS);

// NOP using the variables, forcing a move
#define DNOP asm __volatile__("mov r0,r0" ASM_VARS);

#define DADVANCE 3
#define DUSE (0xFF - (DADVANCE-1))

	// This method is made static to force making register Y available to use for data on AVR - if the method is non-static, then
	// gcc will use register Y for the this pointer.
	static void /*__attribute__((optimize("O0")))*/  /*__attribute__ ((always_inline))*/  showRGBInternal(PixelController<RGB_ORDER> & pixels)  {
		u8 *data = (u8*)pixels.mData;
		data_ptr_t port = FastPin<DATA_PIN>::port();
		data_t mask = FastPin<DATA_PIN>::mask();
		u8 scale_base = 0;

		// FASTLED_REGISTER uint8_t *end = data + nLeds;
		data_t hi = *port | mask;
		data_t lo = *port & ~mask;
		*port = lo;

		// the byte currently being written out
		u8 b0 = 0;
		// the byte currently being worked on to write the next out
		u8 b1 = 0;

		// Setup the pixel controller
		pixels.preStepFirstByteDithering();

		// pull the dithering/adjustment values out of the pixels object for direct asm access

		// even though advanceBy is only an int8, we cast it to int16 for sign extension in case it's negative.
		i16 advanceBy = pixels.advanceBy();
		u16 count = pixels.mLen;

		u8 s0 = pixels.mColorAdjustment.premixed.raw[RO(0)];
		u8 s1 = pixels.mColorAdjustment.premixed.raw[RO(1)];
		u8 s2 = pixels.mColorAdjustment.premixed.raw[RO(2)];
#if (FASTLED_SCALE8_FIXED==1)
		s0++; s1++; s2++;
#endif
		u8 d0 = pixels.d[RO(0)];
		u8 d1 = pixels.d[RO(1)];
		u8 d2 = pixels.d[RO(2)];
		u8 e0 = pixels.e[RO(0)];
		u8 e1 = pixels.e[RO(1)];
		u8 e2 = pixels.e[RO(2)];

		u8 loopvar=0;

		// This has to be done in asm to keep gcc from messing up the asm code further down
		b0 = data[RO(0)];
		{
			LDSCL4(b0,O0) 	PRESCALEA2(d0)
			PRESCALEB4(d0)	SCALE02(b0,0)
			RORSC04(b0,1) 	ROR1(b0) CLC1
			SCROR04(b0,2)		SCALE02(b0,3)
			RORSC04(b0,4) 	ROR1(b0) CLC1
			SCROR04(b0,5) 	SCALE02(b0,6)
			RORSC04(b0,7) 	ROR1(b0) CLC1
			MOV_ADDDE04(b1,b0,d0,e0)
			MOV1(b0,b1)
		}

		{
			// while(--count)
			{
				// Loop beginning
				DNOP;
				LOOP;

				// Sum of the clock counts across each row should be 10 for 8Mhz, WS2811
				// The values in the D1/D2/D3 indicate how many cycles the previous column takes
				// to allow things to line back up.
				//
				// While writing out byte 0, we're loading up byte 1, applying the dithering adjustment,
				// then scaling it using 8 cycles of shift/add interleaved in between writing the bits
				// out.  When doing byte 1, we're doing the above for byte 2.  When we're doing byte 2,
				// we're cycling back around and doing the above for byte 0.

				// Inline scaling - RGB ordering
				// DNOP
				HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O1) 	_D2(4)	LO1	PRESCALEA2(d1)	_D3(2)
				HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d1)	_D2(4)	LO1	SCALE12(b1,0)	_D3(2)
				HI1 _D1(1) QLO2(b0, 5) RORSC14(b1,1) 	_D2(4)	LO1 RORCLC2(b1)		_D3(2)
				HI1 _D1(1) QLO2(b0, 4) SCROR14(b1,2)	_D2(4)	LO1 SCALE12(b1,3)	_D3(2)
				HI1 _D1(1) QLO2(b0, 3) RORSC14(b1,4) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 2) SCROR14(b1,5) 	_D2(4)	LO1 SCALE12(b1,6)	_D3(2)
				HI1 _D1(1) QLO2(b0, 1) RORSC14(b1,7) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 0)
				switch(XTRA0) {
					case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
				}
				MOV_ADDDE14(b0,b1,d1,e1) _D2(4) LO1 _D3(0)

				HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O2) 	_D2(4)	LO1	PRESCALEA2(d2)	_D3(2)
				HI1 _D1(1) QLO2(b0, 6) PSBIDATA4(d2)	_D2(4)	LO1	SCALE22(b1,0)	_D3(2)
				HI1 _D1(1) QLO2(b0, 5) RORSC24(b1,1) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 4) SCROR24(b1,2)	_D2(4)	LO1 SCALE22(b1,3)	_D3(2)
				HI1 _D1(1) QLO2(b0, 3) RORSC24(b1,4) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 2) SCROR24(b1,5) 	_D2(4)	LO1 SCALE22(b1,6)	_D3(2)
				HI1 _D1(1) QLO2(b0, 1) RORSC24(b1,7) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 0)
				switch(XTRA0) {
					case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
				}

				// Because Prescale on the middle byte also increments the data counter,
				// we have to do both halves of updating d2 here - negating it (in the
				// MOV_NEGD24 macro) and then adding E back into it
				MOV_NEGD24(b0,b1,d2) _D2(4) LO1 ADDDE1(d2,e2) _D3(1)
				HI1 _D1(1) QLO2(b0, 7) LDSCL4(b1,O0) 	_D2(4)	LO1	PRESCALEA2(d0)	_D3(2)
				HI1 _D1(1) QLO2(b0, 6) PRESCALEB4(d0)	_D2(4)	LO1	SCALE02(b1,0)	_D3(2)
				HI1 _D1(1) QLO2(b0, 5) RORSC04(b1,1) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 4) SCROR04(b1,2)	_D2(4)	LO1 SCALE02(b1,3)	_D3(2)
				HI1 _D1(1) QLO2(b0, 3) RORSC04(b1,4) 	_D2(4)	LO1 RORCLC2(b1)  	_D3(2)
				HI1 _D1(1) QLO2(b0, 2) SCROR04(b1,5) 	_D2(4)	LO1 SCALE02(b1,6)	_D3(2)
				HI1 _D1(1) QLO2(b0, 1) RORSC04(b1,7) 	_D2(4)	LO1 RORCLC2(b1) 	_D3(2)
				HI1 _D1(1) QLO2(b0, 0)
				switch(XTRA0) {
					case 4: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 3: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 2: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)  /* fall through */
					case 1: _D2(0) LO1 _D3(0) HI1 _D1(1) QLO2(b0,0)
				}
				MOV_ADDDE04(b0,b1,d0,e0) _D2(4) LO1 _D3(5)
				ENDLOOP5
			}
			DONE;
		}

	}

};

#endif
}  // namespace fl

FL_DISABLE_WARNING_POP

#endif // __INC_CLOCKLESS_ATTINY_H
