/* GP3 Relay Calculator 
   April 1, 2013 by Al Williams
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include <gp3lib.h>

// Delay in uS so *1000 if you prefer milliseconds
#define RELAYDELAY (150*1000)

// Set to 1 for extra chatter
#define EXTRACHATTER 1

// GP3 IO Ports
#define AOUT 0
#define BOUT 1
#define BNOTOUT 2
#define SUMIN 7
#define CYIN 6

// Insert a zero to the left of a binary string
void inszero(char *p)
{
  char *pend=p+strlen(p);
  do  // make room, make room
    {
      *(pend+1)=*pend;
      pend--;
    } while (pend!=p);
  *(p+1)=*p;  // save first character
  *p='0';  // replace with '0'
}

// Add two numbers with the
// electromechanical power of relays!
int add2(int a, int b, int *carry)
{
  // Set A and B
  a?gp3high(AOUT):gp3low(AOUT);
  if (b)
    {
      gp3high(BOUT);
      gp3low(BNOTOUT);
    }
  else
    {
      gp3low(BOUT);
      gp3high(BNOTOUT);
    }
  // Let relays do their magic
  usleep(RELAYDELAY);
  // Read carry and sum from relay inputs  
  if (carry) *carry=(gp3input(CYIN)=='1');
  return gp3input(SUMIN)=='1';
}

// Zero out A and B
void cleargp3()
{
  gp3low(AOUT);
  gp3low(BOUT);
  gp3high(BNOTOUT);
}

// Set A=B=1 (used for artificial chatter)
void setgp3()
{
  gp3high(AOUT);
  gp3high(BOUT);
  gp3low(BNOTOUT);
}


int readInput(char *s)
{
        
      scanf("%s",s);
      if (s[0]=='x'||s[0]=='X') return -1;
      if (strspn(s,"01")!=strlen(s))
	{
	  printf("Invalid input!\n");
	  return 0;
	}
      return 1;
}


int main(int argc, char *argv[])
{
  char a[64],b[64];
  char res[65];
  unsigned l;
  int carry0, carry1, lastcarry;
  // Open relay interface
  if (gp3openport(argc>1?argv[1]:"/dev/ttyS1")==-1)
    {
      perror("Error opening relay interface");
      exit(1);
    }
  // set LED on board just to show we are alive!
  gp3setLED(1);
  // A=B=0
  cleargp3();
  // Now get input and do the math
  do 
    {
      int n;
      
      printf("Relay calculator! April 1, 2013 by Al Williams\n");
      printf("Enter two binary numbers A&B\n");
      printf("Enter x to exit\n");
      printf("A: ");
      if (!(n=readInput(a))) continue;
      if (n==-1) break;
      
      printf("B: ");
      if (!(n=readInput(b))) continue;
      if (n==-1) break;
     
      memset(res,' ',sizeof(res));
      // Pad both strings with zeros until same size
      while (strlen(a)<strlen(b))
	{
	  inszero(a);
	}
      while (strlen(b)<strlen(a))
	{
	  inszero(b);
	}
      printf("  %s\n+ %s\n",a,b);
      l=strlen(a);
      res[l+1]='\0';
      // start carry chain at 0
      lastcarry=0;
      // While still digitis to go
      while (l--)
	{
	  unsigned idx=l;
	  int temp;
	  int result;
	  // Ask relays for 1/2 add result
	  temp=add2(a[l]=='1',b[l]=='1',&carry0);
	  // Add carry back in for a full add
	  result=add2(temp,lastcarry,&carry1);
	  // Propagate any carry forward
	  lastcarry=carry1||carry0;
	  res[l+1]=result?'1':'0';
	  // Show result (note \r lets us stay on the line)
	  printf(" %s\r",res);
	  fflush(stdout);  // make sure it  writes
// chatter for effect; without this strings of the same bits are boring
#if EXTRACHATTER==1   
	  setgp3();
	  usleep(RELAYDELAY);
	  cleargp3();
	  usleep(RELAYDELAY);
#endif
	}
      // show top bit as final carry
      res[0]=lastcarry?'1':'0';
      // Print the whole answer (and new line this time)
      printf(" %s\n",res);
    } while (1);
  // shutdown
  gp3setLED(0);
  cleargp3();
  gp3closeport();
}
