#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include "libsys.h"

#define	NO_ERROR		0x00000000
#define	ERROR			0x80000000
#define	IS_ERROR(e)		((e & ERROR) == ERROR)

#define	TEST10_ERROR	(ERROR | 0x10)
#define	TEST11_ERROR	(ERROR | 0x11)
#define	TEST12_ERROR	(ERROR | 0x12)

char buffer[16384];
int i;

extern int paranoia(int argc, char **argv);

void handler3(void)
{
	volatile UINT32 *pUART_stat = (UINT32*)sys_uart_stat;
	volatile UINT32 *pUART_data = (UINT32*)sys_uart_data;
	volatile UINT32 *pLED = (UINT32*)sys_led_port;

	while((0x10 & *pUART_stat))
	{
		buffer[i] = (char)*pUART_data;
		*pLED = buffer[i]*buffer[i];
		i = (i+1)%sizeof(buffer);

	}

}

int fibonacci(int f0, int f1, int *pDst, int len)
{
	int i;

	i = 0;

	pDst[i++] = f0;
	pDst[i++] = f1;

	for (; i < len; i++)
	{
		pDst[i] = pDst[i-2] + pDst[i-1];
	}

	return i;
}

void PrintCPUinfo(void)
{
	int result, rev_id;

	char *cpu_type_str[7] = {"invalid", "R2000", "R3000", "R6000", "R4000", "reserved", "R6000A"};

	// Print Status register
	result = CP0_SR_read();

	printf("Status  : %8.8X\n", result);

	// Print Revision
	result = CP0_PRID_read();
	rev_id = (result >> 8) & 0xFF;
	printf("CPU type: ");
	if ((rev_id > 0) && (rev_id < 0x07))
	{
		printf(cpu_type_str[rev_id]);
		printf(" Rev.");
		rev_id = result & 0xFF;
		printf("%d", rev_id);
	}
	else
		printf("Unknown");

	printf("\n");

}

int Test10_LoadStore()
{
	int *buf, i, result, data, err;
	volatile int *pPtr;

	buf = (int*)malloc(32*sizeof(int));
//	sputs("buf = ");
//	print_word((int)buf);
//	sputs("\n");

	while(1)
	{
		err = TEST10_ERROR;
		// Basic Load/Store
		pPtr = buf;
		*pPtr++ = 0x00000000;
		*pPtr++ = 0x55555555;
		*pPtr++ = 0xAAAAAAAA;
		*pPtr++ = 0xFFFFFFFF;
		pPtr = buf;
		if (*pPtr++ != 0x00000000)
			break;
		if (*pPtr++ != 0x55555555)
			break;
		if (*pPtr++ != 0xAAAAAAAA)
			break;
		if (*pPtr++ != 0xFFFFFFFF)
			break;

		// Filling from left
		pPtr = buf;
		data = 0x00000000;
		for (i=0; i < 32; i++)
		{
			*pPtr++ = data;
			data = data >> 1 | 0x80000000;

		}
		pPtr = buf;
		data = 0x00000000;
		for (i=0; i < 32; i++)
		{

			if (*pPtr++ != data)
				break;

			data = data >> 1 | 0x80000000;
		}
		if (i != 32)
			break;

		// Filling from right
		pPtr = buf;
		data = 0x00000000;
		for (i=0; i < 32; i++)
		{
			*pPtr++ = data;
			data = data << 1 | 1;

		}
		pPtr = buf;
		data = 0x00000000;
		for (i=0; i < 32; i++)
		{

			if (*pPtr++ != data)
				break;

			data = data << 1 | 1;
		}
		if (i != 32)
			break;

		// Walking ones
		pPtr = buf;
		data = 0x00000001;
		for (i=0; i < 32; i++)
		{
			*pPtr++ = data;
			data = data << 1;

		}
		pPtr = buf;
		data = 0x00000001;
		for (i=0; i < 32; i++)
		{

			if (*pPtr++ != data)
				break;

			data = data << 1;
		}
		if (i != 32)
			break;

		// Walking zeros
		pPtr = buf;
		data = 0xFFFFFFFE;
		for (i=0; i < 32; i++)
		{
			*pPtr++ = data;
			data = data << 1 | 1;

		}
		pPtr = buf;
		data = 0xFFFFFFFE;
		for (i=0; i < 32; i++)
		{

			if (*pPtr++ != data)
				break;

			data = data << 1 | 1;

		}
		if (i != 32)
			break;

		err = NO_ERROR;
		break;
	}
	free(buf);
	return err;

}


#define	NUM_ELEMENTS	10000
#define	NUM_RUNS		3
int Test11_AddSub()
{
	int buf[NUM_ELEMENTS], result, i, j, diff, fill, num_right_elements, num_right_runs;

	fill = 0xAAAAAAAA;

	num_right_runs = 0;

	for (i=0; i < NUM_RUNS; i++)
	{
		for (j=0;  j < NUM_ELEMENTS; j++)
		{
			buf[j] = fill;
			fill = ((fill ^ j) - 1) << 1;
		}
		num_right_elements = 2;
		result = fibonacci(i, i+1, buf, NUM_ELEMENTS);
		for (j=2;  j < NUM_ELEMENTS; j++)
		{
			diff = buf[j] - buf[j-1];
			if (diff == buf[j-2])
				num_right_elements++;
		}
		if (num_right_elements == NUM_ELEMENTS)
			num_right_runs++;

	}

	if (num_right_runs != NUM_RUNS)
		return TEST11_ERROR;

	return NO_ERROR;
}

int Test12_MulDiv()
{
	int mix, i, j;
	int s1, s2, sp, st;
	div_t div_res;

	srand(0x19701031);
	for (i=0; i < 100; i++)
	{
		for (j=1; j < 100; j++)
		{
			mix = (int)rand();
			mix = (mix << 8) ^ (mix << 16);
			s1 = (int)rand() ^ mix;
			mix = (int)rand();
			mix = (mix << 8) ^ (mix << 16);
			s2 = (int)rand() ^ mix;

			div_res = div(s1, s2);

			sp = s2*div_res.quot;
			st = div_res.rem + sp;
			if (st != s1)
				return TEST12_ERROR;
		}
	}
	return NO_ERROR;
}

#define PI_SCALE 10000
#define PI_MAXARR 2800
#define PI_ARRINIT 2000

int pi_calc()
{
    int i, j;
    int carry = 0;
    int arr[PI_MAXARR+1];

//	setbuf(stdout, NULL);

    for (i = 0; i <= PI_MAXARR; ++i)
        arr[i] = PI_ARRINIT;
    for (i = PI_MAXARR; i; i -= 14)
    {
        int sum = 0;
        for (j = i; j > 0; --j)
        {
            sum = sum*j + PI_SCALE*arr[j];
            arr[j] = sum % (j*2-1);
            sum /= (j*2-1);
        }
        printf("%04d", carry + sum/PI_SCALE);
        carry = sum % PI_SCALE;
    }
    printf("\n");
    return 0;
}

#define	TEST_SIZE			(16*1024*1024)	// Bytes
#define	SMALL_TEST_SIZE		(8192)			// Bytes
int main (void)
{
	int result, i, j, cnt, size;
	volatile UINT32 *pUART_baud = (UINT32*)sys_uart_baud;
	volatile UINT32 *pReg = (UINT32*)sys_led_port;
	volatile UINT32 *pReg_usec = (UINT32*)sys_timer_usec;
	volatile UINT32 *pReg_sec = (UINT32*)sys_timer_sec;
	volatile UINT32 *pSSRAM = (UINT32*)sys_ssram_io;
	time_t curr_date;
	struct tm *pDate, date;
	UINT32 start, end;
	char sel[80];
	struct timeval time_sec;

	UINT8 *ram8 = NULL;
	UINT16 *ram16 = NULL;
	UINT32 *ram32 = NULL;

	UINT32 *pSrc32, *pDst32;

//	*pUART_baud = 1;

	setbuf(stdout, NULL);
	PrintCPUinfo();

	i = 0;
	memset(buffer, 0, sizeof(buffer));
	interrupt_register(3, handler3);

	// ----------------------------------------------------------
	// Memtest BEGIN
/*	printf("SSRAM Memory Test\r\n");
	printf("Small data test\r\n");
	printf("Write (32-Bit access)...");
	start = clock();
	for (j=0; j < TEST_SIZE/SMALL_TEST_SIZE; j++)
		for (i=0; i < SMALL_TEST_SIZE/4; i++)
			pSSRAM[i] = (UINT32)i;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	PrintBuffer8((UINT8*)pSSRAM, 16, 256);

	printf("Verify (32-Bit access)...");
	start = clock();
	for (j=0; j < TEST_SIZE/SMALL_TEST_SIZE; j++)
		for (i=SMALL_TEST_SIZE/4-1; i >= 0; i--)
			if (pSSRAM[i] != (UINT32)i)
				break;

	end = clock();
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));
*/
	// ----------------------------------------------------------
	printf("Dump Flash\n");
	PrintBuffer8((UINT8*)0, 16, 256);

	// Memtest BEGIN
	printf("SDRAM Memory Test\n");
	printf("Test size %d kByte\n\n", TEST_SIZE/1024);

	ram8 = (UINT8*)calloc(TEST_SIZE,sizeof(UINT8));
	printf("Zeroize Memory (memset)...");
	start = clock();
	memset(ram8, 0, TEST_SIZE);
	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram8);

	ram8 = (UINT8*)calloc(TEST_SIZE,sizeof(UINT8));
	printf("Zeroize Memory (8-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE; i++)
		ram8[i] = (UINT8)0;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram8);

	ram16 = (UINT16*)calloc(TEST_SIZE/2,sizeof(UINT16));
	printf("Zeroize Memory (16-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE/2; i++)
		ram16[i] = (UINT16)0;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram16);

	ram32 = (UINT32*)calloc(TEST_SIZE/4,sizeof(UINT32));
	printf("Zeroize Memory (32-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE/4; i++)
		ram32[i] = (UINT32)0;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram32);

	ram8 = (UINT8*)calloc(TEST_SIZE,sizeof(UINT8));
	printf("Write (8-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE; i++)
		ram8[i] = (UINT8)i;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	printf("Verify (8-Bit access)...");
	start = clock();
	for (i=TEST_SIZE-1; i >= 0; i--)
		if (ram8[i] != (UINT8)i)
			break;

	end = clock();
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram8);

	ram16 = (UINT16*)calloc(TEST_SIZE/2,sizeof(UINT16));
	printf("Write (16-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE/2; i++)
		ram16[i] = (UINT16)i;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	printf("Verify (16-Bit access)...");
	start = clock();
	for (i=TEST_SIZE/2-1; i >= 0; i--)
		if (ram16[i] != (UINT16)i)
			break;

	end = clock();
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram16);

	ram32 = (UINT32*)calloc(TEST_SIZE/4,sizeof(UINT32));
	printf("Write (32-Bit access)...");
	start = clock();
	for (i=0; i < TEST_SIZE/4; i++)
		ram32[i] = (UINT32)i;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	printf("Verify (32-Bit access)...");
	start = clock();
	for (i=TEST_SIZE/4-1; i >= 0; i--)
		if (ram32[i] != (UINT32)i)
			break;

	end = clock();
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram32);

	printf("Small data test\r\n");
	printf("Test size %d kByte\n\n", SMALL_TEST_SIZE/1024);
	printf("Write (32-Bit access)...");
	ram32 = (UINT32*)calloc(SMALL_TEST_SIZE/4,sizeof(UINT32));
	start = clock();
	for (j=0; j < TEST_SIZE/SMALL_TEST_SIZE; j++)
		for (i=0; i < SMALL_TEST_SIZE/4; i++)
			ram32[i] = (UINT32)i;

	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	printf("Verify (32-Bit access)...");
	start = clock();
	for (j=0; j < TEST_SIZE/SMALL_TEST_SIZE; j++)
		for (i=SMALL_TEST_SIZE/4-1; i >= 0; i--)
			if (ram32[i] != (UINT32)i)
				break;

	end = clock();
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed (%.2f MByte/s)\n", (double)TEST_SIZE/(1000*(end-start)));

	free(ram32);

	printf("Memcpy() Test \n");
	pSrc32 = (UINT32*)calloc(TEST_SIZE/8,sizeof(UINT32));
	pDst32 = (UINT32*)calloc(TEST_SIZE/8,sizeof(UINT32));
	printf("Copying %d kBytes...", TEST_SIZE/(2*1024));
	start = clock();
	memcpy(pDst32, pSrc32, TEST_SIZE/2);
	end = clock();
	printf("done (%.2f MByte/s)\n", (double)TEST_SIZE/(2*1000*(end-start)));

	free(pSrc32);
	free(pDst32);

	printf("Boot code read \n");
	size = 0x2000;
	pSrc32 = (UINT32*)0xBFC00000;
	pDst32 = (UINT32*)calloc(size,sizeof(UINT32));
	printf("Copying %d kBytes...", size/1024);
	start = clock();
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	memcpy(pDst32, pSrc32, size);
	end = clock();
	printf("done (%.2f MByte/s)\n", (double)10*size/(1000*(end-start)));
	memdump((UINT8*)pSrc32, 0, 16, size);
	printf("Verify Boot code...");
	for (i=size/4-1; i >= 0; i--)
		if (pDst32[i] != pSrc32[i])
			break;
	i++;
	if (i)
		printf("failed\r\n");
	else
		printf("passed\r\n");

	free(pDst32);

	// Memtest END
	// ----------------------------------------------------------
	sputs("\r\n");

	printf("Datum und Uhrzeit setzen? [j/N]: ");
	scanf("%s", sel);
	if (toupper(sel[0]) == 'J')
	{
		do
		{
			printf("Datum\n");
		printf("Jahr  : ");
		scanf("%d", &date.tm_year);
		printf("Monat : ");
		scanf("%d", &date.tm_mon);
		printf("Tag   : ");
		scanf("%d", &date.tm_mday);

			printf("Uhrzeit\n");
			printf("Stunde  : ");
			scanf("%d", &date.tm_hour);
			printf("Minute  : ");
			scanf("%d", &date.tm_min);
			printf("Sekunde : ");
			scanf("%d", &date.tm_sec);

			date.tm_year  -= 1900;
			date.tm_mon  -= 1;
			time_sec.tv_sec = mktime(&date);
			time_sec.tv_usec = 0;
			settimeofday(&time_sec, NULL);

			printf("Datum\n");
			curr_date = time(NULL);
			pDate = gmtime(&curr_date);
			puts(asctime(pDate));
			printf("\nOK? [J/n]: ");
			scanf("%s", sel);
			printf("sel=%d\n", (int)sel[0]);


		} while(toupper(sel[0]) == 'N');
	}

	interrupt_enable(3);
	cnt = 0;
	while (1)
	{
		*pReg = cnt & 0x3FFFFFFF;

		printf("-------------------------------------------\n");
		printf("-- LoadStore ------------------------------\n");
		printf("-------------------------------------------\n");
		result = Test10_LoadStore();
		if (IS_ERROR(result))
			break;
		printf("passed\n\n");

		printf("-------------------------------------------\n");
		printf("-- AddSub ---------------------------------\n");
		printf("-------------------------------------------\n");
		result = Test11_AddSub();
		if (IS_ERROR(result))
			break;
		printf("passed\n\n");

		printf("-------------------------------------------\n");
		printf("-- MulDiv ---------------------------------\n");
		printf("-------------------------------------------\n");
		result = Test12_MulDiv();
		if (IS_ERROR(result))
			break;
		printf("passed\n\n");

		printf("-------------------------------------------\n");
		printf("-- Paranoia -------------------------------\n");
		printf("-------------------------------------------\n");
		paranoia(1, NULL);
		printf("passed\n\n");

		printf("-------------------------------------------\n");
		printf("-- PI calc --------------------------------\n");
		printf("-------------------------------------------\n");
		pi_calc();
		printf("passed\n\n");

		printf("Iteration %d: Passed\n", cnt);
		curr_date = time(NULL);
		pDate = gmtime(&curr_date);
		puts(asctime(pDate));
		cnt++;

//		printf("V=%.17e\n", pow((double)cnt-1, (double)cnt));
	}
	*pReg = 0x40000000 | cnt;
	printf("Failed with error %8.8X\n", result);

	return 1;

}

