/*

	This file handle the low-level communication.
	It supports both usual Parallel port and the $4 serial link.
	Just put HARD_VERSION = HARD_VERSION_PAR or HARD_VERSION_SER
	to switch between them
*/


//#define DEBUG
//#define STAT

#define HARD_VERSION_PAR 1
#define HARD_VERSION_SER 2

#define HARD_VERSION HARD_VERSION_SER


#include <stdio.h>
#include <stdlib.h>

/* Amiga */
/* Hardware : */
#include <exec/types.h>
#include <hardware/cia.h>

/* System : */
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <devices/serial.h>
#include <devices/parallel.h>
#include <proto/dos.h>
#include <proto/exec.h>

#include "hard.h"

/* Two lines between computer and calc : */

#define LINE1 1
#define LINE2 2


#if HARD_VERSION == HARD_VERSION_SER
#elif HARD_VERSION == HARD_VERSION_PAR
#endif


/* Two states for each line : */
#define HIGH  0
#define LOW   1



/* Assert a state on a line : */
inline void   ti_setline(int value, UBYTE where);
/* Get state of a line : */
inline UBYTE  ti_getline(UBYTE where);


/* Amiga Specific : */
extern struct CIA ciaa, ciab;
struct MsgPort  *SerialMP=NULL;     /* Message port pointer */
struct IOExtSer *SerialIO=NULL;    /* I/O request pointer */

struct MsgPort  *ParallelMP=NULL;     /* Message port pointer */
struct IOExtPar *ParallelIO=NULL;    /* I/O request pointer */

/* Keep state in static, so that we can remember what we did at init,
	 and what we didn't
*/
static int state=0;
#define STATE_PORT  1
#define STATE_EXTIO 2
#define STATE_DEV   4




/*
	Timer stuff
	We use timer only for timeout or retry delays
*/
#include "timer.h"
Timer_Info time_info;


#define HARD_MAX_TRY_ACTIVE     100
#define HARD_MAX_TRY            (HARD_MAX_TRY_ACTIVE+30)

/* Wait after retrying, if the calc. does not ack enough quickly */
#define WAIT_RETR timer_delay(&time_info,10000)

/* Wait if a timeout occures : */
#define WAIT_TIMEOUT timer_delay(&time_info,200000)


/*
	if debug_mode == 0 then we are system compliant : ask for device
	Otherwise, we are not
*/
extern int debug_mode;


char *ti_version(void )
{
#if HARD_VERSION == HARD_VERSION_SER
	return("serial version ("__DATE__")");
#elif HARD_VERSION == HARD_VERSION_PAR
	return("parallel version ("__DATE__")");
#endif

}
int ti_openport(void )
{
  if ( ! debug_mode )
  {
#if HARD_VERSION == HARD_VERSION_SER
		/* Ask for the serial port */
	  if (  !( SerialMP=CreatePort(NULL,0)) )
	  {
	    printf(__FILE__"> CreatePort failed.\n");
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_PORT;

	  if ( !( SerialIO=(struct IOExtSer *)CreateExtIO(SerialMP,sizeof(struct IOExtSer)) ) )
	  {
	    printf(__FILE__"> Unable to open SerialIO.\n");
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_EXTIO;

	  if ( OpenDevice(SERIALNAME,0L,(struct IORequest *)SerialIO,0))
	  {
	    printf(__FILE__"> Unable to open serial port ( %s unit 0 )\n",SERIALNAME);
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_DEV;
#elif HARD_VERSION == HARD_VERSION_PAR
		/* Ask for the parallel port */
		if ( ! ( ParallelMP=CreatePort(NULL,0)  ) )
	  {
	    printf(__FILE__"> CreatePort failed.\n");
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_PORT;
		
		if ( ! ( ParallelIO=(struct IOExtPar *)CreateExtIO(ParallelMP,sizeof(struct IOExtPar)) ) )
	  {
			printf(__FILE__"> Unable to open ParallelIO.\n");
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_EXTIO;

		if (OpenDevice(PARALLELNAME,0L,(struct IORequest *)ParallelIO,0) )
	  {
			printf(__FILE__"> Unable to open parallel port ( %s unit 0 )\n",PARALLELNAME);
	    ti_closeport();
	    return(1);
	  }
	  state += STATE_DEV;
#endif
  }
	/* We now have the serial or paralle device for ourselves */
  ti_initport();


  if ( timer_create(&time_info) )
  {
    printf(__FILE__"> Error creating timer\n");
    ti_closeport();
    return(1);
  }

  return(0); /* No error */
}

void ti_closeport(void )
{
  ti_initport();
  if ( ! debug_mode )
  {
#if HARD_VERSION == HARD_VERSION_SER
    if ( state & STATE_DEV )
      CloseDevice((struct IORequest *)SerialIO);

    if ( state & STATE_EXTIO )
      DeleteExtIO((struct IORequest *)SerialIO);
    if ( state & STATE_PORT )
      DeletePort(SerialMP);
#elif HARD_VERSION == HARD_VERSION_PAR
    if ( state & STATE_DEV )
			CloseDevice((struct IORequest *)ParallelIO);

    if ( state & STATE_EXTIO )
			DeleteExtIO((struct IORequest *)ParallelIO);
    if ( state & STATE_PORT )
			DeletePort(ParallelMP);
#endif
  }
  timer_delete(&time_info);
  state = 0;
}


inline void ti_setline(int value, UBYTE where)
{
#if HARD_VERSION == HARD_VERSION_SER
	if ( where == LINE1 )
		where = CIAF_COMRTS;
	else
		where = CIAF_COMDTR;

	if ( value == LOW )
    ciab.ciapra=ciab.ciapra|where;
  else
    ciab.ciapra=ciab.ciapra&(where^0xFF);
#elif HARD_VERSION == HARD_VERSION_PAR
	if ( where == LINE1 )
		where = 2;
	else
		where = 1;

	if ( value == HIGH )
		ciaa.ciaprb|=where;
  else
		ciaa.ciaprb&=(where^0xFF);
#endif
}



inline UBYTE ti_getline(UBYTE where)
{
#if HARD_VERSION == HARD_VERSION_SER
	if ( where == LINE1 )
		where = CIAF_COMCTS;
	else
		where = CIAF_COMDSR;

  if ( ciab.ciapra&where )
		return(LOW);
  else
		return(HIGH);
#elif HARD_VERSION == HARD_VERSION_PAR
	if ( where == LINE1 )
		where = CIAF_PRTRSEL;
	else
		where = CIAF_PRTRPOUT;

	if ( ciab.ciapra&where )
		return(HIGH);
  else
		return(LOW);
#endif

}


inline void ti_initport(void )
{
#if HARD_VERSION == HARD_VERSION_SER
	ti_setline(HIGH,LINE1);
	ti_setline(HIGH,LINE2);
  ciaa.ciaddrb = 0xc0;

#elif HARD_VERSION == HARD_VERSION_PAR
	ciaa.ciaddrb = 0x03;
	ciab.ciaddra = 0x00;
	ti_setline(HIGH,LINE1);
	ti_setline(HIGH,LINE2);
#endif
}


inline int ti_sendbit(UBYTE bit )
{
  int where1, where2;
	unsigned long int count;

  if ( bit )
  {
		where1 = LINE2;
		where2 = LINE1;
  }
  else
  {
		where1 = LINE1;
		where2 = LINE2;
  }

  ti_setline(LOW,where1);

	count=0;
	do
	{	if ( ++count >= HARD_MAX_TRY_ACTIVE )
			WAIT_RETR;
  }
	while ( ti_getline(where2) != LOW && count <= HARD_MAX_TRY );

  if ( count > HARD_MAX_TRY )
  {
#ifdef DEBUG
		printf(__FILE__"> sendbit Timeout send 1 (%ld)\n",count);
#endif
    WAIT_TIMEOUT;
    return(HARD_TIMEOUT);
  }

#ifdef STAT
  printf(__FILE__"> sendbit Acquittement recu , count=%ld\n",count);
#endif
  ti_setline(HIGH,where1);

  count = 0;
	do
	{	if ( ++count >= HARD_MAX_TRY_ACTIVE )
			WAIT_RETR;
  }
	while ( ti_getline(where2) != HIGH && count <= HARD_MAX_TRY );
#ifdef STAT
  printf(__FILE__"> sendbit Fin acquittement recu , count=%ld\n",count);
#endif

  if ( count > HARD_MAX_TRY )
  {
#ifdef DEBUG
    printf(__FILE__"> sendbit Timeout send 2 (%ld)\n",count);
#endif
    WAIT_TIMEOUT;
    return(HARD_TIMEOUT);
  }

  return(0);
}

inline int ti_recvbit(UBYTE *result )
{
  unsigned long int count,count2;

  count2 = 0;

  do
  {
		if ( ti_getline(LINE1) == LOW )
    {
#ifdef STAT
			printf(__FILE__"> recvbit CTS==LOW count=%ld\n",count2);
#endif
			ti_setline(LOW,LINE2);


      count=0;
			do
			{	if ( ++ count >= HARD_MAX_TRY_ACTIVE )
          WAIT_RETR;
      }
			while ( ti_getline(LINE1) == LOW && count <= HARD_MAX_TRY );

			ti_setline(HIGH,LINE2);
			/* We just make sure that our previous order is on the line : */
			count = 0;
			do {count++;}while(ti_getline(LINE2)!=HIGH&&count<HARD_MAX_TRY_ACTIVE);

      *result = 0;

      if ( count > HARD_MAX_TRY )
      {
#ifdef DEBUG
        printf(__FILE__"> Timeout recv A (%ld)\n",count);
#endif
        WAIT_TIMEOUT;
        return(HARD_TIMEOUT);
      }

      return(0);
    }

		if ( ti_getline(LINE2) == LOW )
    {
#ifdef STAT
			printf(__FILE__"> recvbit DSR==LOW count=%ld\n",count2);
#endif
			ti_setline(LOW,LINE1);


      count=0;
			do
			{
				if ( ++count >= HARD_MAX_TRY_ACTIVE )
          WAIT_RETR;
      }
			while ( ti_getline(LINE2) == LOW && count <= HARD_MAX_TRY );

			ti_setline(HIGH,LINE1);

			count = 0;
			do {count++;} while(ti_getline(LINE1)!=HIGH&&count<HARD_MAX_TRY_ACTIVE);
#ifdef STAT
			printf(__FILE__"> recvbit result=1, count=%ld\n",count);
#endif

      *result = 1;


      if ( count > HARD_MAX_TRY )
      {
#ifdef DEBUG
        printf(__FILE__"> rB%ld\n",count);
        printf(__FILE__"> Timeout recv B (%ld)\n",count);
#endif
        WAIT_TIMEOUT;
        return(HARD_TIMEOUT);
      }

      return(0);
    }
		/*if ( count2 >= HARD_MAX_TRY_ACTIVE )
			WAIT_RETR;*/
    count2++;
  }
	while(count2<100*HARD_MAX_TRY);

#ifdef DEBUG
  printf(__FILE__"> Timeout recv C (%ld)\n",count2);
#endif
  ti_initport();
	//WAIT_TIMEOUT;
  return(HARD_TIMEOUT);
}

int ti_recvbyte(UBYTE *result )
{
  int cpt;
  UBYTE temp;

  *result=0;

  for ( cpt=0; cpt<BITS_PER_BYTE; cpt++)
  {
    if ( ti_recvbit(&temp) == HARD_TIMEOUT )
    {
#ifdef DEBUG
			printf("bytes.c> Error (r) on bit %d\n",cpt);
#endif
      return(BYTE_ERROR);
    }
#ifdef LOG
    //printf("%d",temp);
#endif
    *result=*result | ( temp << cpt );
  }

#ifdef DEBUG
  if ( isalpha(*result) )
		printf("Log> Got %2X (%c)\n",*result,*result);
  else
		printf("Log> Got %2X\n",*result);
#endif

	//WAIT_RETR;

  return(0);
}

int ti_sendbyte(UBYTE byte)
{
  int cpt=0;

#ifdef DEBUG
 if ( isalpha(byte) )
		printf("Log> Put %2X (%c)\n",byte,byte);
  else
		printf("Log> Put %2X\n",byte);
#endif

  for ( cpt=0; cpt<BITS_PER_BYTE; cpt++)
  {
    //printf("%d",byte&1);
    if ( ti_sendbit( byte & 1 ) == HARD_TIMEOUT )
    {
#ifdef DEBUG
      printf("bytes.c> Erreur (s) sur le bit %d\n",cpt);
#endif
      return(BYTE_ERROR);
    }
    byte = byte >> 1;
  }
	//WAIT_RETR;
  return(0);

}

int ti_sendbytes(UBYTE *bytes,int nb)
{
  int cpt;

  for ( cpt=0; cpt<nb; cpt++)
  {
    if ( ti_sendbyte(bytes[cpt]) == BYTE_ERROR )
      return(BYTE_ERROR);
  }
  return(0);

}



