//-------------------------------------------------------------------
// McStarSubs.cpp
//
// March 21, 2008
// Gene Massion		
//-------------------------------------------------------------------

#include "stdafx.h"
#include <windows.h>
#include <string.h>
#include "gpio.h"
#include "ssp.h"
#include "clk.h"
#include "McStar.h"
#include "MapIO.hpp"
#include "resource.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "ADSmartIO.h"
#include "ADSmartIO_load.h"

#include "ceddk.h"

extern volatile GPIO_REGS	*GpioRegPtr;
extern volatile SSP_REGS	*SspRegPtr;
extern volatile CLKMAN_REGS	*v_pClkRegs;

int dwReadSSP( unsigned int* ptr, int numSamples )
{
	int		i,
			count;

	for(i=0; i<numSamples; i++)
	{
		count=0x3ffff;
		while((READ_REGISTER_ULONG(&SspRegPtr->SSSR) & SSSR_RNE) == 0)
		{
			if(count-- == 0)
				break;
		}
	
		if( count <= 0)
		{
			WriteLog(1,1,1, "Read SSP timed out with RNE = 0");
			return 0xFFFFFFFF;
		}
		else
		{	
			*(ptr + i) = READ_REGISTER_ULONG(&SspRegPtr->SSDR);
//			DEBUGMSG(1,(TEXT("Count = %08x SSDR Register = %08x \n"), count, value));
		}
	}
	return i;
}

bool ReadConfig( CFGDATA& configData )
{
	FILE *configFile;
	char inString[80],
	 	 *label,
		 *value,
		 cTemp[160];
	wchar_t	wcTemp[80];
	

	if ( (configFile = fopen( "\\FlashFX Disk\\McStar\\McStarConfig.txt", "r")) != NULL)
		WriteLog(1,1,1, "Opened Configuration File");
	else
	{
		//process error
	}


	while( !feof(configFile) ) 
	{
		fgets(inString, 80, configFile);
		label = strtok(inString, ":");
		value = strtok(NULL, ":") ;

		if (strcmp(label, "SampleRate") == 0)
		{
			configData.sampleRate = atof(value);
			sprintf( cTemp, "sampleRate %10.3f", configData.sampleRate);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
		else if (strcmp(label, "numSamples") == 0)
		{
			configData.numSamples = atoi(value);
			sprintf(cTemp, "numSamples %d", configData.numSamples);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
		else if (strcmp(label, "USBWrites") == 0)
		{
			configData.USBWrites = atoi(value);
			sprintf(cTemp, "USBWrites %d", configData.USBWrites);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
		else if (strcmp(label, "numFlashFiles") == 0)
		{
			configData.numFlashFiles = atoi(value);
			sprintf(cTemp, "NumFlashFiles %d", configData.numFlashFiles);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
		else if (strcmp(label, "USBSpinUpTime") == 0)
		{
			configData.USBSpinUpTime = atoi(value);
			sprintf(cTemp, "USBSpinUpTime %d", configData.USBSpinUpTime);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
		else if (strcmp(label, "SleepPeriod") == 0)
		{
			configData.sleepPeriod = atoi(value);
			sprintf(cTemp, "SleepPeriod rate %d\n", configData.sleepPeriod);
			WriteLog(1,1,1, cTemp);
			label = "";
		}
	}

	fclose(configFile);
	return TRUE;
}

void GoToSleep( int sleepPeriod )
{
	// routine to put Bitsy to sleep
}

void SetPowerState( BOOL powerState )
{
	LPVOID	m_hUSB,
			m_hCKEN,
			m_hAC97;
	DWORD	*ptr;
/*
	ADSmartIO *smartIOCtrl	= 0;

	smartIOCtrl = new ADSmartIO;
	if (smartIOCtrl == NULL)
	{
		RETAILMSG (TRUE, (L"Out of memory\r\n"));
		return;
	}

	if(powerState == LOW_POWER)
	{
//		(*smartIOCtrl).SetBacklightState (false);	//Turn off the LCD
*/
		//Turn off the USB port
/*		m_hUSB = MapAddress(USB_BASE);
		ptr = (DWORD*)m_hUSB;
		ptr += (UHCCOMS_OFFSET / sizeof(DWORD));
		*ptr |= 0x1;		// Set the HCR bit
		Sleep(1);

		ptr = (DWORD*)m_hUSB;
		ptr += (UHCRHS_OFFSET / sizeof(DWORD));
		*ptr |= 0x1;		// Set the LPS bit

		ptr = (DWORD*)m_hUSB;
		ptr += (UHCHR_OFFSET / sizeof(DWORD));
		*ptr |= 0x1 << 5;		// Set the SSE bit

		//Turn off the USB clocks and the PWM clocks
		m_hCKEN = MapAddress(CKEN_BASE);
		ptr = (DWORD*)m_hCKEN;
		*ptr |= 0x0C00;		// Set the USB clock enable bits
		*ptr &= ~0x0C00;		// Clear the USB clock enable bits
		*ptr &= ~0x03;			// Clear the PWM clock enable bits

		UnmapAddress(m_hUSB);
*/
/*		//Turn off the AC97
		m_hAC97 = MapAddress(AC97_BASE);
		ptr = (DWORD*)m_hAC97;
		ptr += (AC97x26_OFFSET / sizeof(DWORD));
		*ptr |= 0x1000;		// Set AC97 Powerdown register PR4 bit

		// wait for GSR[cdone]
		ptr = (DWORD*)m_hAC97;
		ptr += (AC97GSR_OFFSET / sizeof(DWORD));
		while( (*ptr & 0x00080000) == 0)
			DEBUGMSG(1,(TEXT("AC97 GSR Register CDONE = %d\n"), (*ptr & 0x00080000)));
		
		ptr = (DWORD*)m_hAC97;
		ptr += (AC97GCR_OFFSET / sizeof(DWORD));
		*ptr |= 0x08; // Set the ACOFF bit

		//Set the AC97 bit in CKEN
		m_hCKEN = MapAddress(CKEN_BASE);
		ptr = (DWORD*)m_hCKEN;
		*ptr |= 0x80000000;

		//Wait for GSR[ACOFFD]
		ptr = (DWORD*)m_hAC97;
		ptr += (AC97GSR_OFFSET / sizeof(DWORD));
		while( (*ptr & 0x00000008) == 0)
			DEBUGMSG(1,(TEXT("AC97 GSR Register ACOFFD= %d\n"), (*ptr & 0x00080000)));

		UnmapAddress(m_hAC97);

		//Clear the AC97 bit in CKEN and clear the AC97 clock enable
		m_hCKEN = MapAddress(CKEN_BASE);
		ptr = (DWORD*)m_hCKEN;
		*ptr &= ~0x80000000;
		*ptr &= ~0x00000004;

		UnmapAddress(m_hCKEN);
	}
	else
	{
//		(*smartIOCtrl).SetBacklightState (true);
	}

	delete smartIOCtrl;*/
}

VOID InitPSP( VOID )
{
	LPVOID	m_hSSP3,
			m_hGPIO,
			m_hCKEN;
	DWORD	*ptr;

	PVOID	m_HSSP3Mm;
	
	m_hSSP3 = MapAddress(SSP3_BASE);

	volatile unsigned short *cpld_spi_data_reg;
	PHYSICAL_ADDRESS phy_addr;
	/* Place the physical address of the register in the low part. */
	phy_addr.LowPart  = SSP3_BASE;
	phy_addr.HighPart = 0;

	m_HSSP3Mm = MmMapIoSpace(phy_addr,4,FALSE);


	m_hCKEN = MapAddress(CKEN_BASE);
	m_hGPIO = MapAddress(GPIO_BASE);

	// Disable SSP Clock
	ptr = (DWORD*)m_hCKEN;
	*ptr &= ~(0x1 << 4);	

	//Set GPIO alternate function bits for SSP port
	ptr = (DWORD*)m_hGPIO;
	ptr += (GAFR2_U_OFFSET / sizeof(DWORD));
	*ptr &= ~(0x03 << 2);	// clear AF81 bits
	*ptr |= (0x1 << 2);		// AF81 bits set for AF1 - SSPTXD3 output
	*ptr &= ~(0x03 << 4);	// clear AF82 bits
	*ptr |= (0x1 << 4);		// AF82 bits set for AF1 - SSPRXD3 input
	*ptr &= ~(0x03 << 6);	// clear AF83 bits
	*ptr |= (0x1 << 6);		// AF83 bits set for AF1 - SSPSFRM3 output
	*ptr &= ~(0x03 << 8);	// clear AF84 bits
	*ptr |= (0x1 << 8);		// AF84 bits set for AF1 - SSPCLK3 output
	DEBUGMSG(1,(TEXT("GAFR2_U = 0x%8x \r\n"),*ptr));

	//Set SSP3 SSCR0 register
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSCR0_3_OFFSET / sizeof(DWORD));
	*ptr = 0;			// clear all SSCR0 bits
	*ptr |= (0x3 << 4); // Set Frame Format to PSP 
	*ptr |= (0x1 << 20);// Set Extended Data Size Select
	*ptr &= 0xFFF000FF; // Clear Serial Clock Rate bits
	*ptr |= (0x3 << 8); // Set Serial Clock Rate
	*ptr &= 0xFFFFFFF0; // Clear Data Size Select bits
	*ptr |= 0x07;		// Set Data Size Select to 24 bits
	//Set Network mode
//	*ptr |= (0x01 << 31); 
	//Set FRDC network mode bits to 2 time slots
//	*ptr |= (0x01 << 24);	// Actual value is 1 +FRDC
	
	DEBUGMSG(1,(TEXT("SSCR0_3 = 0x%8x \r\n"),*ptr));

	//Set SSP3 SSCR1 register
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSCR1_3_OFFSET / sizeof(DWORD));
	*ptr = 0;				// clear all SSCR1 bits
	*ptr |= (0x1 << 28);	// Set Slave Clock Free Running just active only during transfers just to make sure 
	*ptr |= (0x0 << 25);	// Set SSPSCLK direction to Master mode just to make sure
	*ptr |= (0x1 << 24);	// Set SSP Frame Direction to Slave 
//	*ptr |= (0x1 << 23);	// Set Receive Without Transmit mode, this also makes the clock run continuously
	// Set TTE because it says to do this in network mode
//	*ptr |= (0x1 << 30);
	DEBUGMSG(1,(TEXT("SSCR1_3 = 0x%8x \r\n"),*ptr));

	//Set SSP3 SSPSP register
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSPSP_3_OFFSET / sizeof(DWORD));
	*ptr = 0;				// clear all SSPSP bits
	*ptr |= (0x1 << 16);	// Set SFRMWDTH to 1 clock cycle 
	DEBUGMSG(1,(TEXT("SSPSP_3 = 0x%8x \r\n"),*ptr));

	//Set SSP3 SSRSA register
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSRSA_3_OFFSET / sizeof(DWORD));
	*ptr = 0;			// clear all SSRSA bits
	*ptr |= 0x03;		// Set bits to receive in time slot 1 and 2
	DEBUGMSG(1,(TEXT("SSRSA_3 = 0x%8x \r\n"),*ptr));

	//Set GPIO direction register must be written after PSP bits are set
	ptr = (DWORD*)m_hGPIO;
	ptr += (GPDR2_OFFSET / sizeof(DWORD));
	*ptr |= (0x1 << 17); // GPIO 81 set to output
	*ptr &= ~(0x1 << 18); // GPIO 82 set to input
	*ptr &= ~(0x1 << 19); // GPIO 83 set to input
	*ptr |= (0x1 << 20); // GPIO 84 set to output
	DEBUGMSG(1,(TEXT("GPDR2 = 0x%8x \r\n"),*ptr));

	//Clear the RX FIFO Overrun bit (not sure this needs to be done for read only operations)
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSSR_3_OFFSET / sizeof(DWORD));
	*ptr |= (0x1 << 7);	
	DEBUGMSG(1,(TEXT("SSSR_3 = 0x%8x \r\n"),*ptr));

	//Enable SSP Synchronous Serial Enable
	ptr = (DWORD*)m_hSSP3;
	ptr += (SSCR0_3_OFFSET / sizeof(DWORD));
	*ptr |= (0x1 << 7); // Set SSE bit
	DEBUGMSG(1,(TEXT("SSCR0_3 = 0x%8x \r\n\n"),*ptr));

	// Enable SSP Clock 
	ptr = (DWORD*)m_hCKEN;
	*ptr |= (0x1 << 4);	

	UnmapAddress(m_hSSP3);
	UnmapAddress(m_hCKEN);
	UnmapAddress(m_hGPIO);

	return ;
}


int WriteToUSBDisk(unsigned int* data, int numValues, TCHAR fileName[80])
{
	int		numWritten;
	FILE	*outFile;

	CreateFilename( fileName, USBFILE );
	outFile = _wfopen( fileName, (TEXT("ab")));

	numWritten = fwrite(data, sizeof(int), numValues, outFile);
	DEBUGMSG(1, (_T("Wrote %d samples to USB Disk \n"), numWritten));

	fclose(outFile);

	return numWritten;
}

int WriteToFlash(unsigned int* data, int numValues, TCHAR fileName[80])
{
	int		numWritten;
	FILE	*outFile;

	CreateFilename( fileName, FLASHFILE );

	outFile = _wfopen( fileName, (TEXT("ab")));

	numWritten = fwrite(data, sizeof(int), numValues, outFile);
	DEBUGMSG(1, (_T("Wrote %d samples to Flash Disk \n"), numWritten));

	fclose(outFile);

	return numWritten;
}

double iToDW( int iValue )
{
	double dResult;
	double LSB; //= 2.980232594e-7;
	double Vref;
	
	Vref = (double)2.5;
	LSB = Vref / (pow(2.0, 23.0) - 1.0);

	if( (iValue & 0x00800000) != 0)
	{
		iValue = ~iValue;
		iValue = iValue & 0x00FFFFFF;
		dResult = -1.0 * ((double)(iValue + 0x00000001)) * LSB;
	} else
		dResult = double(iValue) * LSB;

	return dResult;
}

int DeleteOldFiles( VOID )
{
	WIN32_FIND_DATA fd;
	HANDLE			hFind;
	int				numFiles;
	DWORD			time1, time2, dwTemp;
	wchar_t			cBuf[100];

	numFiles = 0;

	RETAILMSG(1, (TEXT("Deleting old CF files \n")));
	time1 = GetTickCount();
	hFind = FindFirstFile(TEXT ("\\Storage Card\\McSTAR\\Data\\*.*"), &fd);
	if(hFind != INVALID_HANDLE_VALUE )
	{
		do
		{
			wcscpy(cBuf,(TEXT ("\\Storage Card\\McSTAR\\Data\\")));
			wcscat(cBuf, fd.cFileName);
			RETAILMSG(1, (TEXT("Deleting CF file: %s \n"), cBuf));
			DeleteFile(cBuf);
			numFiles = numFiles + 1;
		} while(FindNextFile(hFind, &fd));
	}
	time2 = GetTickCount();
	dwTemp = time2 - time1;
	RETAILMSG(1, (TEXT("Delete Done   Time = %d mSec\n\n"), dwTemp));

	return numFiles;
}

void UnmapAddress(LPVOID address)
{
	VirtualFree(address, 0, MEM_RELEASE);
	address = NULL;
}

#define	MEMORY_SIZE					0x10000  // 64kiB memory space; max allocation for VirtualAlloc is 64MiB

extern "C" BOOL VirtualCopy(LPVOID lpvDestMem, LPVOID lpvSrcMem, 
							DWORD dwSizeInBytes, DWORD dwProtectFlag);

/////////////////////////////////////////////
// MapAddress 
//  Maps the desired memory address.
//  This function should be called first and its
//  return value is then passed to the ReadData16 or
//  WriteData16 functions as the baseAddress.
//
// [I] baseAddress - base address to map
// [O] Virtual Address pointer to mapped region
// 
/////////////////////////////////////////////
LPVOID MapAddress(DWORD baseAddress)
{
	LPVOID pAddr = VirtualAlloc(0, MEMORY_SIZE, MEM_RESERVE, PAGE_NOACCESS);
	
	if(pAddr)	
	{
		if(VirtualCopy (pAddr, (PVOID)(baseAddress >> 8), 
				MEMORY_SIZE, PAGE_PHYSICAL|PAGE_READWRITE|PAGE_NOCACHE) == TRUE) 
		{
//				DEBUGMSG(1, (_T("0x%08x mapped to: 0x%08x\r\n"), baseAddress, pAddr));
		}
		else 
		{
			RETAILMSG(1, (_T("Error! at VirtualCopy()\r\n")));
			return NULL;
		}
	}
	else 
	{
		RETAILMSG(1, (_T("Error! at VirtualAlloc()\r\n")));
		return NULL;
	}

	return pAddr;
}

void MapRegisters( VOID )
{
	ULONG	sscr0,
			sscr1,
			sspsp,
			gafr,
			gpdr,
			pcon=0;

	GpioRegPtr = (GPIO_REGS *)MapPhysicalAddr((GPIO_REGS *)GPIO_BASE_PHYSICAL,sizeof(GPIO_REGS));	// 1 Page
	if(GpioRegPtr == NULL)
		RETAILMSG(1,(L" Error 'cause GpioRegPtr == NULL \r\n"));

	SspRegPtr = (SSP_REGS *)MapPhysicalAddr((SSP_REGS *)NSSP_BASE_PHYSICAL,sizeof(SSP_REGS));	// 1 Page
	if(SspRegPtr == NULL)
		RETAILMSG(1,(L" Error 'cause SspRegPtr == NULL \r\n"));

	v_pClkRegs = (CLKMAN_REGS *)MapPhysicalAddr((CLKMAN_REGS *)CLK_BASE_PHYSICAL,sizeof(CLKMAN_REGS));	// 1 Page
	if(v_pClkRegs == NULL)
		RETAILMSG(1,(L" Error 'cause v_pClkRegs == NULL \r\n"));

	DEBUGMSG(1,(TEXT(" InitNSSP: + \r\n")));
	DEBUGMSG(1,(TEXT("Initial Register values before InitNSP \r\n")));
	gafr = READ_REGISTER_ULONG(&GpioRegPtr->GAFR2_U);
	DEBUGMSG(1,(TEXT(" SspRegPtr->GAFR2_U= 0x%x \r\n"),gafr));
	gpdr = READ_REGISTER_ULONG(&GpioRegPtr->GPDR_2);
	DEBUGMSG(1,(TEXT(" SspRegPtr->GPDR_2= 0x%x \r\n"),gpdr));
	sscr0 = READ_REGISTER_ULONG(&SspRegPtr->SSCR0);
	DEBUGMSG(1,(TEXT(" SspRegPtr->SSCR0= 0x%x \r\n"),sscr0));
	sscr1 = READ_REGISTER_ULONG(&SspRegPtr->SSCR1);
	DEBUGMSG(1,(TEXT(" SspRegPtr->SSCR1= 0x%x \r\n"),sscr1));
	sspsp = READ_REGISTER_ULONG(&SspRegPtr->SSPSP);
	DEBUGMSG(1,(TEXT(" SspRegPtr->SSPSP= 0x%x \r\n"),sspsp));
}

float GetBatVoltage( int numAvgs )
{
	int i,
		sum;
	unsigned short ADCValue;
	float avg;

	sum = 0;
	for(i=0; i<numAvgs; i++)
	{
		ADCValue = SIOAnalogConversion( (byte)0 );
		sum = sum + ADCValue;
	}
	avg = sum / numAvgs;

	return(avg);
}




void WriteLog(BOOL print, BOOL debug, BOOL file, char logEntry[200])
{
	FILE	*logFile;
	TCHAR	tcharTemp[160];
	__time64_t	t64Time;
	struct tm	timeNow;
	static BOOL		firstCall = 1;
	static TCHAR	fileName[80];
	
	if(firstCall == 1)
	{
		CreateFilename(fileName, LOGFILE );
		firstCall = 0;
	}

	if(print == 1)
	{
		_time64( &t64Time );
		_localtime64_s(&timeNow, &t64Time);
		printf("%d-%d-%d\t%d:%d:%d\t%s\n", timeNow.tm_mon+1, timeNow.tm_mday, timeNow.tm_year+1900, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec, logEntry);
	}
	if(debug == 1)
	{
		mbstowcs(tcharTemp, logEntry, 200);
		DEBUGMSG(1, (TEXT("%s\n"), tcharTemp));
	}
	if(file == 1)
	{ 
		logFile = _wfopen( fileName, L"a");
		_time64( &t64Time );
		_localtime64_s(&timeNow, &t64Time);
		fprintf(logFile, "%d-%d-%d\t%d:%d:%d\t%s\n", timeNow.tm_mon+1, timeNow.tm_mday, timeNow.tm_year+1900, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec, logEntry);
		fclose(logFile);
	}
}

void CheckMemory( char comment[80] )
{
   // Program memory information
    MEMORYSTATUS	memInfo;
	char			cTemp[80];
	
	memInfo.dwLength = sizeof(memInfo);
    GlobalMemoryStatus(&memInfo);
    sprintf(cTemp, "%s Total RAM: %d bytes\n Free: %d \nUsed: %d\nMemory Load %d percent\n\n", comment, memInfo.dwTotalPhys, memInfo.dwAvailPhys, memInfo.dwTotalPhys-memInfo.dwAvailPhys, memInfo.dwMemoryLoad);
    WriteLog(1,1,1, cTemp);
}

void InitGPIO( VOID )
{
//	Note this function only works for GPIO bits 105, 106 & 107
//	Bitsy Xb EIO7 = GPIO105
//	Bitsy Xb EIO8 = GPIO106
//	Bitsy Xb EIO9 = GPIO107

	LPVOID	m_hGPIO;
	DWORD	*ptr;

	m_hGPIO = MapAddress(GPIO_BASE);

	//Set GPIO Set Register bits to 1
	ptr = (DWORD*)m_hGPIO;
	ptr += (GPSR3_OFFSET / sizeof(DWORD));
	*ptr |= (0x07 << 9);	// set the 3 bits to 1
	DEBUGMSG(1,(TEXT("GPSR3 = 0x%8x \n\n"),*ptr));

	//Set the direction register to output
	ptr = (DWORD*)m_hGPIO;
	ptr += (GPDR3_OFFSET / sizeof(DWORD));
	*ptr |= (0x07 << 9);	// set the 3 direction bits to 1 for output
	DEBUGMSG(1,(TEXT("GPDR3 = 0x%8x \r\n"),*ptr));

	//Set GPIO alternate function bit
	ptr = (DWORD*)m_hGPIO;
	ptr += (GAFR3_L_OFFSET / sizeof(DWORD));
	*ptr &= ~(0x3F << 18);	// clear the AF bits (2 per GPIO bit) to 0 for GPIO (no alternate function)
	DEBUGMSG(1,(TEXT("GAFR3_L = 0x%8x \r\n"),*ptr));
}

void SR_GPIOBit(int GPBitNum, int value)
{
//	Note this function only works for GPIO bits 96 to 120
//	Bitsy Xb EIO7 = GPIO105
//	Bitsy Xb EIO8 = GPIO106
//	Bitsy Xb EIO9 = GPIO107

	int		iTemp;
	LPVOID	m_hGPIO;
	DWORD	*ptr;

	m_hGPIO = MapAddress(GPIO_BASE);

	//Set GPIO Set or Clear Register
	if (value == 1)
	{
		ptr = (DWORD*)m_hGPIO;
		ptr += (GPSR3_OFFSET / sizeof(DWORD));
		iTemp = GPBitNum - 96;  // find the right bit in the GPSR3 register (bit0 = GPIO96)
		*ptr |= (0x1 << iTemp);	// set the bit
		DEBUGMSG(1,(TEXT("GPSR3 = 0x%8x \n\n"),*ptr));
	} else
	{
		ptr = (DWORD*)m_hGPIO;
		ptr += (GPCR3_OFFSET / sizeof(DWORD));
		iTemp = GPBitNum - 96;  // find the right bit in the GPSR3 register (bit0 = GPIO96)
		*ptr |= (0x1 << iTemp);	// set the bit
		DEBUGMSG(1,(TEXT("GPCR3 = 0x%8x \n\n"),*ptr));
	}
}
