#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>




void consolePrint (char* s);
void consolePrintln (char* s);




//#################################################################################################
//### FIXPOINT START ##############################################################################
//#################################################################################################


//-------------------------------------------------------------------------------------- 
//--- Basis-Fixpoint-Arithmetik-Routinen ------------------- Version vom 08.Feb.2007 ---
//--- Zulässiger Wertebereich ist -32767.000 bis 32767.000 -----------------------------
//--- Die Nachkommagenauigkeit beträgt lediglich 10 Bit --------------------------------
//--- atofixpoint und fixpointtoa wandeln so gut sie können, aber nicht präzise --------
//--- Also nicht vergessen: Die Rechenergebnisse sind reichlich ungenau ----------------
//-------------------------------------------------------------------------------------- 
//--- (c) 2007 Ralf Rosenkranz ---------------------------------------------------------
//--- voltax ätt prima punkt de --------------------------------------------------------
//--- http://basic.3dc.de --------------------------------------------------------------
//-------------------------------------------------------------------------------------- 
//--- Die Routinen scheinen zu funktionieren aber ich garantiere für nichts ------------
//--- Ich hafte für keinerlei Schäden und übernehme keine Gewährleistung ---------------
//--- Dieser Quellcode darf nur für *nichtkommerzielle* Zwecke frei verwendet werden ---
//-------------------------------------------------------------------------------------- 


typedef int32_t fixpoint;

#define pixpointFractShift 10 //--- (NUR 10 geht !!!) Und muß für sqrt GERADE sein ---
#define fixpointRealBits (32-1-pixpointFractShift)



char* fixpointtoa (fixpoint f, char* str)
{
   char* result = str;

   if (f < 0)
   {
      f = - f;
      *str++ = '-';
   }

   uint16_t vk = f >> pixpointFractShift;
   uint16_t nk = f & ((1 << pixpointFractShift) - 1);
   uint16_t snk;

   itoa (vk, str, 10);
   
   if (nk != 0)
   {
      snk = (nk*29)/28;  //--- 512 -> 500 ---
      snk = (snk*17)/18; //--- gut genug so ---      
      
      if (snk > 999)      
      {
         snk = 999;
      }
      else if (snk < 1)
      {      
         snk = 1;
      }

      size_t len = strlen (str);
      str [len++] = '.';

      uint16_t p = nk * 10;
      uint16_t t = 1024;
   
      while (p < t)
      {
         str [len++] = '0';
         p *= 10;
      }

      itoa (snk, str + len, 10);   
   }
   
   return result;
}      



fixpoint int16ToFixpoint (int16_t i)
{
   return ((fixpoint) i) << pixpointFractShift;
}   



fixpoint atofixpoint (char* str)
{
   char vkBuf [6];
   char nkBuf [4];

   bool neg = false;
   uint8_t n = 0;
   uint8_t m = 0;
   uint8_t c;
   bool pointFound = false;
   
   
   while (((c = str [n]) != '.') && (c != 0) && (n < sizeof (vkBuf) - 1))
   {
      if ((n == 0) && (c == '-'))
      {
         neg = true;
      }
      else
      {
         vkBuf [m] = c;
         m++;
      }

      n++;
   }

   vkBuf [m] = 0;
   m = 0;

   if (c == '.')
   { 
      pointFound = true;
      n++;

      while (((c = str [n]) != 0) && (m < sizeof (nkBuf) - 1))
      {
         nkBuf [m] = c;
         n++;
         m++;
      }

      nkBuf [m] = 0;
   }
   else
   {
      nkBuf [0] = 0;
   }
         
   
   int32_t i = atoi (vkBuf);
   fixpoint f = ((fixpoint) i) << pixpointFractShift;
   
   
   if (pointFound)
   {
      for (m = 0; m < sizeof (nkBuf) - 1; m++)  
      {
         if (nkBuf [m] == 0)
         {
            nkBuf [m] = '0';
            nkBuf [m+1] = 0;
         }
      }
   
   
      int16_t r = atoi (nkBuf);            
      int16_t sr = r;
   
   
      if (sr != 0)
      {
         if (sr < 0)
         {
            sr = -sr;
         }
         
         sr = (sr*17)/16; //--- 500 -> 512 ---
         sr = (sr*28)/29; //--- gut genug so ---
      
         if (sr > 1023)      
         {
            sr = 1023;
         }
         else if (sr < 1)      
         {
            sr = 1;
         }
   
         f |= sr;
      }
   }   

   if (neg)
   {
      return -f;
   }
   else
   {
      return f;
   }
}   



int16_t fixpointToInt16 (fixpoint f)
{
   return (int16_t) (f >> pixpointFractShift);   
}   



fixpoint fixpointAdd (fixpoint a, fixpoint b)
{
   return a + b;   
}



fixpoint fixpointSub (fixpoint a, fixpoint b)
{
   return a - b;
}



fixpoint fixpointMult (fixpoint a, fixpoint b)
{   
   uint8_t aBits = 0;
   fixpoint aProbe = a;
  
   if (aProbe < 0)
   {
      aProbe = -aProbe;
   }

   while (aProbe != 0)
   {
      aProbe >>= 1;
      aBits ++;
   }
  
   uint8_t bBits = 0;
   fixpoint bProbe = b;

   if (bProbe < 0)
   {
      bProbe = -bProbe;
   }

   while (bProbe != 0)
   {
      bProbe >>= 1;
      bBits ++;
   }
  
   if (aBits + bBits < fixpointRealBits)
   {
      return (a * b) >> pixpointFractShift;
   }
   else
   {
      return (a >> (pixpointFractShift >> 1)) * (b >> (pixpointFractShift >> 1));
   }
}



fixpoint fixpointDiv (fixpoint a, fixpoint b)
{ 
   uint8_t aBits = 0;
   fixpoint aProbe = a;
  
   if (aProbe < 0)
   {
      aProbe = -aProbe;
   }

   while (aProbe != 0)
   {
      aProbe >>= 1;
      aBits ++;
   }
    
   if (aBits < fixpointRealBits)
   {    
      return (a << pixpointFractShift) / b;
   }
   else
   {
      return (a << (pixpointFractShift >> 1)) / (b >> (pixpointFractShift >> 1));
   }   
}



fixpoint fixpointSqr (fixpoint x)
{   
   return fixpointMult (x, x);
}



fixpoint fixpointSqrt (fixpoint x)
{
   uint32_t r = 0;         
   uint32_t hi = 0;       
   uint32_t lo = x;        
   uint32_t c = 15 + (pixpointFractShift >> 1);
   uint32_t d;

   do 
   {
      hi = (hi << 2) | (lo >> 30);  
      lo <<= 2;
      r <<= 1;   
      d = (r << 1) + 1;   
      
      if (hi >= d) 
      {
         hi -= d;
         r += 1;
      }
      
   } while (c-- != 0);

   return (r);
}



//#################################################################################################
//### FIXPOINT ENDE ###############################################################################
//#################################################################################################



//-------------------------------------------------------------------------------------------------
//--- Routinen für die serielle Schnittstelle (ATmege168 und ATmega32 und codekompatible Typen) ---
//-------------------------------------------------------------------------------------------------


#ifndef UBRR0H
#define UBRR0H UBRRH 
#define UBRR0L UBRRL
#define UCSR0B UCSRB
#define RXEN0 RXEN
#define TXEN0 TXEN
#define UCSR0C UCSRC
#define UCSZ01 UCSZ1
#define UCSZ00 UCSZ0
#define UCSR0A UCSRA
#define UDRE0 UDRE
#define UDR0 UDR
#define UCSR0A UCSRA
#define RXC0 RXC
#define UCSR0A UCSRA
#define UDRE0 UDRE
#define UDR0 UDR
#endif



void USART0_init (uint32_t baud)
{
   //--- Set baud rate ---

   uint16_t ubrr = (uint16_t) ((F_CPU / (baud << 4)) - 1);

   UBRR0H = (uint8_t) (ubrr >> 8);
   UBRR0L = (uint8_t) ubrr;

   //--- Enable receiver and transmitter ---

   UCSR0B = (1 << RXEN0) | (1 << TXEN0);

   //--- Set frame format: 8data, 1stop bit ---

   UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}


void USART0_sendByte (uint8_t data)
{
   //--- Wait for empty transmit buffer ---

   while (! (UCSR0A & (1 << UDRE0)));

   //--- Put data into buffer, sends the data ---

   UDR0 = data;
}


uint8_t USART0_receiveByte (void)
{
   //--- Wait for data to be received ---

   while (! (UCSR0A & (1 << RXC0)));

   //--- Get and return received data from buffer ---

   return UDR0;
}



void consolePrint (char* s)
{
   uint8_t len = 0;
   char c;

   while (((c = s [len]) != 0) && (len < 0xFF))
   {
      USART0_sendByte (c);
      len++;
   }   
}


void consolePrintln (char* s)
{
   uint8_t len = 0;
   char c;

   while (((c = s [len]) != 0) && (len < 0xFF))
   {
      USART0_sendByte (c);
      len++;
   }

   USART0_sendByte ('\r');
   USART0_sendByte ('\n');
}








//-------------------------------------------------------------------------------------------------
//--- Als Anwendungsbeispiel hier ein Apfelmännchen in Buchstabengrafik ---------------------------
//-------------------------------------------------------------------------------------------------

int main (void)
{
   //--- Ausgabe per serieller Schnittstelle mit 9600 Baud,8,n,1 ---

   USART0_init (9600);
    
   //--- Der Code dieses Apfelmännchens basiert auf einem Java-Programm von Bernd Hohmann ---
      
   fixpoint a;
   fixpoint b;
   fixpoint c;
   fixpoint d;
   fixpoint e;
   fixpoint f;
   char buf [2];
   char g;
   int16_t h;
   
   for (b = atofixpoint ("1.1"); b > atofixpoint ("-1.2"); b = b - atofixpoint ("0.1"))
   {
      for (a = int16ToFixpoint (-2); a < int16ToFixpoint (1); a = a + atofixpoint ("0.04"))
      {
         c = int16ToFixpoint (0);
         d = int16ToFixpoint (0);
    
         for (f = int16ToFixpoint (128); (fixpointSqr (c) + fixpointSqr (d) < int16ToFixpoint (4)) && ((f = f - int16ToFixpoint (1)) > int16ToFixpoint (32));)
         {
            e = c;
            c = fixpointSqr (c) - fixpointSqr (d) + a;
            d = fixpointMult (fixpointMult (int16ToFixpoint (2), e), d) + b;
         }
   
         h = fixpointToInt16 (f);
         
         g= ' '; 
         if (h>=64) g = '*';
         if (h>=96) g = 'O';
         if (h>=112) g = 'H';
         if (h>=120) g = 'x';
         if (h>=124) g = '=';
         if (h>=126) g = '-';
         if (h>=127) g = '+';
         
         buf [0] = g;
         buf [1] = 0;
         consolePrint (buf);
      }
   
      consolePrintln ("");
   }

   
   while (1==1);
   return (0);
}
