/*****************************************************
test.c : 
  μITRONトレーナcmtoy サンプルプログラム
  Copyright(C) 2002 - 2019 佐々木芳
  Revised at 2023,2,28

タスクの実装

Windowsのシミュレーション環境上で動作するアプリケーションプログラムのコンパイル時には以下のマクロ定義が必要
	_CMTOY
*****************************************************/
#ifdef _CMTOY
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "hal.h"
#include "hal_uart.h"
#include "itron.h"
#include "debug.h"
#include "test.h"

//改行コード
#define	NEWLINE			"\r"		/* 端末による　\n or \r or \r\n */


void DisplayContext(void);


/*トレース*/
TRACE_BUFFER	IntrTrace;
void InitTrace()
{
	IntrTrace.num_slots = NUM_OF_TRACE_SLOTS;
	IntrTrace.next = 0;
}
void IncrTrace()
{
	++ IntrTrace.next;
	if(IntrTrace.next == IntrTrace.num_slots){
		IntrTrace.next = 0;
	}
}
TRACE_SLOT* GetTraceSlot()
{
	memset(&IntrTrace.slot[IntrTrace.next], 0, sizeof(IntrTrace.slot[IntrTrace.next]));
	return &IntrTrace.slot[IntrTrace.next];
}



/*****************************************************
//シリアルチャネル１
//割込み４を使用
*****************************************************/
int SendData(char* s);
void LedDisplayModemStatus(BYTE ms);

int ch16550 = 1;
int SizeOfFIFO;
/***************************************************************************
16550 初期化
***************************************************************************/
/*割込み使用	*/
/*BAUD RATES DIVISOR値 9.216 MHz CRYSTAL*/
#define BAUDRATES_115200	(5)

void Init16550(int ch, BOOL fifo)
{
	WORD temp;
	DECLARE_CRISEC;
	ENTER_CRISEC;
	WRITE_LCR(ch,0x80);					/*;DLAB=1*/
	WRITE16550(ch,BAUDRATES_115200);	/*;DLAB=1: Diviser Latch(LS)*/
	WRITE_IER(ch,BAUDRATES_115200>>8);	/*;DLAB=1: Diviser Latch(MS)*/
	WRITE_LCR(ch,0x03);					/*;DLAB=0: DATA=8, PARITY=NONE, STOPBIT=1*/
	temp = halGetSwitch();
	if(temp & 0x08){
		//SW3がONならループバックとする
		DebugPrintf("Loopback mode." NEWLINE);
		WRITE_MCR(ch,0x13);				/*;LOOPBACK=ON, DTR=ON, RTS=ON*/
	}else{
		WRITE_MCR(ch,0x03);				/*;DTR=ON, RTS=ON*/
	}
	WRITE_IER(ch,0x0D);					/*;ENABLE RX READY,LINE STATUS,MODEM STATUS*/
//	WRITE_IER(ch,0x00);					/*;DISABLE RX READY,LINE STATUS,MODEM STATUS*/

	if(fifo){
		//FIFO使用
		WRITE_FCR(ch,0xc7);				/*;FIFO enable,FIFO clear, 14-bytes trigger*/
		SizeOfFIFO = 16;
	}else{
		WRITE_FCR(ch,0x00);				/*;FIFO disable*/
		SizeOfFIFO = 1;
	}
	LSTAT16550(ch);						/*read Line Status*/
	temp = MSTAT16550(ch);
	LedDisplayModemStatus((BYTE)temp);
	ena_int(4);									/*レベル４の割り込みを許可*/
	LEAVE_CRISEC;
}

BOOL bSending;	//送信中フラグ
int nTx;		//残り送信データバイト数
char* pTx;		//送信データへのポインタ
char TxBuf[64];
char TxData1[] = "abc" NEWLINE;					//送信データ（ 3+1バイト）
char TxData2[] = "12345678901234567890" NEWLINE;//送信データ（20+1バイト）

int	WriteCount = 0;

void Task16550(VP_INT exinf)
{
	ER ercd;
	WORD sw = halGetSwitch();
	bSending = FALSE;
	/*DisplayContext();*/
	/*デバグコンソールの初期化*/
	DebugInitConsole(DP_CON0);
	DebugPrintf(NEWLINE "Debug console initialized." NEWLINE);
	InitTrace();
	/*１６５５０の初期化*/
	CMTRACE("app: Task16550 started.\n");
	DebugPrintf(NEWLINE "Initialize serial channel 1(16550)." NEWLINE);
//	Init16550(ch16550, FALSE);	//16450
	Init16550(ch16550, TRUE);	//16550
	while(1){
		WORD temp;
		int result;
		ercd = tslp_tsk(100);
		temp = halGetSwitch();
		if((temp ^ sw) & 0x01){
			//SW0の状態が変わった
			//DebugPrintf("sw0 = %s" NEWLINE, temp & 0x01 ? "on" : "off");
			if(nTx == 0){
				/*送信開始*/
				if(temp & 0x01){
					//SW0:OFF -> ON
					result = SendData(TxData1);
				}else{
					//SW0:ON -> OFF
					result = SendData(TxData2);
				}
			}else{
				DebugPrintf("In sending %d" NEWLINE, nTx);
			}
		}
		if((temp ^ sw) & 0x02){
			//SW1の状態が変わった
			DebugPrintf("OUT1 changed %d" NEWLINE, (temp & 0x02) != 0);
			if(temp & 0x02){
				//0->1
				SET_OUT116550(ch16550);
			}else{
				//1->0
				CLEAR_OUT116550(ch16550);
			}
		}
		if((temp ^ sw) & 0x04){
			//SW2の状態が変わった
			DebugPrintf("OUT2 changed %d" NEWLINE, (temp & 0x04) != 0);
			if(temp & 0x04){
				//0->1
				SET_OUT216550(ch16550);
			}else{
				//1->0
				CLEAR_OUT216550(ch16550);
			}
		}
		if((temp ^ sw) & 0x08){
			//SW3の状態が変わった
			if(temp & 0x08){
				//SW3がONならループバックとする
				DebugPrintf("Loopback mode." NEWLINE);
				SET_LOOPBACK16550(ch16550);				/*;LOOPBACK=ON, DTR=ON, RTS=ON*/
			}else{
				DebugPrintf("Normal mode." NEWLINE);
				CLEAR_LOOPBACK16550(ch16550);			/*;DTR=ON, RTS=ON*/
			}
		}
		sw = temp;
	}
}

int SendData(char* s)
{
	int result = 0;
	DECLARE_CRISEC;
	ENTER_CRISEC;
	if(nTx > 0){
		//送信中
		result = -1;
		CMTRACE("in sending %d\n", nTx); 
	}else{
		//送信バッファにコピーして送信開始
		strcpy(TxBuf, s);
		pTx = TxBuf;
		nTx = strlen(s);//送信文字数
		bSending = TRUE;
		ENA_TX16550(ch16550);
		CMTRACE("send %d bytes\n", nTx); 

	}
	LEAVE_CRISEC;
	return result;
}


/*****************************************************
１６５５０割込みハンドラ
*****************************************************/
int ReadFIFO(int ch, BYTE buf[], BYTE le[]);
void PrintLineError(BYTE stat);
void LineErrorTag(BYTE stat, char tag[]);

BYTE	RecvBuff[64];	//受信データ
BYTE	LineError[64];	//ラインエラー
DWORD EC16550 = 0;	/*ライン状態変化の割込みの起きた回数*/	
DWORD nTotalRecvData = 0;

void PrintHexString(BYTE* p, BYTE* le, int n)
{
	int i, j=0;
	char tag[5];
	for(i=0; i<n; ++i){
		LineErrorTag(*le++, tag);
		DebugPrintf("%02x%s ", *p++ & 0xff, tag);
	}
}

void PrintReceivedData(BYTE* p, BYTE* le, int n)
{
	if(n>0){
		DebugPrintf(" %d: ", n);
		PrintHexString(p, le, n);
		DebugPrintf(NEWLINE);
	}
}

void IntHdr16550(void)
{
	int i;
	TRACE_SLOT* pTrace;
	BYTE	ms,iid,ls=0xff;//,data;
	int		nRecv;
	iid = IID16550(ch16550);		/*割込み識別レジスタ値*/
	ms  = MSTAT16550(ch16550);	/*モデム ステータス レジスタ値*/
	pTrace = GetTraceSlot();
	get_tim(&pTrace->timestamp);
	pTrace->iid = iid & 0x0f;
	pTrace->mstat = ms;
	switch( iid & 0x0f){
	case 0x06:			/*ライン状態変化(Overrun Error/Parity Error/Framing Error/Break Interrupt*/
		DebugPrintf("16550 intr : line status changed."NEWLINE);
//		ls = LSTAT16550(ch16550);
//		pTrace->lstat = ls;
//		DebugPrintf("16550 intr : line status changed : %02x [", ls);
//		PrintLineError(ls);
//		DebugPrintf("]" NEWLINE );
		//if(ls & LS16_BREAK_SIGNAL){
			//if(ls & LS16_RECV_READY){
			//	BYTE stat = LSTAT16550(ch16550);	/*read status*/
			//	data = READ16550(ch16550);	//データを捨てる
			//	DebugPrintf("Line status changed: %02X, %02X\n", stat, data);
			//}
		//}
		nRecv = ReadFIFO(ch16550, RecvBuff, LineError);
		PrintReceivedData(RecvBuff, LineError, nRecv);
		pTrace->count = nRecv;
		++ EC16550;
		break;
	case 0x0c:			/*FIFOモードでの受信タイムアウト(Timeout)*/
		DebugPrintf("16550 intr : receive timeout." NEWLINE);
		nRecv = ReadFIFO(ch16550, RecvBuff, LineError);
		PrintReceivedData(RecvBuff, LineError, nRecv);
		pTrace->count = nRecv;
		break;
	case 0x04:			/*文字受信(Rx Ready)*/
		DebugPrintf("16550 intr : Rx Ready." NEWLINE);
		nRecv = ReadFIFO(ch16550, RecvBuff, LineError);
		PrintReceivedData(RecvBuff, LineError, nRecv);
		pTrace->count = nRecv;
		break;
	case 0x02:			/*送信(Tx Empty)*/
		//DebugPrintf("16550 intr : Tx Empty." NEWLINE);
		if(nTx > 0){
			for (i=0; i<SizeOfFIFO; ++i){
				WRITE16550(ch16550, *pTx++);
				++WriteCount;
				++pTrace->count;
				if (--nTx == 0){
					/*送信データなし*/
					DIS_TX16550(ch16550);
					bSending = FALSE;
					break;
				}else{
					//ENA_TX16550(ch16550);
				}
			}
		}else{
			CMTRACE("Tx Empty : no send data\n");
			DIS_TX16550(ch16550);
			bSending = FALSE;
		}
		break;
	case 0x00:			/*モデム状態変化(Clear to Send/Data Set ready/Ring Indicator/Data Carrier Detect*/
		DebugPrintf("16550 intr : modem status changed : %02x." NEWLINE, ms);
		LedDisplayModemStatus(ms);
		break;
	default://0x01は要因なし
		DebugPrintf("16550 intr : unexpected interrupt : iid=%02x." NEWLINE, iid);
		break;
	}
	IncrTrace();
}

/*
	FIFOが空になるまで読み取る
*/
void DisplayDecimal(int num);
int ReadFIFO(int ch, BYTE buf[], BYTE le[])
{
	int	 nRecvData;
	BYTE stat,data;
	nRecvData = 0;
	while(1){	//FIFOにデータがなくなるまで読み出す
		stat = LSTAT16550(ch);	/*read status*/
		if(stat & LS16_RECV_READY){
			data = READ16550(ch);	/*read data*/
			le[nRecvData] = stat;
			buf[nRecvData++] = data;
			if(stat & LS16_LINE_ERROR){
				//DebugPrintf(" ReadFIFO[%d] error :", nRecvData);
				//PrintLineError(stat);
				//DebugPrintf(NEWLINE);
			}else{
				++nTotalRecvData;//正常受信数
			}
		}else{
			//終了
			if(stat & LS16_LINE_ERROR){
				DebugPrintf(" ReadFIFO[last]");
				PrintLineError(stat);
				DebugPrintf(NEWLINE);
			}
			break;
		}
	};
	DisplayDecimal(nTotalRecvData);
	return nRecvData;
}

void PrintLineError(BYTE stat)
{
	if(stat & LS16_LINE_ERROR){
		//DebugPrintf("Line error :");
		if(stat & LS16_BUFFER_OVERRUN){
			DebugPrintf(" OVERRUN");
		}
		if(stat & LS16_PARITY_ERROR){
			DebugPrintf(" PARITY");
		}
		if(stat & LS16_FRAMING_ERROR){
			DebugPrintf(" FRAMING");
		}
		if(stat & LS16_BREAK_SIGNAL){
			DebugPrintf(" BREAK");
		}
		//DebugPrintf(NEWLINE);
	}
}

void LineErrorTag(BYTE stat, char tag[])
{
	int i =0;
	if(stat & LS16_LINE_ERROR){
		if(stat & LS16_BUFFER_OVERRUN){
			tag[i++] = 'o';
		}
		if(stat & LS16_PARITY_ERROR){
			tag[i++] = 'p';
		}
		if(stat & LS16_FRAMING_ERROR){
			tag[i++] = 'f';
		}
		if(stat & LS16_BREAK_SIGNAL){
			tag[i++] = 'b';
		}
	}
	tag[i] = 0;
}

//モデム信号状態をLEDへ表示
void LedDisplayModemStatus(BYTE ms)
{
	halSetLED((BYTE)((ms>>4) & 0x0f));
}


