text: /*
   * ATtinySerialOut.cpp
   *
   * For transmitting debug data over bit bang serial with 115200 baud for 1/8/16 MHz ATtiny clock.
   * For 1 MHz you can choose also 38400 baud (120 bytes smaller code size).
   * For 8/16 MHz you can choose also 230400 baud (just faster).
   * 1 Start, 8 Data, 1 Stop, No Parity
   *
   * Using PB2 // (Pin7 on Tiny85) as default TX pin to be compatible with digispark board
   * To change the output pin, modify the line "#define TX_PIN ..." in TinySerialOut.h or or set it as compiler symbol like "-DTX_PIN PB1".
   *
   * Using the Serial.print commands needs 4 bytes extra for each call.
   *
   *
   *  Copyright (C) 2015-2020  Armin Joachimsmeyer
   *  Email: armin.joachimsmeyer@gmail.com
   *
   *  This file is part of TinySerialOut https://github.com/ArminJo/ATtinySerialOut.
   *
   *  TinySerialOut is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program.  If not, see <http://www.gnu.org/licenses/gpl.html>.
   *
   */
  
  #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \
      || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \
      || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
  #include "ATtinySerialOut.h"
  #include <avr/eeprom.h>     // for eeprom_read_byte() in writeString_E()
  
  #ifndef _NOP
  #define _NOP()  __asm__ volatile ("nop")
  #endif
  
  #ifndef PORTB
  #define PORTB (*(volatile uint8_t *)((0x18) + 0x20))
  #endif
  
  #if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
  #  ifndef TX_PORT
  #define TX_PORT PORTA
  #define TX_PORT_ADDR 0x02 // PORTA
  #define TX_DDR DDRA
  
  //#define TX_PORT PORTB
  //#define TX_PORT_ADDR 0x05
  //#define TX_DDR DDRB
  #  endif
  
  #else
  //  ATtinyX5 here
  #define TX_PORT PORTB
  #define TX_PORT_ADDR 0x18 // PORTB
  #define TX_DDR DDRB
  #endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
  
  void write1Start8Data1StopNoParity(uint8_t aValue);
  
  bool sUseCliSeiForWrite = true;
  
  void initTXPin() {
      // TX_PIN is active LOW, so set it to HIGH initially
      TX_PORT |= (1 << TX_PIN);
      // set pin direction to output
      TX_DDR |= (1 << TX_PIN);
  }
  
  void write1Start8Data1StopNoParityWithCliSei(uint8_t aValue) {
      uint8_t oldSREG = SREG;
      cli();
      write1Start8Data1StopNoParity(aValue);
      SREG = oldSREG;
  }
  
  void writeValue(uint8_t aValue) {
      write1Start8Data1StopNoParity(aValue);
  }
  
  /*
   * Used for writeString() and therefore all write<type>() and print<type>
   */
  void useCliSeiForStrings(bool aUseCliSeiForWrite) {
      sUseCliSeiForWrite = aUseCliSeiForWrite;
  }
  
  /*
   * Write String residing in RAM
   */
  void writeString(const char *aStringPtr) {
  #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
      if (sUseCliSeiForWrite) {
  #endif
          while (*aStringPtr != 0) {
              write1Start8Data1StopNoParityWithCliSei(*aStringPtr++);
          }
  #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
      } else {
          while (*aStringPtr != 0) {
              write1Start8Data1StopNoParity(*aStringPtr++);
          }
      }
  #endif
  }
  
  /*
   * Write string residing in program space (FLASH)
   */
  void writeString_P(const char *aStringPtr) {
      uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr);
  // Comparing with 0xFF is safety net for wrong string pointer
      while (tChar != 0 && tChar != 0xFF) {
  #ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
          write1Start8Data1StopNoParityWithCliSei(tChar);
  #else
          if (sUseCliSeiForWrite) {
              write1Start8Data1StopNoParityWithCliSei(tChar);
          } else {
              write1Start8Data1StopNoParity(tChar);
          }
  #endif
          tChar = pgm_read_byte((const uint8_t * ) ++aStringPtr);
      }
  }
  
  /*
   * Write string residing in program space (FLASH)
   */
  void writeString(const __FlashStringHelper *aStringPtr) {
      PGM_P tPGMStringPtr = reinterpret_cast<PGM_P>(aStringPtr);
      uint8_t tChar = pgm_read_byte((const uint8_t * ) aStringPtr);
  // Comparing with 0xFF is safety net for wrong string pointer
      while (tChar != 0 && tChar != 0xFF) {
  #ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
          write1Start8Data1StopNoParityWithCliSei(tChar);
  #else
          if (sUseCliSeiForWrite) {
              write1Start8Data1StopNoParityWithCliSei(tChar);
          } else {
              write1Start8Data1StopNoParity(tChar);
          }
  #endif
          tChar = pgm_read_byte((const uint8_t * ) ++tPGMStringPtr);
      }
  }
  
  /*
   * Write string residing in EEPROM space
   */
  void writeString_E(const char *aStringPtr) {
      uint8_t tChar = eeprom_read_byte((const uint8_t *) aStringPtr);
      // Comparing with 0xFF is safety net for wrong string pointer
      while (tChar != 0 && tChar != 0xFF) {
  #ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
          write1Start8Data1StopNoParityWithCliSei(tChar);
  #else
          if (sUseCliSeiForWrite) {
              write1Start8Data1StopNoParityWithCliSei(tChar);
          } else {
              write1Start8Data1StopNoParity(tChar);
          }
  #endif
          tChar = eeprom_read_byte((const uint8_t *) ++aStringPtr);
      }
  }
  
  void writeStringWithoutCliSei(const char *aStringPtr) {
      while (*aStringPtr != 0) {
          write1Start8Data1StopNoParity(*aStringPtr++);
      }
  }
  
  void writeStringWithCliSei(const char *aStringPtr) {
      while (*aStringPtr != 0) {
          write1Start8Data1StopNoParityWithCliSei(*aStringPtr++);
      }
  }
  
  void writeStringSkipLeadingSpaces(const char *aStringPtr) {
      // skip leading spaces
      while (*aStringPtr == ' ' && *aStringPtr != 0) {
          aStringPtr++;
      }
  #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
      if (sUseCliSeiForWrite) {
  #endif
          while (*aStringPtr != 0) {
              write1Start8Data1StopNoParityWithCliSei(*aStringPtr++);
          }
  #ifndef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
      } else {
          while (*aStringPtr != 0) {
              write1Start8Data1StopNoParity(*aStringPtr++);
          }
      }
  #endif
  }
  
  void writeBinary(uint8_t aByte) {
  #ifdef USE_ALWAYS_CLI_SEI_GUARD_FOR_OUTPUT
      write1Start8Data1StopNoParityWithCliSei(aByte);
  #else
      if (sUseCliSeiForWrite) {
          write1Start8Data1StopNoParityWithCliSei(aByte);
      } else {
          write1Start8Data1StopNoParity(aByte);
      }
  #endif
  }
  
  void writeChar(uint8_t aChar) {
      writeBinary(aChar);
  }
  
  void writeCRLF() {
      writeBinary('\r');
      writeBinary('\n');
  }
  
  void writeUnsignedByte(uint8_t aByte) {
      char tStringBuffer[4];
      utoa(aByte, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  /*
   * 2 Byte Hex output
   */
  void writeUnsignedByteHex(uint8_t aByte) {
      char tStringBuffer[3];
      //    tStringBuffer[0] = nibbleToHex(aByte >> 4);
      //    tStringBuffer[1] = nibbleToHex(aByte);
      //    tStringBuffer[2] = '\0';
      // the utoa() version is 8 bytes smaller than the version with nibbleToHex(), if utoa() is allocated by another function.
      utoa(aByte, &tStringBuffer[0], 16);
      if (tStringBuffer[1] == '\0') {
          tStringBuffer[2] = '\0';
          tStringBuffer[1] = tStringBuffer[0];
          tStringBuffer[0] = '0';
      }
      writeString(tStringBuffer);
  }
  
  /*
   * 2 Byte Hex output with 2 Byte prefix "0x"
   */
  void writeUnsignedByteHexWithPrefix(uint8_t aByte) {
      writeBinary('0');
      writeBinary('x');
      writeUnsignedByteHex(aByte);
  }
  
  char nibbleToHex(uint8_t aByte) {
      aByte = aByte & 0x0F;
      if (aByte < 10) {
          return aByte + '0';
      }
      return aByte + 'A' - 10;
  }
  
  void writeByte(int8_t aByte) {
      char tStringBuffer[5];
      itoa(aByte, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeInt(int16_t aInteger) {
      char tStringBuffer[7];
      itoa(aInteger, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeUnsignedInt(uint16_t aInteger) {
      char tStringBuffer[6];
      utoa(aInteger, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeLong(int32_t aLong) {
      char tStringBuffer[12];
      ltoa(aLong, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeUnsignedLong(uint32_t aLong) {
      char tStringBuffer[11];
      ultoa(aLong, tStringBuffer, 10);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeFloat(double aFloat) {
      char tStringBuffer[11];
      dtostrf(aFloat, 10, 3, tStringBuffer);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void writeFloat(double aFloat, uint8_t aDigits) {
      char tStringBuffer[11];
      dtostrf(aFloat, 10, aDigits, tStringBuffer);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  /******************************************************
   * The TinySerialOut class fuctions which implements
   * the Serial + printHex() and printlnHex() functions
   ******************************************************/
  /*
   * An alternative way to call the init function :-)
   */
  void TinySerialOut::begin(long aBaudrate) {
      initTXPin();
  #if defined(USE_115200BAUD) // else smaller code, but only 38400 baud at 1 MHz
      if (aBaudrate != 115200) {
          println(F("Only 115200 supported!"));
      }
  #else
  #if (F_CPU == 1000000)
      if (aBaudrate != 38400) {
          println(F("Only 38400 supported!"));
      }
  #else
      if (aBaudrate != 230400) {
          println(F("Only 230400 supported!"));
      }
  #endif
  #endif
  }
  
  void TinySerialOut::end() {
      // no action needed
  }
  
  void TinySerialOut::flush() {
      // no action needed, since we do not use a buffer
  }
  
  /*
   * 2 Byte Hex output with 2 Byte prefix "0x"
   */
  void TinySerialOut::printHex(uint8_t aByte) {
      writeUnsignedByteHexWithPrefix(aByte);
  }
  
  void TinySerialOut::printHex(uint16_t aWord) {
      writeUnsignedByteHexWithPrefix(aWord >> 8);
      writeUnsignedByteHex(aWord);
  }
  
  void TinySerialOut::printlnHex(uint8_t aByte) {
      printHex(aByte);
      println();
  }
  
  void TinySerialOut::printlnHex(uint16_t aWord) {
      printHex(aWord);
      println();
  }
  
  // virtual functions of Print class
  size_t TinySerialOut::write(uint8_t aByte) {
      writeBinary(aByte);
      return 1;
  }
  
  void TinySerialOut::print(const char *aStringPtr) {
      writeString(aStringPtr);
  }
  
  void TinySerialOut::print(const __FlashStringHelper *aStringPtr) {
      writeString(aStringPtr);
  }
  
  void TinySerialOut::print(char aChar) {
      writeBinary(aChar);
  }
  
  void TinySerialOut::print(uint8_t aByte, uint8_t aBase) {
      if (aBase == 16) {
          /*
           * Print Hex always with two characters
           */
          writeUnsignedByteHex(aByte);
      } else {
          char tStringBuffer[4];
          utoa(aByte, tStringBuffer, aBase);
          writeStringSkipLeadingSpaces(tStringBuffer);
      }
  }
  
  void TinySerialOut::print(int16_t aInteger, uint8_t aBase) {
      char tStringBuffer[7];
      itoa(aInteger, tStringBuffer, aBase);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void TinySerialOut::print(uint16_t aInteger, uint8_t aBase) {
      char tStringBuffer[6];
      utoa(aInteger, tStringBuffer, aBase);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void TinySerialOut::print(int32_t aLong, uint8_t aBase) {
      char tStringBuffer[12];
      ltoa(aLong, tStringBuffer, aBase);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void TinySerialOut::print(uint32_t aLong, uint8_t aBase) {
      char tStringBuffer[11];
      ultoa(aLong, tStringBuffer, aBase);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void TinySerialOut::print(double aFloat, uint8_t aDigits) {
      char tStringBuffer[11];
      dtostrf(aFloat, 10, aDigits, tStringBuffer);
      writeStringSkipLeadingSpaces(tStringBuffer);
  }
  
  void TinySerialOut::println(char aChar) {
      print(aChar);
      println();
  }
  
  void TinySerialOut::println(const char *aStringPtr) {
      print(aStringPtr);
      println();
  }
  
  void TinySerialOut::println(const __FlashStringHelper *aStringPtr) {
      print(aStringPtr);
      println();
  }
  
  void TinySerialOut::println(uint8_t aByte, uint8_t aBase) {
      print(aByte, aBase);
      println();
  }
  
  void TinySerialOut::println(int16_t aInteger, uint8_t aBase) {
      print(aInteger, aBase);
      println();
  }
  
  void TinySerialOut::println(uint16_t aInteger, uint8_t aBase) {
      print(aInteger, aBase);
      println();
  }
  
  void TinySerialOut::println(int32_t aLong, uint8_t aBase) {
      print(aLong, aBase);
      println();
  }
  
  void TinySerialOut::println(uint32_t aLong, uint8_t aBase) {
      print(aLong, aBase);
      println();
  }
  
  void TinySerialOut::println(double aFloat, uint8_t aDigits) {
      print(aFloat, aDigits);
      println();
  }
  
  void TinySerialOut::println() {
      print('\r');
      print('\n');
  }
  
  /*
   * The Serial Instance!!!
   */
  // #if ... to be compatible with ATTinyCores and AttinyDigisparkCores
  #if (!defined(UBRRH) && !defined(UBRR0H)) /*AttinyDigisparkCore and AttinyDigisparkCore condition*/ \
      || USE_SOFTWARE_SERIAL /*AttinyDigisparkCore condition*/\
      || ((defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(LINBRRH)) && !USE_SOFTWARE_SERIAL)/*AttinyDigisparkCore condition for HardwareSerial*/
  // Switch to SerialOut since Serial is already defined
  // or activate line 745 in TinyDebugSerial.h included in AttinyDigisparkCores/src/tiny/WProgram.h at line 24 for AttinyDigisparkCores
  TinySerialOut SerialOut;
  #else
  TinySerialOut Serial;
  #endif
  
  /********************************
   * Basic serial output function
   *******************************/
  
  inline void delay4CyclesInlineExact(uint16_t a4Microseconds) {
      /*
       * The loop takes 4 cycles (4 microseconds  at 1 MHz). Last loop is only 3 cycles. Setting of loop counter a4Microseconds needs 2 cycles
       * 3 -> 13 cycles (3*4 -1 + 2) = 3*4 + 1
       * 4 -> 17 cycles
       * 5 -> 21 cycles
       */
      asm volatile (
              "1: sbiw %0,1" "\n\t" // 2 cycles
              "brne .-4" : "=w" (a4Microseconds) : "0" (a4Microseconds)// 2 cycles
      );
  }
  
  #if (F_CPU == 1000000) && defined(USE_115200BAUD) //else smaller code, but only 38400 baud at 1 MHz
  /*
   * 115200 baud - 8,680 cycles per bit, 86,8 per byte at 1 MHz
   *
   *  Assembler code for 115200 baud extracted from Digispark core files:
   *  Code size is 196 Byte (including first call)
   *
   *   TinySerialOut.h - Tiny write-only software serial.
   *   Copyright 2010 Rowdy Dog Software. This code is part of Arduino-Tiny.
   *
   *   Arduino-Tiny is free software: you can redistribute it and/or modify it
   *   under the terms of the GNU Lesser General Public License as published by
   *   the Free Software Foundation, either version 3 of the License, or (at your
   *   option) any later version.
   */
  void write1Start8Data1StopNoParity(uint8_t aValue) {
      asm volatile
      (
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- 0 */
              "ror   %[value]" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b0h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- st is 9 cycles */
              "rjmp  L%=b0z" "\n\t" /* 2 */
              "L%=b0h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- st is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b0z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b1h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b0 is 8 cycles */
              "rjmp  L%=b1z" "\n\t" /* 2 */
              "L%=b1h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b0 is 8 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b1z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b2h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b1 is 9 cycles */
              "rjmp  L%=b2z" "\n\t" /* 2 */
              "L%=b2h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b1 is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b2z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b3h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b2 is 9 cycles */
              "rjmp  L%=b3z" "\n\t" /* 2 */
              "L%=b3h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b2 is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b3z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b4h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b3 is 8 cycles */
              "rjmp  L%=b4z" "\n\t" /* 2 */
              "L%=b4h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b3 is 8 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b4z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b5h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b4 is 9 cycles */
              "rjmp  L%=b5z" "\n\t" /* 2 */
              "L%=b5h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b4 is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b5z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b6h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b5 is 9 cycles */
              "rjmp  L%=b6z" "\n\t" /* 2 */
              "L%=b6h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b5 is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b6z: "
              "ror   %[value]" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
  
              "brcs  L%=b7h" "\n\t" /* 1  (not taken) */
              "nop" "\n\t" /* 1 */
              "cbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b6 is 8 cycles */
              "rjmp  L%=b7z" "\n\t" /* 2 */
              "L%=b7h: " /* 2  (taken) */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b6 is 8 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "L%=b7z: "
              "nop" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
  
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "sbi   %[txport], %[txpin]" "\n\t" /* 2  <--- b7 is 9 cycles */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              "nop" "\n\t" /* 1 */
              /*    <---sp is 9 cycles */
  
              :
              :
              [value] "r" ( aValue ),
              [txport] "I" ( TX_PORT_ADDR ),
              [txpin] "I" ( TX_PIN )
      );
  }
  #else
  /*
   * Small code using loop. Code size is 76 Byte (including first call)
   *
   * 1 MHz CPU Clock
   *  26,04 cycles per bit, 260,4 per byte for 38400 baud at 1 MHz Clock
   *  17,36 cycles per bit, 173,6 per byte for 57600 baud at 1 MHz Clock -> therefore use 38400 baud
   *  24 cycles between each cbi/sbi (Clear/Set Bit in IO-register) command.
   *
   * 8 MHz CPU Clock
   *  69,44 cycles per bit, 694,4 per byte for 115200 baud at 8 MHz Clock
   *  34,72 cycles per bit, 347,2 per byte for 230400 baud at 8 MHz Clock.
   *  68 / 33 cycles between each cbi (Clear Bit in IO-register) or sbi command.
   *
   * 16 MHz CPU Clock
   *  138,88 cycles per bit, 1388,8 per byte for 115200 baud at 16 MHz Clock
   *  69,44 cycles per bit, 694,4 per byte for 230400 baud at 16 MHz Clock
   *  137 / 68 cycles between each cbi (Clear Bit in IO-register) or sbi command.
   *
   * 2 cycles for each cbi/sbi instruction.
   */
  void write1Start8Data1StopNoParity(uint8_t aValue) {
      asm volatile
      (
              "cbi  %[txport] , %[txpin]" "\n\t" // 2    PORTB &= ~(1 << TX_PIN);
  #if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud
              // 0 cycles padding to get additional 4 cycles
              //delay4CyclesInlineExact(5); -> 20 cycles
              "ldi  r30 , 0x05" "\n\t"// 1
  #elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud
              // 3 cycles padding to get additional 7 cycles
              "nop" "\n\t"// 1    _nop"();
              "nop" "\n\t"// 1    _nop"();
              "nop" "\n\t"// 1    _nop"();
              //delay4CyclesInlineExact(15); -> 61 cycles
              "ldi  r30 , 0x0F" "\n\t"// 1
  #elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud
              // 0 cycles padding to get additional 4 cycles
              //delay4CyclesInlineExact(7); -> 29 cycles
              "ldi  r30 , 0x07" "\n\t"// 1
  #elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud
              // 0 cycles padding to get additional 4 cycles
              //delay4CyclesInlineExact(33); -> 133 cycles
              "ldi  r30 , 0x21" "\n\t"// 1
  #endif
              "ldi  r31 , 0x00" "\n\t"            // 1
              "delay1:"
              "sbiw r30 , 0x01" "\n\t"// 2
              "brne delay1" "\n\t"// 1-2
  
              "ldi r25 , 0x08" "\n\t"// 1
  
              // Start of loop
              // if (aValue & 0x01) {
              "txloop:"
              "sbrs %[value] , 0" "\n\t"// 1
              "rjmp .+6" "\n\t"// 2
  
              "nop" "\n\t"// 1
              "sbi %[txport] , %[txpin]" "\n\t"// 2    PORTB |= 1 << TX_PIN;
              "rjmp .+6" "\n\t"// 2
  
              "cbi %[txport] , %[txpin]" "\n\t"// 2    PORTB &= ~(1 << TX_PIN);
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              "lsr %[value]" "\n\t"// 1    aValue = aValue >> 1;
  
  #if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud
              // 3 cycles padding to get additional 11 cycles
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              // delay4CyclesInlineExact(3); -> 13 cycles
              "ldi  r30 , 0x03" "\n\t"// 1
  #elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud
              // 3 cycles padding to get additional 11 cycles
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              // delay4CyclesInlineExact(14); -> 57 cycles
              "ldi r30 , 0x0E" "\n\t"// 1
  #elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud
              // 0 cycles padding to get additional 8 cycles
              // delay4CyclesInlineExact(6); -> 25 cycles
              "ldi r30 , 0x05" "\n\t"// 1
  #elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud
              // 0 cycles padding to get additional 8 cycles
              //delay4CyclesInlineExact(32); -> 129 cycles
              "ldi  r30 , 0x20" "\n\t"// 1
  #endif
              "ldi r31 , 0x00" "\n\t"            // 1
              "delay2:"
              "sbiw r30 , 0x01" "\n\t"// 2
              "brne delay2" "\n\t"// 1-2
  
              // }while (i > 0);
              "subi r25 , 0x01" "\n\t"// 1
              "brne txloop" "\n\t"// 1-2
              // To compensate for missing loop cycles at last bit
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
              "nop" "\n\t"// 1
  
              // Stop bit
              "sbi %[txport] , %[txpin]" "\n\t"// 2    PORTB |= 1 << TX_PIN;
  
  #if (F_CPU == 1000000) && !defined(USE_115200BAUD) // 1 MHz 38400 baud
              // delay4CyclesInlineExact(4); -> 17 cycles - gives minimum 25 cycles for stop bit
              "ldi  r30 , 0x04" "\n\t"// 1
  #elif ((F_CPU == 8000000) && defined(USE_115200BAUD)) || ((F_CPU == 16000000) && !defined(USE_115200BAUD)) // 8 MHz 115200 baud OR 16 MHz 230400 baud
              // delay4CyclesInlineExact(15) -> 61 cycles - gives minimum 69 cycles for stop bit
              "ldi r30 , 0x0F" "\n\t"// 1
  #elif (F_CPU == 8000000) && !defined(USE_115200BAUD) // 8 MHz 230400 baud
              // delay4CyclesInlineExact(5) -> 27 cycles - gives minimum 35 cycles for stop bit
              "ldi r30 , 0x05" "\n\t"// 1
  #elif (F_CPU == 16000000) && defined(USE_115200BAUD) // 16 MHz 115200 baud
              // delay4CyclesInlineExact(32) -> 129 cycles - gives minimum 137 cycles for stop bit
              "ldi r30 , 0x20" "\n\t"// 1
  #endif
              "ldi r31 , 0x00" "\n\t"            // 1
              "delay3:"
              "sbiw r30 , 0x01" "\n\t"//
              "brne delay3" "\n\t"// 1-2
              // return needs 4 cycles, load of next value needs 1 cycle, next rcall needs 3 cycles -> gives additional 8 cycles minimum for stop bit
  
              :
              :
              [value] "r" ( aValue ),
              [txport] "I" ( TX_PORT_ADDR ) , /* 0x18 is PORTB on Attiny 85 */
              [txpin] "I" ( TX_PIN )
              :
              "r25",
              "r30",
              "r31"
      );
  
  }
  #endif
  
  /*
   * C Version which generates the assembler code above.
   *      In order to guarantee the correct timing, compile with Arduino standard settings or:
   *      avr-g++ -I"C:\arduino\hardware\arduino\avr\cores\arduino" -I"C:\arduino\hardware\arduino\avr\variants\standard" -c -g -w -Os -ffunction-sections -fdata-sections -mmcu=attiny85 -DF_CPU=1000000UL -MMD -o "TinySerialOut.o" "TinySerialOut.cpp"
   *      Tested with Arduino 1.6.8 and 1.8.5/gcc4.9.2
   *      C Version does not work with AVR gcc7.3.0, since optimization is too bad
   */
  void write1Start8Data1StopNoParity_C_Version(uint8_t aValue) {
      /*
       * C Version here for 38400 baud at 1 MHz Clock. You see, it is simple :-)
       */
  // start bit
      TX_PORT &= ~(1 << TX_PIN);
      _NOP();
      delay4CyclesInlineExact(4);
  
  // 8 data bits
      uint8_t i = 8;
      do {
          if (aValue & 0x01) {
              // bit=1
              // to compensate for jump at data=0
              _NOP();
              TX_PORT |= 1 << TX_PIN;
          } else {
              // bit=0
              TX_PORT &= ~(1 << TX_PIN);
              // compensate for different cycles of sbrs
              _NOP();
              _NOP();
          }
          aValue = aValue >> 1;
          // 3 cycles padding
          _NOP();
          _NOP();
          _NOP();
          delay4CyclesInlineExact(3);
          --i;
      } while (i > 0);
  
  // to compensate for missing loop cycles at last bit
      _NOP();
      _NOP();
      _NOP();
      _NOP();
  
  // Stop bit
      TX_PORT |= 1 << TX_PIN;
  // -8 cycles to compensate for fastest repeated call (1 ret + 1 load + 1 call)
      delay4CyclesInlineExact(4); // gives minimum 25 cycles for stop bit :-)
  }
  #endif // defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
  