/***********************************************************************
	userblock_clear.c
	Z-World, 2002

	This program clears the contents of the user block.  Note that it
	does NOT clear the calibration constants or the system ID block.
	After writing, the program checks to make sure fill pattern is there.

	See userblock_info.c for general information about your board's
	installed User Block.

	Note:  In Debug mode, there is a 300 millisecond timer delay after
	       each writeUserBlock call to allow Dynamic C and the target
	       controller to exchange a debug packet, in order to inform the
	       debug kernel that the target is still alive.  The timer delay
          is not necessary in nodebug Run mode or when single-stepping.

***********************************************************************/

#class auto

// If it exists, use the Z-World reserved size that is explicitly set
//  for this controller in its hardware-specific library.
#ifndef ZWORLD_RESERVED_SIZE
// Otherwise, reserve 2K bytes of space below top of ID/User Blocks.
// This may be overprotective for boards with no calibration constants
//  or other Z-World installed information, but, better safe than sorry!
#define ZWORLD_RESERVED_SIZE 0x0800
#endif

#define CLEAR_VALUE 0xFF

// local function prototypes
void delay_ms(unsigned long delay_mS);
void lockup(void);

void main(void)
{
	static char buffer[4096];			// Our 4K stack will blow if "auto"
	auto int err;
   auto unsigned ub_rwcheck, ub_rwsize;
	auto unsigned long available_size, ub_rwaddr;

	// Initialize the buffer
	memset(buffer, CLEAR_VALUE, sizeof(buffer));

	// first, get the total User block size
	//  (NB:  possibly combined with the ID block)
	available_size = GetIDBlockSize() * 4096ul;

	// next, protect the User Block as necessary
	if (!ZWORLD_RESERVED_SIZE) {
		// no reserved User block area, but combined ID+User blocks?
		if (1 < SysIDBlock.tableVersion) {
			// yes, combined ID+User blocks, so subtract the ID block's size
			available_size -= (unsigned long) sizeof(SysIDBlock);
		} else {
			// no, the ID and User blocks are separate, so nothing to do!
			//  (ie:  User block size is a multiple of 4 KB and fully available)
		}
	}
	else if (available_size > (unsigned long) (ZWORLD_RESERVED_SIZE)) {
		// protect the calibration constants, etc.
		available_size -= ZWORLD_RESERVED_SIZE;
	} else {
		// some error has occurred, protect the entire User Block!
		available_size = 0ul;
	}

	printf("The available User Block size is 0x%08LX (%ld) bytes.\n",
	       available_size, available_size);
	printf("Z-World's (possibly assumed) reserved size is 0x%08LX (%ld) bytes.\n\n",
	       (unsigned long) (ZWORLD_RESERVED_SIZE),
	       (unsigned long) (ZWORLD_RESERVED_SIZE));

	/*  Write a pattern into the User Block: */
	ub_rwaddr = 0ul;
	while (ub_rwaddr < available_size) {
		if ((ub_rwaddr + sizeof(buffer)) <= available_size) {
			ub_rwsize = sizeof(buffer);
		} else {
			ub_rwsize = (unsigned) (available_size - ub_rwaddr);
		}
		err = writeUserBlock((unsigned) ub_rwaddr, buffer, ub_rwsize);
		if (OPMODE & 0x08) {
			delay_ms(300ul);
		}
		if (err) {
			printf("Error %d:  Pattern write failed, offset = 0x%08LX.\n",
			       err, ub_rwaddr);
			lockup();
		} else {
			printf("Wrote 0x%04X bytes to User Block, offset = 0x%08LX.\n",
			       ub_rwsize, ub_rwaddr);
		}
		ub_rwaddr += ub_rwsize;
	}

	printf("\n");

	/*  Now read back and ensure the pattern was written, abort if not: */
	ub_rwaddr = 0ul;
	while (ub_rwaddr < available_size) {
		if ((ub_rwaddr + sizeof(buffer)) <= available_size) {
			ub_rwsize = sizeof(buffer);
		} else {
			ub_rwsize = (unsigned) (available_size - ub_rwaddr);
		}
		memset(buffer, ~CLEAR_VALUE, sizeof(buffer));
		err = readUserBlock(buffer, (unsigned) ub_rwaddr, ub_rwsize);
		if (err) {
			printf("Error %d:  Pattern read failed, offset = 0x%08LX.\n",
			       err, ub_rwaddr);
			lockup();
		} else {
			printf("Read 0x%04X bytes from User Block, offset = 0x%08LX.\n",
			       ub_rwsize, ub_rwaddr);
		}
		for (ub_rwcheck = 0; ub_rwcheck < ub_rwsize; ub_rwcheck++) {
			if (buffer[ub_rwcheck] != (char) CLEAR_VALUE) {
				printf("Error:  Pattern didn't stick, offset = 0x%08LX.\n",
				       ub_rwaddr + ub_rwcheck);
				lockup();
			}
		}
		ub_rwaddr += ub_rwsize;
	}

	if (available_size > 0L) {
		printf("\n\nAvailable User Block area cleared!\n");
	} else {
		printf("\n\nDid not attempt to clear the available User Block area!\n");
		printf("Either no User Block installed, or bad User Block information!\n");
		printf("Recommend running the userblock_info.c sample program.\n");
	}
   lockup();
}

void delay_ms(unsigned long delay_mS)
{
	auto unsigned long delay_end;

	delay_end = MS_TIMER + delay_mS;
	// OK to return early if MS_TIMER + delay_mS wraps
	while ((MS_TIMER < delay_end) && (delay_end >= delay_mS));
}

void lockup(void)
{
	printf("\n\nLocking up to prevent repetitive flash write attempts.\n");
	printf("In Debug mode, press Ctrl-Q and then Ctrl-F2 to reset this program.\n");
	for(;;);
}