/* ----------------------------------------------------------------------
Modulo per la gestione della porta seriale

   URL 
     www.pklab.net/kbase/infind/seriale
   AUTORE
     PK-Lab Cyber Solutions
     email pk@pklab.net
     AGGIORNAMENTO: 22.03.97

  02.06.2001
     corretto errore nella memorizzazione della dimensione del buffer
     allocato dinamicamente in TerminalON.

----------------------------------------------------------------------*/
 
#include <dos.h>
#include <alloc.h>
#include "TERMINAL.H"


/*--------------- INDIRIZZI DELLE PORTE E DEI REGISTRI -------------------
 gli indirizzi di base sono da sommare agli indirizzi dei registri in modo
 da ottenere che THR di com1 Š 0x300 + 0xf8 = 03f8
--------------------------------------------------------------------------*/


#define THR     0xF8    /* Trasmitter Olding Register */
#define RDR     0xF8    /* Receiver Data Register     */
#define BRDL    0xf8    /* Baud Rate divisor (LSB)    */
#define BRDH    0xf9    /* Baud Rate Divisor (MSB)    */
#define IER     0xf9    /* Interrupt Enable Register  */
#define IIR     0xfa    /* Interrupt ID register      */
#define LCR     0xfb    /* Line Control Register      */
#define MCR     0xfc    /* Modem Control Register     */
#define LSR     0xfd    /* Line Status Register       */
#define MSR     0xfe    /* Modem Status Register      */



/*-------------------BUFFER DI LETTURA E SCRITTURA ----------------------*/
BYTE *buffer;                  /* buffer circolare */
int  indice_in  = 0;           /* indice al primo posto libero */
int  indice_out = 0;           /* indice al byte dal lebbere   */
int  LUNGBUFFER=0;             /* dimensione del buffer circolare */

/*-------------------VARIABILI DI STATO----------------------------------*/
int numSeriale=COM1;
BOOL stato = OFF;                /* stato di terminal */

/*-------------------FUNZIONI--------------------------------------------*/
void ComOFF(void);
void ComON(void);
void interrupt (*oldintr)(void);        /* vecchio interrupt 0xC */
void interrupt ReadRS232(void);         /* nuovo   interrupt 0xC */

/**************************************************************************
		TerminalON                                        Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Installa l'interrupr vector per la lettura automatica
	dalla porta seriale.
--------------------------------------------------------------------------*/
ERRORMSG TerminalON(int port,int baud,int parity,int stop,int wordlength,int buflen)
{
  if(NULL== (buffer = (BYTE * ) malloc(buflen)))
    return NOMEM;

        LUNGBUFFER=buflen;          /* memorizzo la dimensione del buffer allocato */
	stato = ON;                 /* stato del sistema TERMINAL */
	indice_in  = 0;             /* resetto il buffer circolare */
	indice_out = 0;
	numSeriale = port;          /* quale seriale COM1 COM2 */
	if(numSeriale == COM1)
	{
		oldintr = getvect(0xC);
		setvect(0xC,ReadRS232);
	} else if(numSeriale == COM2)
	{
		oldintr = getvect(0xB);
		setvect(0xB,ReadRS232);
	}
	SetPort( baud, parity, stop, wordlength);
	return NOERROR;
}

/**************************************************************************
		TerminalOFF                                      Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Spegne la seriale,disinstalla l'interrupr vector per la
 lettura automatica dalla porta seriale,remimpostando il precedente.
--------------------------------------------------------------------------*/
void TerminalOFF(void)
{
	stato = OFF;
	ComOFF();
	if(numSeriale == COM1)      setvect(0xC,oldintr);
	else if(numSeriale == COM2) setvect(0xB,oldintr);
  free(buffer);
  LUNGBUFFER = 0;

}

/**************************************************************************
		Trasmetti                                         Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Trasmette un byte sulla porta seriale selezionata con
	TerminalON. Se la trasmissione fallisce torna FALSE, altrimenti TRUE
--------------------------------------------------------------------------*/
BOOL Trasmetti(BYTE ch)
{
	int timeout = 1500;
	int porta;
	BYTE byte;

	for(;timeout>0;timeout--)
	{
		porta = numSeriale + LSR;
		byte = inp(porta);

		/*
			il bit 5 (xx1x xxxx = 0x20) del registro LSR se impostato indica che
			il registro di trasmissione Š vuoto, quindi si puo trasmettere.
		*/
		if(byte & 0x20)                     /* se posso trasmettere */
		{
			porta = numSeriale + THR;
			outp(porta,ch);                   /* trasmetto */
			return TRUE;
		}
	}
	return FALSE;                         /* timeout */
}


/**************************************************************************
		LeggiDatoDaBuffer                                 Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Legge dal buffer circolare il primo dato disponibile. Se
	nessun dato e' disponibile allora ritorna FALSE
--------------------------------------------------------------------------*/
BYTE LeggiDatoDaBuffer(void)
{
	BYTE ret;

	if(indice_in==indice_out) return FALSE;

	ret = buffer[indice_out++];
	if(indice_out == LUNGBUFFER) indice_out = 0;
	return ret;
}

/**************************************************************************
		SetPort                                        Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE:
--------------------------------------------------------------------------*/
void SetPort(int baud,int parity,int stop,int wordlength)
{
	union REGS regs;

	if(stato != ON) return;

	ComOFF();                                 /* spengo la linea seriale */

	regs.h.ah = 0;                            /* servizio richiesto */
	regs.h.al = baud+parity+stop+wordlength;  /* byte di protocollo */

	/* numero porta */
	if     (numSeriale == COM1)   regs.x.dx = 0;
	else if(numSeriale == COM2)   regs.x.dx = 1;
	int86(0x14,&regs,&regs);

	ComON();             /* riaccendo la seriale */

	return;
}

/**************************************************************************
		ComOff                                         Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Disabilita la comunicazione impostando i bit per IRQ3 e 4
--------------------------------------------------------------------------*/
void ComOFF(void)
{
	BYTE byte;

	byte = inp(0x21);          /* registro dell'8259 */
	byte = (byte | 0x18);      /* 0x18 = 0001 1000  set IRQ3 e IRQ4 */
	outp(0x21,byte);

}

/**************************************************************************
		ComON                                         Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Imposta le linee seriali per attivare un interrupt per
	ogni dato ricevuto.
--------------------------------------------------------------------------*/
void ComON(void)
{
	int  porta,byte;

	disable();      /* disabilito la linea INTR (tutti gli IRQ) */

	/*
		Inizializzo MCR per data terminal ready (bit 0), request to send
		(bit 1) e output 2 (bit 3)
	*/
	porta = numSeriale + MCR;
	byte  = 0xB;                  /* 0000 1011 ->imposto i bit 0 1 3 */
	outp(porta,byte);

	/*
		ora imposto il bit 7 del registro LCR-DLAB per accedere all'abilitazione
		dell'interrupt nel IER
	*/
	porta = numSeriale + LCR;    /* line control register */
	byte = inp(porta);
	byte = (byte & 0x7F);           /* 0111 1111 ->reset DLAB */
	outp(porta,byte);

	/*
		ora abilito interrupt solo per DATA READY
	*/
	porta = numSeriale + IER;       /* interrupt enable register */
	byte  = 1;
	outp(porta,byte);

	/*
		ora abilito la comunicazione dell'interrupt resettando le linee
		IRQ3 IRQ4 nel regsitro degli interrupt dell 8259, situato all'indirizzo
		21h
	*/
	byte = inp(0x21);
	byte = (byte & 0xE7);           /* 1110 0111 ->reset bit 3 e 4 */
	outp(0x21,byte);

	enable();             /* abilito la linea INTR */
}



/**************************************************************************
		ReadRS232                                      Pino Contini 27/03/95
---------------------------------------------------------------------------
DESCRIZIONE: Funzione interrupt che viene chiamata direttamete dalla
	presenza di un segnale sulla porta seriale. Questo interrupt legge il
	contenuto del byte e lo memorizza nell'array buffer, che viene gestito come
	un buffer circolare
--------------------------------------------------------------------------*/
void interrupt ReadRS232(void)
{
	BYTE byte;
	int porta, ok = FALSE;

	enable();

	while(ok == FALSE)
	{
		porta = numSeriale + LSR;  /* line status register  */
		byte  = inp(porta);        /* leggo un byte dal LSR */

		/*
			se il bit 0 del LSR vale 1 allora un dato Š pronto nel
			RDR (read data register). Questo bit viene automaticamente
			resettato quando si fa una lettura all'indirizzo COMx + 0xF8
		*/

		if((byte & 1) > 0)         /* se dato pronto */
		{
			porta = numSeriale + RDR;    /* read data register */
			byte = inp(porta);           /* leggo il carattere dalla porta */
			buffer[indice_in++]= byte;   /* memorizzo il carattere */
			if(indice_in == LUNGBUFFER) indice_in = 0;
			ok = TRUE;
		} else                    /* dato non pronto */
		/*
			i bit 1 2 3 4 del LSR se impostati indicano delle condizioni
			di errore. pertanto se facendo (0x1E) 0001 1110  AND LSR, qualche
			bit Š ancora impostato (il risultato Š maggiorn di 0) allora
			Š avvenuto qualche  errore.
		*/
		if((byte & 0x1E) > 0)     /* se dato non pronto ed errore */
		{
			buffer[indice_in++]= '?';   /* memorizzo il carattere */
			if(indice_in == LUNGBUFFER) indice_in = 0;
			ok = TRUE;
		}
	}
	outp(0x20,0x20);   /* end of interrupt all'8259 */
}
