#include "libsys.h"
#include "cfiflash.h"

typedef struct _ssrec_t
{
	UINT32 addr;
	UINT32 size;
	UINT32 type;
	UINT8 *data;

} srec_t;

enum srec_type
{
	srec_err, srec_sob, srec_data, srec_rec, srec_eob
};

UINT8 h2i(UINT8 *c)
{
	int i;
	UINT8 t, b = 0;

	for (i=0; i < 2; i++)
	{
		t = c[i];
		if (t >= 'A')
			t = t - 'A' + 10;
		else
			t -= '0';

		b = (b << 4) | t;
	}
	return b;
}

int decode_srec(srec_t *pRec, UINT8 *pBuf, int buflen)
{
	UINT8 chksum, byte;
	int alen, i;

	if (!buflen)
		return srec_err;

	pRec->type = srec_rec;
	alen = 0;
	if ((pBuf[1] > '0') && (pBuf[1] < '4'))
	{
	  alen = pBuf[1] - '0' + 1;
	  pRec->type = srec_data;
	}
	else if ((pBuf[1] >= '7') && (pBuf[1] <= '9'))
	{
	  alen = 11 - (pBuf[1] - '0');
	  pRec->type = srec_eob;
	}
	else if (pBuf[1] == '0')
	{
	  alen = 2;
	  pRec->type = srec_sob;
	}

	byte = h2i(&pBuf[2]);
	pRec->size = byte;
	chksum = byte;

	pRec->addr = 0;
	for (i=0; i < alen; i++)
	{
		byte = h2i(&pBuf[4+2*i]);
        pRec->addr = pRec->addr << 8 | byte;
		chksum += byte;
	}
	pRec->size -= (alen+1);

	pRec->data = (UINT8*) &pBuf[4 + 2*alen];
	for (i=0; i < (int)pRec->size; i++)
	{
		byte = h2i(&pRec->data[2*i]);
		pRec->data[i] = byte;
		chksum += byte;
	}
	chksum = ~chksum;
	byte = h2i(&pRec->data[2*i]);
	if (chksum != byte)
		return 0;

	return pRec->type;
}

int srec_getline(UINT8 *pLine)
{
	char c;
	int i = 0;

	do
	{
		c = readchar();

	} while ((c != 's') && (c != 'S'));

	while(((c != 0x0D) && (c != 0x0A)))
	{
		pLine[i++] = c;
		c = readchar();
	};

	return i;
}

void Exec_at(void *pEntry)
{
	__asm
	(
		".set	noreorder\n"
		"mfc0	$26, $12\n"			// change exception vector
		"li		$27, 0xFFBFFFFF\n"
		"and	$26, $27\n"
		"mtc0	$26, $12\n"
		"jr		$4\n"				// jump entry
		"rfe\n"
		".set	reorder\n"

	);
}

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();

	sputs("Status  : ");
	print_word(result);
	sputs("\n");

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

	sputs("\n");

}


#define	TEST_SIZE			(1024*1024)	// bytes
//#define	TEST_SIZE			(1024)	// bytes
#define SDRAM_BASE			0x40000000
#define	IMAGE_OFFSET		0x01000000
#define	FLASH_OFFSET		0x00700000

int main(int argc, char *argv[])
{
	UINT8 buf[256];
	srec_t srec;
	int result, i;
	UINT32 haddr, addr;
	flash_t flash;

	volatile UINT32 *pReg = (UINT32*)sys_led_port;
	volatile UINT32 *pBtn = (UINT32*)sys_gpio0;
	volatile UINT8 *pMem;
	volatile UINT32 *pROM32;
	volatile UINT32 *pMem32;
	volatile UINT32 *pFlash = (UINT32*)sys_flash_io;
	volatile UINT32 *pUSB = (UINT32*)sys_usb_data;

	volatile UINT8 *ram8 = (UINT8*)SDRAM_BASE;
	volatile UINT16 *ram16 = (UINT16*)SDRAM_BASE;
	volatile UINT32 *ram32 = (UINT32*)SDRAM_BASE;

	*pReg = 0;
	haddr = 0;
/*
	__asm __volatile
	(
		".set	noreorder\n"
		"li $a0, 0xA0020000\n"
		"li $t0, 0x12345678\n"
		"li $t1, 0x5555AAAA\n"
		"sw $t0, 0($a0)\n"
//		"nop\n"
		"sw $t1, 4($a0)\n"
//		"nop\n"
		"lw $v0, 0($a0)\n"
//		"nop\n"
		"lw $v0, 4($a0)\n"
//		"nop\n"
		"sw $t0, 0($a0)\n"
//		"nop\n"
		"lw $v0, 4($a0)\n"
//		"nop\n"
		"sw $t1, 4($a0)\n"
//		"nop\n"
		"lw $v0, 0($a0)\n"
		".set	reorder\n"
	);
	__asm __volatile
	(
		".set	noreorder\n"
		"lui    $v1, 0xA002\n"
		"lui	$v0,0x70\n"
		"ori	$v0,$v0,0x70\n"
		"sw	$v0,0($v1)\n"
		"lui	$a0,0x4002\n"
		"lw	$a1,0($v1)\n"
		"lw	$ra,12($sp)\n"
		"lw	$s1,8($sp)\n"
		"lw	$s0,4($sp)\n"
		".set	reorder\n"
	);

*/
//	PrintCPUinfo();
/*
	*(pFlash+0) = 0x12345678;
	*(pFlash+1) = 0xAAAA5555;
	*(pFlash+2) = 0xFFFF0000;
	*(pFlash+3) = 0x0000FFFF;

	*pReg = *(pFlash+0);
	*pReg = *(pFlash+1);
	*pReg = *(pFlash+2);
	*pReg = *(pFlash+3);

	*(pUSB+0) = 0x12345678;
	*(pUSB+1) = 0xAAAA5555;
	*(pUSB+2) = 0xFFFF0000;
	*(pUSB+3) = 0x0000FFFF;

	*pReg = *(pUSB+0);
	*pReg = *(pUSB+1);
	*pReg = *(pUSB+2);
	*pReg = *(pUSB+3);
	sputs("BOOT ");

	pMem32 = (UINT32*)0x40000000;
	pROM32 = (UINT32*)0x00000000;
	for (i=0; i < 0x3500; i+=4)
		*pMem32++ = *pROM32++;

	Exec_at((void*)0x40000000);


	// Memtest BEGIN
	sputs("SDRAM Memory Test\r\n");
	sputs("Write (8-Bit access)...");
	for (i=0; i < TEST_SIZE; i++)
		ram8[i] = (UINT8)i;

	sputs("done\r\n");

	sputs("Verify (8-Bit access)...");
	for (i=TEST_SIZE-1; i >= 0; i--)
		if (ram8[i] != (UINT8)i)
			break;
	i++;
	if (i)
		sputs("failed\r\n");
	else
		sputs("passed\r\n");


	sputs("Write (16-Bit access)...");
	for (i=0; i < TEST_SIZE/2; i++)
		ram16[i] = (UINT16)i;

	sputs("done\r\n");

	sputs("Verify (16-Bit access)...");
	for (i=TEST_SIZE/2-1; i >= 0; i--)
		if (ram16[i] != (UINT16)i)
			break;
	i++;
	if (i)
		sputs("failed\r\n");
	else
		sputs("passed\r\n");


	sputs("Write (32-Bit access)...");
	for (i=0; i < TEST_SIZE/4; i++)
		ram32[i] = (UINT32)i;

	sputs("done\r\n");

	sputs("Verify (32-Bit access)...");
	for (i=TEST_SIZE/4-1; i >= 0; i--)
		if (ram32[i] != (UINT32)i)
			break;
	i++;
	if (i)
		sputs("failed\r\n");
	else
		sputs("passed\r\n");

	for (i=0; i < TEST_SIZE/4; i++)
		ram32[i] = 0;

	// Memtest END
	// ----------------------------------------------------------
*/
	ram32 = (UINT32*)(SDRAM_BASE + IMAGE_OFFSET);
	pFlash = (UINT32*)(sys_flash_io + FLASH_OFFSET);
	print_word((UINT32)*pBtn);
	if (!*pBtn)
	{
		sputs("Booting from flash..");
		for (i=0; i < TEST_SIZE/4; i++)
			ram32[i] = pFlash[i];

		sputs("done");

		Exec_at((void*)ram32);
	}
	else
	{

		sputs("SDRAM erase at        : ");
		print_word((UINT32)ram32);
		sputs("\n");
		for (i=0; i < TEST_SIZE/4; i++)
			ram32[i] = 0;

		cfi_init(&flash, sys_flash_io);
		if (cfi_find(&flash) < 0)
			return 1;

		sputs("Found Flash at ");
		print_word((UINT32)flash.pBase);
		sputs("\n");

		addr = FLASH_OFFSET;
		sputs("Flash erase at        : ");
		print_word(addr);
		sputs("\n");
		cfi_block_erase(&flash, addr/4);

		while(1)
		{
			sputs("BOOT ");

			while(1)
			{
				result = srec_getline(buf);
				result = decode_srec(&srec, buf, result);
				*pReg = 0;
				srec.addr += IMAGE_OFFSET;
				switch (result)
				{
					case srec_data:
						if ((srec.addr + srec.size) > haddr)
							haddr = srec.addr + srec.size;

						pMem = (UINT8*)srec.addr;
						for (i=0; i < srec.size; i++)
							*pMem++ = srec.data[i];

						break;

					case srec_eob:
						*pReg = 1;
						print_word(srec.addr);
						sputs(" to ");
						print_word(haddr-4);
						sputs("\n");

						addr = FLASH_OFFSET;
						sputs("Program Flash at      : ");
						print_word(addr);
						sputs("\n");
						cfi_program_multi(&flash, addr/4, (UINT32*)ram32, TEST_SIZE/4);

	//					Exec_at((void*)srec.addr);
						break;

					default:
						break;
				}
				if (result == srec_err)
				{
					*pReg = 0x40000000;
					break;
				}

			};
		}
	}
	return 0;
}

