/***************************************************************************/
/*         --------------------------------------------------------        */
/*                        Rclab Laboratory - Bologna                       */
/*                       Andrea Cipriani, Ottobre 2001                     */
/*                        E-Mail :  a71cip@tiscali.it                      */
/*             Libreria di funzioni per la gestione della SPP              */
/*                          Standard Parallel Port                         */
/*         --------------------------------------------------------        */
/*                                                                         */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*    Attenzione, l'hardware delle porte STATUS e CONTROL inverte alcuni   */
/*  bit. Per gestire effettivamente il byte nella maniera corretta occorre */
/*  mettere il byte letto sulla porta STATUS in xor (^) con 0x80 e il byte */
/*  scritto sula porta CONTROL in xor con 0x0b.                            */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/***************************************************************************/

# include <stdio.h>
# include <conio.h>
# include <dos.h>
# include <binext.h>

# define  NUM_LPT 1        //Numero massimo porte parallele presenti
# define _CNT 0x0b
# define _STATUS 0x80

// Prototipi delle funzioni
unsigned int *trova_lpt (void);
char status_read        (unsigned int);
char data_read          (unsigned int);
char control_read       (unsigned int);
void data_write         (unsigned int, char);
void control_write      (unsigned int, char);
void data_set_bit       (unsigned int, char);
void data_clear_bit     (unsigned int, char);
void control_set_bit    (unsigned int, char);
void control_clear_bit  (unsigned int, char);
/*char pick_status_bit  (unsigned int, char);*/
char pick_data_bit      (unsigned int, char);
/*char pick_control_bit (unsigned int, char);*/
char board_read         (unsigned int, unsigned int, unsigned int, unsigned int);
void adc_ch_init		(unsigned int);
char adc_ch_read		(unsigned int, char); 		

// Restituisce puntatore ad array che contiene indirizzi
// delle porte parallele da 0 fino a NUM_LPT.
unsigned int *trova_lpt(void)
{
 // Modifica la durata dell'array in questione fino al main
 // altrimenti rimarrebbe utile solo il primo elemento dell'array
 // cioŠ quello relativo all'indirizzo n
 static unsigned int n[NUM_LPT];
 int i;

 for (i=0; i<NUM_LPT; i++)
  {
   n[i]=*(unsigned far*)(MK_FP(0x40,0x008+(i*2)));
  }
 return n;
}


// Legge un byte dalla porta STATUS
char status_read (unsigned int add)
{
 return((inportb(add)^_STATUS));
}

// Legge il valore della porta data
char data_read (unsigned int add)
{
 return (inportb(add));
}

// Legge il valore della porta control
char control_read (unsigned int add)
{
 return ((inportb(add)^_CNT));
}

//Scrive il value sulla porta dati
void data_write (unsigned int add, char value)
{
 outportb(add,value);
}

//Scrive il value sulla porta control
void control_write (unsigned int add, char value)
{
 outportb(add,(value^_CNT));
}

// Setta il bit in posizione position sulla data port
void data_set_bit (unsigned int add, char position)
{
 char last;
 last = data_read(add);
 switch (position)
  {
   case 0:
    data_write(add,(last|0x01));
    break;

   case 1:
    data_write(add,(last|0x02));
    break;

   case 2:
    data_write(add,(last|0x04));
    break;

   case 3:
    data_write(add,(last|0x08));
    break;

   case 4:
    data_write(add,(last|0x10));
    break;

   case 5:
    data_write(add,(last|0x20));
    break;

   case 6:
    data_write(add,(last|0x40));
    break;

   case 7:
    data_write(add,(last|0x80));
    break;
  }
}

// Resetta il bit in posizione position sulla data port
void data_clear_bit (unsigned int add, char position)
{
 char last;
 last = data_read(add);
 switch (position)
  {
   case 0:
    data_write(add,(last&0xfe));
    break;

   case 1:
    data_write(add,(last&0xfd));
    break;

   case 2:
    data_write(add,(last&0xfb));
    break;

   case 3:
    data_write(add,(last&0xf7));
    break;

   case 4:
    data_write(add,(last&0xef));
    break;

   case 5:
    data_write(add,(last&0xdf));
    break;

   case 6:
    data_write(add,(last&0xbf));
    break;

   case 7:
    data_write(add,(last&0x7f));
    break;
  }
}

// Setta il bit in posizione position sulla data port
void control_set_bit (unsigned int add, char position)
{
 char last;
 last = control_read(add);
 switch (position)
  {
   case 0:
    control_write(add,(last|0x01));
    break;

   case 1:
    control_write(add,(last|0x02));
    break;

   case 2:
    control_write(add,(last|0x04));
    break;

   case 3:
    control_write(add,(last|0x08));
    break;
  }
}

// Resetta il bit in posizione position sulla control port
void control_clear_bit (unsigned int add, char position)
{
 char last;
 last = control_read(add);
 switch (position)
  {
   case 0:
    control_write(add,(last&0xfe));
    break;

   case 1:
    control_write(add,(last&0xfd));
    break;

   case 2:
    control_write(add,(last&0xfb));
    break;

   case 3:
    control_write(add,(last&0xf7));
    break;
  }
}

// Restituisce il bit di posizione position sulla porta status
char pick_data_bit (unsigned int add, char position)
{
 return(((data_read(add)>>position)&0x01));
}

// Legge un byte sulla scheda parallela board1
char board_read (unsigned int add, unsigned int dadd, unsigned int sadd, unsigned int cadd)
{
 char overflow,read,low_nibble,high_nibble;

 control_write(cadd,add);      // STROBE a 0, LS NIBBLE
 add = add+1;
 low_nibble = status_read(sadd); // S7=V,S6=X-ACK,S5=V,S4=V,S3=V,S2=X,S1=X,S0=X
 low_nibble = low_nibble & 0xbf;   // 0xbf=10111111 S7=V,S6(0),S5=V,S4=V,S3=V,S2=X,S1=X,S0=X

 if (low_nibble & 0x80)            // Verifica stato di S7 e lo salva in overflow
  {
   overflow = 0x80;
  }
  else
  {
   overflow = 0x00;
  }

 low_nibble = low_nibble<<1;      // S7=OVERFLOW,S6=V,S5=V,S4=V,S3=X,S2=X,S1=X,S0=X
 low_nibble = low_nibble & 0x7f;  // 0x07=01111111 S7=V,S6=V,S5=V,S4=V,S3=S2=S1=S0=X
 low_nibble = low_nibble | overflow;

 low_nibble = low_nibble>>4;      // S7=S6=S5=S4=X,S3=S2=S1=S0=V
 low_nibble = low_nibble & 0x0f;  // S7=S6=S5=S4=0,S3=S2=S1=S0=V

 control_write(cadd,add);     // STROBE=1, MS NIBBLE
 high_nibble = status_read(sadd);
 high_nibble = high_nibble & 0xbf;

 if (high_nibble & 0x80)
  {
   overflow = 0x80;
  }
  else
  {
   overflow = 0x00;
  }
 high_nibble = high_nibble<<1;
 high_nibble = high_nibble & 0x7f;

 high_nibble = high_nibble | overflow;
 high_nibble = high_nibble & 0xf0;
 read = high_nibble | low_nibble;     // DATA AVAILABLE
 return(read);
}

//Inizializza per la scheda adc 8 channel
//Passare l'indirizzo del Data Port come parametro datadd
void adc_ch_init		(unsigned int datadd)
{
 unsigned int data_address     =datadd;
 unsigned int status_address  =datadd+1;
 unsigned int control_address =datadd+2;

 //Channel address = 000
 control_clear_bit (control_address,1);
 control_clear_bit (control_address,2);
 control_clear_bit (control_address,3);

 //Write = 0
 data_clear_bit (data_address,0);

 //Read = 0
 data_clear_bit (data_address,1);
}


//Converte il canale prescelto sulla scheda adc 8 channel e restituisce una char
//Passare l'indirizzo del Data Port come parametro datadd
char adc_ch_read	(unsigned int datadd, char ch)
{
 unsigned int data_address     =datadd;
 unsigned int status_address   =datadd+1;
 unsigned int control_address  =datadd+2;
 char channel=ch;
 char last_cnt = control_read(control_address);
 unsigned char low_nibble,high_nibble,overflow,read;
 
 //Imposta il canale prescelto sulle apposite linee di decodifica
 channel = channel << 1;
 channel = channel & 0xfe;
 control_write(control_address,channel);
 delay(1);

 //Start of conversion : Write
 data_set_bit (data_address,0);
 delay(1);
 data_clear_bit (data_address,0);

 //Lettura del valore convertito
 
 // Output enable
 data_set_bit (data_address,1);
 

 //Strobe a 0 : read lower nibble
 control_clear_bit (control_address,0);
 low_nibble = inportb(status_address);
 low_nibble = low_nibble ^ 0x80;

 low_nibble = low_nibble & 0xbf;

 if (low_nibble & 0x80)
  {
   overflow = 0x80;
  }
  else
  {
   overflow = 0x00;
  }

 low_nibble = low_nibble<<1;

 low_nibble = low_nibble & 0x7f;

 low_nibble = low_nibble | overflow;

 low_nibble = low_nibble>>4;
 low_nibble = low_nibble & 0x0f;

 //Strobe a 1 : read upper nibble
 control_set_bit (control_address,0);
 high_nibble = inportb(status_address);
 high_nibble = high_nibble ^ 0x80;
 high_nibble = high_nibble & 0xbf;

 if (high_nibble & 0x80)
  {
   overflow = 0x80;
  }
  else
  {
   overflow = 0x00;
  }
 high_nibble = high_nibble<<1;
 high_nibble = high_nibble & 0x7f;

 high_nibble = high_nibble | overflow;
 high_nibble = high_nibble & 0xf0;

 read = high_nibble | low_nibble;

 //Output disable
 data_clear_bit (data_address,1);

 //Control port resume
 control_write(control_address,last_cnt);
 return(read);
 

}