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

#define COM1 0
#define COM2 1
#define COM3 2
#define COM4 3

#define COM1BASE 0x3F8	/* 0011 1111 1000 */
#define COM2BASE 0x2F8	/* 0010 1111 1000 */
#define COM3BASE 0x3E8	/* 0011 1110 1000 */
#define COM4BASE 0x2E8	/* 0010 1110 1000 */

#define COM13_INTERRUPT 0x0C
#define COM24_INTERRUPT 0x0B

#define MASKON13 0xEF	/* 1110 1111 */
#define MASKON24 0xF7 	/* 1111 0111 */
#define MASKOFF13 0x10	/* 1000 0000 */
#define MASKOFF24 0x08	/* 0000 1000 */

#define DATABIT7 0x02		/* 0000 0010 */
#define DATABIT8 0x03		/* 0000 0011 */

#define STOPBIT1 0x00		/* 0000 0000 */
#define STOPBIT2 0x04		/* 0000 0100 */

#define PARITYNONE 0x00	/* 0000 0000 */
#define PARITYODD 0x08	/* 0000 1000 */
#define PARITYEVEN 0x18	/* 0001 1000 */

#define BPS1200 1200L
#define BPS2400 2400L
#define BPS4800 4800L
#define BPS9600 9600L
#define BPS19200 19200L
#define BPS38400 38400L
#define BPS57600 57600L
#define BPS115200 115200L

#define LCR_OFFSET 3   /*line control offset*/
#define MCR_OFFSET 4
#define IER_OFFSET 1
#define LSR_OFFSET 5
#define MSR_OFFSET 6

#define PICMASK 0x21
#define PICEOI 0x20

#define BUFFER_LENGTH 10000

#define OK 1
#define ERROR 0

#define ALT_X 0x2D
#define ALT_S 0x1F
int first, last;
char *buffer;
int comport_base = COM1BASE;
int comport=1, baudrate=4, connectstate=0;

int base_addr; /* 직렬 포트 기본 주소를 위한 전역 변수 */
int port_int_no; /* 직렬 포트 인터럽트 벡터 번호 */
int imr_on; /* 인터럽트 마스크 레지스터를 바꾸기 위한 값 */
int imr_off;

void interrupt(*old_intr)(); /* 예전 interrupt vector 를 저장해주는 변수 선언..interrupt 형식이라 이런거야.*/
void interrupt port_interrupt(void);

/*********FUNCTION PROTOTYPES***********/

void InitSerialPort( int port, unsigned char databit, unsigned  char stopbit, unsigned char parity, long bps);
void InitModem(void);
void CloseModem(void);
void SendCharacter(unsigned char ch);
void SendString(unsigned char *str);
void InitChannel(void);

void InitValue(void);
void ConnectInitial(void);
void InitSetting(void);

void Background(void);
void Thankx(void);

void Delay(int time);

void Delay(int time){
	int i,j;
	for(i=0;i<time;i++){
		for(j=0;j<10000;j++){
		printf("");
		}
	}
}

/**********************************************************
 *	void  InitSerialPort(int port, unsigned char databit, unsigned char stopbit,
 *					unsigned char parity, long bps)
 *    -직렬포트를 초기화 시키는 함수
 ***********************************************************/

void InitSerialPort( int port, unsigned char databit, unsigned  char stopbit, unsigned char parity, long bps)
{
	char ch, old_lcr;
	unsigned int divisor;
	unsigned char high, low;

	/*받은 port값에 따라서, 여러 extern value들을 setting.*/
	switch(port)
	{
		case COM1 :
		base_addr = COM1BASE;
		port_int_no = COM13_INTERRUPT;
		imr_on = MASKON13;
		imr_off = MASKOFF13;
		break;
		case COM2 :
		base_addr = COM2BASE;
		port_int_no = COM24_INTERRUPT;
		imr_on = MASKON24;
		imr_off = MASKOFF24;
		break;
		case COM3 :
		base_addr = COM3BASE;
		port_int_no = COM13_INTERRUPT;
		imr_on = MASKON13;
		imr_off = MASKOFF13;
		break;
		case COM4 :
		base_addr = COM4BASE;
		port_int_no = COM24_INTERRUPT;
		imr_on = MASKON24;
		imr_off = MASKOFF24;
		break;

	}

	/*라인제어 레지스터  건드리기*/
	ch=databit | stopbit | parity;  /*라인제어 레지스터의 값 셋팅..^^ ...or 연산으로*/
	outportb(base_addr + LCR_OFFSET, ch);

	/***********모뎀 속도 setting  시작 ***********/
	divisor = (unsigned int)(1843200L / (bps * 16L));
	high = (unsigned char) (divisor >> 8);
	low = (unsigned char) divisor;

	/* 라인 제어 레지스터의 비트 1번 DLAB(divisor latch access bit)를 1롤 셋팅 (0x80 = 10000000b) */
	old_lcr = inportb(base_addr + LCR_OFFSET);
	outportb(base_addr + LCR_OFFSET, old_lcr | 0x80);
	/* DLL과 DLH에 모뎀 속도 값 보내기 */
	/*위에서 LCR 에 0x80 을 보내서, 두 레지스터 주소에 다른 buffer 주소로 임시 사용..*/
	/*모뎀의 속도에 관한 것들*/
	outportb(base_addr, low);
	outportb(base_addr + 1, high);

	/* 라인 제어 레지스터의 비트 1번 DLAB를 다시 0으로 셋팅 (0x7F = 01111111b) */
	old_lcr = inportb(base_addr + LCR_OFFSET);
	outportb(base_addr + LCR_OFFSET, old_lcr & 0x7F);  /* 0x7F = 0111 1111 이걸 & 해주면 0부분만 0 값으로 강제 setting  */
	/**********모뎀속도 제어 끝 **************/
}

/* interrupt 발생시에, 넘겨받아서 처리하는 routine..*/
void interrupt port_interrupt(void)
{
	unsigned char ch;

	/* last = 가장 최근에 들어온 데이터를 가리키는 변수 */
	if(last > (BUFFER_LENGTH-1))
		last = 0;

	/* 포트에서 데이터를 읽어와 저장 */
	ch = inportb(base_addr);
	if(ch)
		buffer[last++] = ch;

	/* PIC의 EOI 인터럽트에 인터럽트가 끝났다는 신호 보내기 */
	outportb(PICEOI, 0x20);
}


/* 모뎀을 초기화 하는 함수 */
void InitModem(void)
{
	unsigned char old_msr, old_imr;

	/* 포트에서 발생하는 인터럽트 재정의 */
	old_intr = getvect(port_int_no);
	setvect(port_int_no, port_interrupt);

	/* 모뎀 제어 레지스터 셋팅 (0x0B = 00001011b) */
	outportb(base_addr + MCR_OFFSET, 0x0B);

	/* 인터럽트 허용 레지스터 셋팅 (0x01 = 00000001b) */
	/* data를 받았을때 허용. */
	outportb(base_addr + IER_OFFSET, 0x01);

	/* 버퍼 정보 셋팅 */
	first = last = 0;	/* extern value. */

	/* 모뎀 상태 레지스터 셋팅 (0x30 = 00110000b) */
	old_msr = inportb(base_addr + MSR_OFFSET);
	outportb(base_addr + MSR_OFFSET, old_msr | 0x30);

	/* 인터럽트 마스크 레지스터 셋팅 */
	old_imr = inportb(PICMASK);
	outportb(PICMASK, old_imr & imr_on);

	/* 라인 청소 */
	Delay(200);	/*200 milisecound 동안 */
	inportb(base_addr);
	inportb(base_addr);
}

/* 모뎀을 원상태로 복구하는 함수 */
void CloseModem(void)
{
	unsigned char old_imr;

	/* 인터럽트 허용 레지스터 셋팅 */
	outportb(base_addr + IER_OFFSET, 0x00);

	/* 인터럽트 마스크 레지스터 셋팅 */
	old_imr = inportb(PICMASK);
	outportb(PICMASK, old_imr | imr_off);

	/* 인터럽트 핸들러 회복 */
	setvect(port_int_no, old_intr);

	/* 버퍼 메모리 해제 */
	free(buffer);

}

/* UART의 전송대기 레지스터가 비어있으면(전송 이 끝났으면), uart 전송대기 레지스터에 char 집어넣기. */
void SendCharacter(unsigned char ch)
{
	char lsr;
    do {
		/* UART의 전송 대기 레지스터가 비워질 때까지 대기하고 있는 routine */
		lsr = inport(base_addr + LSR_OFFSET) & 0x20;  /* 0010 0000 */
	} while(lsr == 0);

	/* 문자 전송 */
	outportb(base_addr, ch);
}

/* 위에 SendCharacter를 이용해서, SendString을 ... 구현.. */
void SendString(unsigned char *str)
{
	while(*str)	/* 멘 마지막에 NULL 이 들어오면 False이므로 while loop 를 빠져나온다.*/
		SendCharacter(*str++);
}

void InitChannel(void){
	SendCharacter('a');
	SendCharacter('t');
	SendCharacter('\r');
	SendCharacter('\n');
}

void InitValue(void){
	int i;
	first = last = 0;

	if((buffer = malloc(sizeof(char)))==NULL){
		printf("\r\nCannot Allocate Memory\r\n");
		exit(0);
	}

	for(i=0;i< BUFFER_LENGTH;i++){
		buffer[i] = 0;
	}
}

void Background(void){
	clrscr();
	printf("*************************************************************************\r\n");
	printf("**** RS-232C Terminal Test Program **************************************\r\n");
	printf("**** Ver 1.0 ************************************************************\r\n");
	printf("**** E-mail : davidryu1@yahoo.co.kr *************************************\r\n");
	printf("**** Welcome !! *********************************************************\r\n");
	printf("**** Setting : ALT_S      Quit : ALT_X **********************************\r\n");
	printf("*************************************************************************\r\n");
}

void Thankx(void){
	printf("\r\n\r\n******************************************\r\n");
	printf("**** RS-232C Terminal Test Program *******\r\n");
	printf("**** Ver 1.0 *****************************\r\n");
	printf("**** E-mail : davidryu1@yahoo.co.kr ******\r\n");
	printf("**** Thanks for using ! ******************\r\n");
	printf("**** July 10, 2005 ***********************\r\n");
	printf("******************************************\r\n\r\n");
}

void ConnectInitial(void){
	switch(baudrate){
		case 1: InitSerialPort(comport-1, DATABIT8, STOPBIT1, PARITYNONE, BPS1200);
				connectstate=0;
				break;
		case 2: InitSerialPort(comport-1, DATABIT8, STOPBIT1, PARITYNONE, BPS2400);
				connectstate=0;
				break;
		case 3: InitSerialPort(comport-1, DATABIT8, STOPBIT1, PARITYNONE, BPS4800);
				connectstate=0;
				break;
		case 4: InitSerialPort(comport-1, DATABIT8, STOPBIT1, PARITYNONE, BPS9600);
                connectstate=0;
				break;
		case 5: InitSerialPort(comport-1, DATABIT8, STOPBIT1, PARITYNONE, BPS19200);
                connectstate=0;
				break;
		default :
				connectstate=1;
				break;
		}

	if(connectstate == 0){
		printf("*************************************************************************\r\n");
		switch(baudrate){
			case 1 : printf("**** Connected with COM%d  1200 bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport);
					 break;
			case 2 : printf("**** Connected with COM%d  2400 bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport);
					 break;
			case 3 : printf("**** Connected with COM%d  4800 bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport);
					 break;
			case 4 : printf("**** Connected with COM%d  9600 bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport);
					 break;
			case 5 : printf("**** Connected with COM%d  19200 bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport);
					 break;
			default : break;
		}

		printf("*************************************************************************\r\n");
	}else{
		printf("*************************************************************************\r\n");
		printf("**** Cannot Open Port with %d! ******************************************\r\n", baudrate*1200);
		printf("*************************************************************************\r\n");
	}
}

void InitSetting(void){
	clrscr();
	printf("\r\n\r\n***********************************************\r\n");
	printf("**** Serial Setting ***************************\r\n");
	printf("***********************************************\r\n");

	printf("\r\n***********************************************\r\n");
	printf("**** COM Port *********************************\r\n");
	printf("**** Select Port (1-4) ************************\r\n");
	printf("**** 1. COM1   2. COM2   3. COM3   4. COM4 ****\r\n");
	printf("**** : ");
	scanf("%d",&comport);
	switch(comport){
		case 1 : comport_base = COM1BASE;
				 break;
		case 2 : comport_base = COM2BASE;
				 break;
		case 3 : comport_base = COM3BASE;
				 break;
		case 4 : comport_base = COM4BASE;
				 break;
		default : break;
	}
	printf("\r\n\r\n***********************************************\r\n");
	printf("**** Baudrate Testing ************************************\r\n");
	printf("**** Select Port (1-5) ***********************************\r\n");
	printf("**** 1. 1200   2. 2400   3. 4800   4. 9600   5. 19200 ****\r\n");
    printf("**** : ");
	scanf("%d",&baudrate);
	printf("\r\n\r\n*********************************************************\r\n");
	printf("*******************************************************\r\n");
	printf("**** COM%d  %d bps  Data:8bit  Parity:None Stop:1bit ****\r\n",comport,1200*baudrate);
}

void main(void)
{
	char ch, EXIT = 1;

	InitValue();
	InitModem();

	Background();
	ConnectInitial();

	do{
		if(kbhit()){
			ch = getche();
			switch(ch){
				case ALT_X : EXIT=0;
							 break;
				case ALT_S : InitSetting();
							 Background();
							 ConnectInitial();
							 break;
				default : outportb(comport_base,ch);
						  break;
			}
		}
		if(first != last){
			printf("%c",buffer[first++]);
			if(first >= (BUFFER_LENGTH-1)){
				first = 0;
			}
		}
	}while(EXIT);

	CloseModem();
	Thankx();
}
