#include "all03.h"

/*
 * MPU Atmel AT89S51/52: parallel mode
 * based on AT89S8253 tool
 * 
 * Changelog
 * - 2019.09.21: 1.00 initial release
  *
 * TODO:
 * - add AT89S52
 * - test lock bits functions
 *
 */

#include <bios.h>

/* PARALLEL MODE */

#define _VERSION_		"1.00"

#define MODE_ERASE		0x05
#define MODE_WRITE		0x1E
#define MODE_READ		0x18 // 1C for AT89S8253 , 18 for AT89S51
#define MODE_LOCK_1		0x1F 
#define MODE_LOCK_2		0x07 
#define MODE_LOCK_3		0x0D 
#define MODE_LOCK_RD	0x0B 
#define MODE_SIGNATURE	0x00 // 17 for AT89S8253 , 00 for AT89S51

unsigned char _RST, _RDY, _PGM;
unsigned char _VCC, _VPP, _GND, _MODE[5], _DATA[8], _ADDR[16], _SEPARATE;

long _CODESIZE, _DATASIZE;
int _PP[5];

struct DEV {
	char *name;
	long Size, DataSize;		// ROM & EEPROM sizes in bytes
	char separate;				// 1:separated code and data space, 0:combined, data follows code
	int BlkSize, DataBlkSize;	// ROM & EEPROM pagesizes in bytes
	int progpulse[5];			// prog pulse length in us for 0:erase, 1:code, 2:data, 3:lock 4:fuses
	char rst,rdy,pgm;			// Pin numbers for RESET,RDY/BUSY and PROG
	char vcc,vpp,gnd,mode[5];	// Pin numbers for VCC, GND and MODE0..4
	char data[8];				// Pin numbers for d0..7
	char addr[16];				// Pin numbers for a0..15
	unsigned char UBmin, UBmax;	// min. and max. Vcc value
	unsigned char Upp;			// Vpp value
};

struct DEV ATMEL_devs[] = { //        C/D  prog   erase  code  data  lock  fuse          ale     ea
//			TYPE       ROMSIZE EESIZE sep blksize *----- progpulse[0-4] ------*  rst rdy pgm vcc vpp gnd * mode[0-4] -*  *----- data[0-7] -----+  +----------------- addr[0-15] -----------------+  Umn Umx Upp
/* 0*/	{ "AT89S51",   0x1000, 0x0000, 1,  1,  1,     1,    1,    1,    1,    1,  9, 10, 30, 40, 31, 20, 27,28,13,16,17, 39,38,37,36,35,34,33,32,  1, 2, 3, 4, 5, 6, 7, 8, 21,22,23,24, 0, 0, 0, 0, 40, 55, 120 }
// Note: EESIZE should be set to 0 in this version for AT89S51
};

struct {
	char name[20];
	int numdevs;
	struct DEV *dev;
} mfr[] = {
	{ "ATMEL", sizeof(ATMEL_devs)/sizeof(struct DEV), ATMEL_devs }
};

int mfrno = 0, devno = 0;
long signature;
int lock_bits = 0, fuse_bits = 0;

#define BUFSIZE		0x8000U

long addr;
long DevStart= 0x0000;
long Counter = 0x0000;

void ShowCounter( long val )
{
	struct text_info ti;

	gettextinfo( &ti );

	textattr( ( BLUE << 4 ) + WHITE );
	locate( 5, 73 ); cprintf( "%04lX", val );

	textattr( ti.attribute );
	gotoxy( ti.curx, ti.cury );
}

void ShowConfig( void )
{
	struct text_info ti;
	int idx;

	gettextinfo( &ti );

	textattr( ( BLUE << 4 ) + WHITE );
	locate( 8,61 );
	cprintf( "%06.6lX", signature );

	locate( 9,61 );
	//cprintf( "%01X", lock_bits );
	

	for (idx = 128; idx > 0; idx = idx/2)
	{
		cprintf( "%c", (lock_bits & (idx)) ? '1': '0' );
	}



	//EEPROM not present
        //locate( 2, 69 ); cprintf( "%04X", _DATASIZE );

	textattr( ti.attribute );
	gotoxy( ti.curx, ti.cury );
}

void ShowType( void )
{
	int i;
	
	if( mfrno < 0 || mfrno > sizeof(mfr) / sizeof(mfr[0]) )
		mfrno = 0;
	if( devno < 0 || devno >= mfr[mfrno].numdevs )
		devno = 0;

	_RST = mfr[mfrno].dev[devno].rst;
	_RDY = mfr[mfrno].dev[devno].rdy;
	_PGM = mfr[mfrno].dev[devno].pgm;

	_VCC = mfr[mfrno].dev[devno].vcc;
	_VPP = mfr[mfrno].dev[devno].vpp;
	_GND = mfr[mfrno].dev[devno].gnd;

	_CODESIZE = mfr[mfrno].dev[devno].Size;
	_DATASIZE = mfr[mfrno].dev[devno].DataSize;
	_SEPARATE = mfr[mfrno].dev[devno].separate;

	for( i = 0; i < sizeof(_MODE); ++i )
		_MODE[i]  = mfr[mfrno].dev[devno].mode[i];

	for( i = 0; i < sizeof(_DATA); ++i )
		_DATA[i]  = mfr[mfrno].dev[devno].data[i];

	for( i = 0; i < sizeof(_ADDR); ++i )
		_ADDR[i]  = mfr[mfrno].dev[devno].addr[i];

	for( i = 0; i < sizeof(_PP)/2; ++i )
		_PP[i]  = mfr[mfrno].dev[devno].progpulse[i];

	bufsize = _CODESIZE + _DATASIZE;
	BufEnd = bufsize - 1;

	textattr( ( BLUE << 4 ) | WHITE );

	locate( 0,40 ); cprintf( "*Mfr.: %s", mfr[mfrno].name );
	locate( 0,60 ); cprintf( "*TYPE: %s", mfr[mfrno].dev[devno].name );

	locate( 3,41 ); cprintf( "       end   addr.: %04lX", BufEnd );

	textattr( ( CYAN << 4 ) | WHITE );
}

int getdata( void )
{
	int i, val = 0;
	
	for( i = sizeof(_DATA)-1; i >= 0; --i )
		val = ( val << 1 ) | getpin( _DATA[i] );

	return val;
}

void setdata( int val )
{
	int i;
	
	for( i = 0; i < sizeof(_DATA); ++i )
	{
		setpin( _DATA[i], TTLID, val & 1 );
		val >>= 1;
	}
}

void setaddr( void )
{
	long a = addr;
	int i;

	if( _SEPARATE )
		a %= _CODESIZE;

	for( i = 0; i < sizeof(_ADDR); ++i )
	{
		setpin( _ADDR[i], TTLID, (int)(a & 1) );
		a >>= 1;
	}
}

void setmode( int md )
{
	int i;
	
	for( i = 0; i < sizeof(_MODE); ++i )
	{
		setpin( _MODE[i], TTLID, (int)(md & 1) );
		md >>= 1;	   
	}
}

void pulse( int pulselength )
{
	us_delay(2);

	setpin( _PGM, TTLID, 0 );
	us_delay( pulselength );
	setpin( _PGM, TTLID, 1 );

	us_delay(2);
}

int wait_busy( int maxwait )
{
	int t;

	maxwait *= 10;

	for( t = 0; t < maxwait; ++t )
	{
		if( getpin( _RDY ) )
			break;
		dly100u();
	}
	return getpin( _RDY ) ? 1 : 0;
}

void power( int voltage )
{
	int i;

	// we dont need VHH
	setdac( VHHID, 0 );										// VHH = 0V
	for( i = 0; i <= 4; ++i ) setport( VHHENID, i, 0 );		// no VHH
	setport( VHHENCID,0, 0 );								// no VHHC
	setport( VHHENCID,1, 0 );								// no VHHC

	// Can't use Pins 2,3,4,6,8,32..35,37..40 for Vop
	if( _VPP == 2 || _VPP == 3 || _VPP == 4 || _VPP == 6 || _VPP == 8 || ( _VPP > 31 && _VPP != 36 ) )
	{
		errbeep();
		textattr( ( RED << 4 ) | WHITE );
		locate( 41, 23 ); cprintf( "Connect pin 1 to %d", _VPP );
		textattr( ( CYAN << 4 ) | WHITE );
		_VPP = 1;											// force using Pin 1
	}

	setport( OTHERENID, 0, 0 );								// Pin 20 = GND

	if( voltage )
	{
		// Set all pins LOW
		for( i = 0; i < 5; ++i )
			setport( TTLID, i, 0 );

		setdac( VCCID, voltage );					// Vcc = xV
		setdac( VOPID, mfr[mfrno].dev[devno].Upp );	// Vpp = xV
		delay( 500 );							// wait to stabilize

		setpin( _VCC, VCCENID, 1 );				// set Vcc pin (automatically sets TTLID also)
		
		dly20u();
		
		setpin( _RST, TTLID, 1 );				// RST = H
		
		dly20u();

		setpin( 18, TTLID, 1 );					// start oscillator on pins 18/19 
		setpin( 19, TTLID, 1 );
        setport( OTHERENID, 0, 0x30 );			// with 4MHz
		
		dly50m();
		
		setpin( _VPP, TTLID, 1 );				// set EA = H
		setpin( _PGM, TTLID, 1 );				// set PGM = H

		setdata( 0xFF );						// set D0..7 = H
		setpin( _RDY, TTLID, 1 );				// set RDY = H (HiZ)

		dly20u();

		setpin( _VPP, VOPENID, 1 );				// set EA = Vpp 

		dly20u();
	}
	else
	{
		setpin( _VPP, VOPENID, 0 );				// remove Vpp
		setpin( _VPP, TTLID, 0 );

		dly20u();

        setport( OTHERENID, 0, 0 );				// osc off

		dly20u();

		setpin( _RST, TTLID, 0 );				// pull reset low

		dly20u();

		// Set all pins LOW except VCC
		for( i = 1; i < 40; ++i )
			if( i != _VCC )
				setpin( i, TTLID, 0 );

		dly20u();

		setpin( _VCC, VCCENID, 0 );				// remove Vcc
		setpin( _VCC, TTLID, 0 );

		setdac( VOPID, 0 );						// Vpp = 0V
		setdac( VCCID, 0 );						// Vcc = 0V
	}
}

int program( void )
{
	long end = _CODESIZE + _DATASIZE;
	int i, blksize;

	setmode( MODE_WRITE );
	blksize = mfr[mfrno].dev[devno].BlkSize;
	for( addr = BufStart; addr < end; )
	{
		/*
		if( _SEPARATE && addr == _CODESIZE )		// change area
		{
			setmode( MODE_WRITE_EE );
			blksize = mfr[mfrno].dev[devno].DataBlkSize;
		}
		*/

		if( ( addr & 0xFF ) == 0 )
			ShowCounter( addr );

		disable();
		for( i = 0; i < blksize; ++i )
		{
			setaddr();
			setdata( buffer[addr] );
			pulse( _PP[ (addr > _CODESIZE) ? 2 : 1 ] );
			++addr;
		}
		enable();
		
		dly1m();								// chip should have started programming after 1ms
		if( !wait_busy( 10*blksize ) )			// wait for completion (max. 10ms per byte)
			return 0;
	}

	ShowCounter( addr );
	return 1;
}

int read_verify_check( int md )
{
	long end = _CODESIZE + _DATASIZE;
	int val;
	
	setdata( 0xFF );		// release pin drivers on data pins

	setmode( MODE_READ );
	for( addr = BufStart; addr < end; ++addr )
	{
		/*
		if( _SEPARATE && addr == _CODESIZE )	// change area
			setmode( MODE_READ_EE );
		*/

		setaddr();
		if( ( addr & 0xFF ) == 0 ) ShowCounter( addr );

		val = getdata();
		
		switch( md )
		{
		case 0:		// read
			Chks += ( buffer[addr] = val );
			break;
			
		case 1:		// verify
			if( buffer[addr] != val )
			{
				ShowCounter( addr );
				return 0;
			}
			break;

		case 2:		// blank check
			if( val != 0xFF )
			{
				ShowCounter( addr );
				return 0;
			}
			break;
		}
	}

	ShowCounter( addr );
	return 1;
}

int write_config( void )
{
        if( lock_bits & 0x01 ) {
		setmode( MODE_LOCK_1 );
		pulse( _PP[3] );
		if( !wait_busy(50) )
			return 0;
	}
	
        if( lock_bits & 0x02 ) {
		setmode( MODE_LOCK_2 );
		pulse( _PP[3] );
		if( !wait_busy(50) )
			return 0;
	}
	
	
        if( lock_bits & 0x04 ) {
		setmode( MODE_LOCK_3 );
		pulse( _PP[3] );
		if( !wait_busy(50) )
			return 0;
	}
	
	
	/*
	
	setdata( fuse_bits & 0x0F );
	setmode( MODE_FUSE );
	pulse( _PP[4] );
	return wait_busy(50);
	*/
	return 1;
	
}

int read_config( void )
{
    long signature_temp;
        
	setdata( 0xFF );
	
	
	setmode( MODE_LOCK_RD );
	lock_bits = (getdata() >> 2) & 7;
/*	
	setmode( MODE_FUSE_RD );
	fuse_bits = getdata() & 0x0F;
*/
	setmode( MODE_SIGNATURE );
    addr = 0x0200L;
	setaddr();
	signature = getdata()&(0xFF);
	addr = 0x0100L;
	setaddr();
	signature += (getdata() << 8) & 0xFF00;
	addr = 0x0000L;
	setaddr();
    signature_temp = getdata();
    signature += ( signature_temp << 16) & 0xFF0000L;
	
	

	return 1;
}


int flash_check( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "   BLANK CHECK device:" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to check (Y/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );
		
		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Blank checking now... " );

		power(50); done = read_verify_check(2); power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Blank checking now... " );

		locate( 15, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Blank check error at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
		}
	}
}

void flash_program( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "   PROGRAM :" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to program (Y/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Programming now... " );

		power(50); done = program(); power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Programming now... " );

		locate( 15, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			setport( USERBITS, 0, 8 );
			cprintf( " OK !" );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Program error ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
		}
	}
}

void flash_protect( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 42 ); cprintf( " Program protect bits: " );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to program (Y/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Programming now... " );

		power(50); done = write_config(); power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Programming now... " );

		locate( 15, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			setport( USERBITS, 0, 8 );
			cprintf( " OK !" );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Program error ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
		}
	}
}

void flash_read( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "   READ to buffer :" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to start (Y/Even/Odd/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;

			case 'e':
			case 'E':
			case 'o':
			case 'O':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Reading now... " );

		Chks = 0;
		power(50);
		read_verify_check(0);
		read_config();
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Reading now... " );

		locate( 15, 41 );
		putchar( 7 );
		cprintf( " OK !" );

		textattr( ( BLUE << 4 ) | WHITE );
		locate( 4, 41 ); cprintf( "       Check Sum  : %04X", Chks );

		ShowConfig();
	}
}

void flash_verify( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "   VERIFY with buffer :" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to verify (Y/Even/Odd/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;

			case 'e':
			case 'E':
			case 'o':
			case 'O':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Verifying now @ VDDmin... " );

		power(mfr[mfrno].dev[devno].UBmin);
		done = read_verify_check(1);
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Verifying now @ VDDmin... " );

		locate( 15, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( " VERIFY ERROR ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
			continue;
		}

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 16, 41 ); cprintf( "Verifying now @ VDDmax... " );

		power(mfr[mfrno].dev[devno].UBmax);
		done = read_verify_check(1);
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 16, 41 ); cprintf( "Verifying now @ VDDmax... " );

		locate( 17, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( " VERIFY ERROR ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
		}
	}
}

void flash_erase( void )
{
	int done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "  EEPROM Erase:" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to erase (Y/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Erase now... " );

		power(50);
		setmode( MODE_ERASE );
		pulse( _PP[0] );
		done = wait_busy(500);
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( 14, 41 ); cprintf( "Erase now... " );

		locate( 15, 41 );
		if( done )
		{
			putchar( 7 );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( " ERROR ! " );
			textattr( ( CYAN << 4 ) | WHITE );

			locate( 21, 41 ); cprintf( "press any key to continue" );
			if( getch() == 0 ) getch();
		}

		clscrn( 13,41, 22,78 );
	}
}

void flash_auto( void )
{
	int done, l;

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "   AUTO :" );

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 13, 41 ); cprintf( "Ready to start (Y/Even/Odd/<CR>)? " );

		for( done = 0; !done; )
		{
			switch( getch() )
			{
			case 0:
				getch();
				break;

			case '\n':
			case '\r':
			case 0x1B:
				return;

			case 'y':
			case 'Y':
				done = 1;

			case 'e':
			case 'E':
			case 'o':
			case 'O':
				done = 1;
			}
		}

		clscrn( 14,41, 22,78 );

		setport( USERBITS, 0, 0 );

		l = 14;

		// BLANK CHECK

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( l, 41 ); cprintf( "Blank checking now... " );

		power(50); done = read_verify_check(2); power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( l++, 41 ); cprintf( "Blank checking now... " );

		locate( l++, 41 );

		if( done )
		{
			ShowCounter( BufEnd );
			cprintf( " OK !" );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Blank check error at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );

		// ERASE

			l -= 2;

			clscrn( l, 41, l+1, 78 );

			textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
			locate( l, 41 ); cprintf( "Erase now... " );

			power(50);
			setmode( MODE_ERASE );
			pulse( _PP[0] );
		   	done = wait_busy(500);
			power(0);

			textattr( ( CYAN << 4 ) | WHITE );
			locate( l++, 41 ); cprintf( "Erase now... " );

			locate( l++, 41 );

			if( done )
				cprintf( " OK !" );
			else
			{
				errbeep();
				textattr( ( RED << 4 ) | WHITE );
				cprintf( " ERROR" );
				textattr( ( CYAN << 4 ) | WHITE );

				continue;
			}
		}

		// PROGRAM

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( l, 41 ); cprintf( "Programming now... " );

		power(50); done = program(); power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( l++, 41 ); cprintf( "Programming now... " );

		locate( l++, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			cprintf( " OK !" );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Program error ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );

			continue;
		}

		// VERIFY

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( l, 41 ); cprintf( "VDD max verifying now..." );

		power(mfr[mfrno].dev[devno].UBmax);
		done = read_verify_check(1);
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( l++, 41 ); cprintf( "VDD max verifying now..." );

		locate( l++, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			putchar( 7 );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( " VERIFY ERROR ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
			continue;
		}

		textattr( BLINK | ( LIGHTGREEN << 4 ) | WHITE );
		locate( l, 41 ); cprintf( "VDD min verifying now..." );

		power(mfr[mfrno].dev[devno].UBmin);
		done = read_verify_check(1);
		power(0);

		textattr( ( CYAN << 4 ) | WHITE );
		locate( l++, 41 ); cprintf( "VDD min verifying now..." );

		locate( l, 41 );
		if( done )
		{
			ShowCounter( BufEnd );
			cprintf( " OK !" );
			setport( USERBITS, 0, 8 );
			putchar( 7 );
		}
		else
		{
			textattr( ( RED << 4 ) | WHITE );
			cprintf( " VERIFY ERROR ! at %04lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );
			errbeep();
		}
	}
}

void edit_config( void )
{
	int i, done;

	textattr( ( CYAN << 4 ) | WHITE );
	_window(  1,0, 10,39 );
	_window( 11,1, 24,78 );

	locate(  3, 1 ); cprintf( "Protection: " );
	
	/*
	locate(  5, 1 ); cprintf( "Serial Pgm: " );
	locate(  6, 1 ); cprintf( "x2 clock  : " );
	locate(  7, 1 ); cprintf( "UsrRow Pgm: " );
	locate(  8, 1 ); cprintf( "CrystalClk: " );
	*/
	locate( 14, 4 ); cprintf( "A : no protection" );
	locate( 14,43 ); cprintf( "B : MOVC protection" );
	locate( 15, 4 ); cprintf( "C : VERIFY protection" );
	locate( 15,43 ); cprintf( "D : EXT_EXEC protection" );

	/*
	locate( 17, 4 ); cprintf( "E : Serial Pgm Enable toggle" );
	locate( 18, 4 ); cprintf( "F : x2 Clock Enable toggle" );
	locate( 19, 4 ); cprintf( "G : UsrRow Pgm Enable toggle" );
	locate( 20, 4 ); cprintf( "H : Crystal Clock Enable toggle" );
	*/
	
	locate( 23, 4 ); cprintf( "Select options or <CR><ESC> to go back to the main menu ?" );

	textattr( ( BLUE << 4 ) | WHITE );
	locate(  1, 5 ); cprintf( " Configuration Bit Setting :" );
	locate( 11,28 ); cprintf( " Configuration Options :" );

	for(;;)
	{
		textattr( ( BLUE << 4 ) | WHITE );

		locate( 3, 13 );
		switch( lock_bits )
		{
                default : cprintf( "none" ); lock_bits = 0; break;
                case 1  : cprintf( "MOVC" ); break;
                case 3  : cprintf( "MOVC & VERIFY" ); break;
                case 7  : cprintf( "MOVC & VERIFY & EXT_EXEC" ); break;
		}
                /*
		for( i = 0; i < 4; ++i )
		{
			locate( 5 + i, 13 );
			cprintf( ( fuse_bits & ( 1 << i ) ) ? "disable" : "enable" );
		}
                */
		for( done = 0; !done; )
		{
			done = 1;

			switch( toupper( getch() ) )
			{
			case 0:	getch(); done = 0; break;

			case '\n':
			case '\r':
			case 0x1B: return;
			case 'A' : lock_bits = 7; break;
			case 'B' : lock_bits = 6; break;
			case 'C' : lock_bits = 4; break;
			case 'D' : lock_bits = 0; break;
			/*
			case 'E' : fuse_bits ^= 1; break;
			case 'F' : fuse_bits ^= 2; break;
			case 'G' : fuse_bits ^= 4; break;
			case 'H' : fuse_bits ^= 8; break;
			*/
			default  : done = 0;
			}
		}
	}	
}

void type_select( void )
{
	int done, i, num, len=15, left = 40;
	char no[10];

	if( ( num = mfr[mfrno].numdevs ) > 14 )
	{
		left = 0;
		for( i = len = 0; i < num; ++i )
			if( ( done = strlen( mfr[mfrno].dev[i].name ) ) > len )
				len = done;
	}

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,left, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, left+5 ); cprintf( "  TYPE SELECT:" );

	textattr( ( CYAN << 4 ) | WHITE );

	for( i = 0; i < num; ++i )
	{
		locate( 13+(i%7), left + 1 + (i/7)*(len+4) );
		cprintf( "%d.%s", i, mfr[mfrno].dev[i].name );
	}

	locate( 21, left+1 ); cprintf( "<CR> back to main menu." );
	locate( 22, left+1 ); cprintf( "SELECT NUMBER ?" );

	for(;;)
	{
		for( no[0] = done = 0; !done; )
		{
			locate( 22, left+16 ); cprintf( "%s ", no );
			locate( 22, left+16+strlen(no) );

			switch( i = getch() )
			{
			case 0:
				getch();
				break;

			case 8:
				if( ( i = strlen(no) ) != 0 )
					no[i-1] = 0;
				break;

			case '\n':
			case '\r':
				if( strlen( no ) &&
					( i = atoi(no) ) >= 0 && i < mfr[mfrno].numdevs )
				{
					devno = i;
					ShowType();
					return;
				}
				break;

			case 0x1B:
				return;

			default:
				if( isdigit( i ) )
					strcat( no, (char*)&i );
				break;
			}
		}
	}
}

void mfr_select( void )
{
	int done, i;
	char no[10];

	textattr( ( CYAN << 4 ) | WHITE );
	_window( 12,40, 23,79 );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 12, 45 ); cprintf( "  MFR SELECT:" );

	textattr( ( CYAN << 4 ) | WHITE );

	for( i = 0; i < sizeof(mfr) / sizeof(mfr[0]); ++i )
	{
		locate( 13+i, 41 ); cprintf( "%d.%s", i, mfr[i].name );
	}

	locate( 21, 41 ); cprintf( "<CR> back to main menu." );
	locate( 22, 41 ); cprintf( "SELECT NUMBER ?" );

	for(;;)
	{
		for( no[0] = done = 0; !done; )
		{
			locate( 22, 56 ); cprintf( "%s ", no );
			locate( 22, 56+strlen(no) );

			switch( i = getch() )
			{
			case 0:
				getch();
				break;

			case 8:
				if( ( i = strlen(no) ) != 0 )
					no[i-1] = 0;
				break;

			case '\n':
			case '\r':
				if( strlen( no ) &&
					( i = atoi(no) ) >= 0 && i < sizeof(mfr) / sizeof(mfr[0]) )
				{
					mfrno = i;
					if( devno >= mfr[mfrno].numdevs )
						devno = 0;
					ShowType();
					return;
				}
				break;

			case 0x1B:
				return;

			default:
				if( isdigit( i ) )
					strcat( no, (char*)&i );
				break;
			}
		}
	}
}

/*=========================================================*/
int main( void )
{
	int redraw, first = 1;
	long tmpval;

	/*---------------------------------------------------------*/
	/* main program starts here                                */
	/*---------------------------------------------------------*/

	getcwd( oldpath, 260 );
	strcpy( path, oldpath );

	if( ( buffer = farmalloc( BUFSIZE ) ) == NULL )
		return -1;
	memset( (void far *)buffer, 0, BUFSIZE );

	ReadConfig();
	delay(0);

	for( ;; )
	{
		if( first )
		{
			first = 0;

			init_hw();
			initdacs();
			setport( USERBITS, 0, 0 );
		}

		textattr( ( LIGHTGRAY << 4 ) | YELLOW ); clscrn( 0,0, 24,79 );

		locate( 0,0 ); cprintf( "Universal   Programmer" );
		locate( 1,0 ); cprintf( "MODEL: PC Based" );
		locate( 2,0 ); cprintf( "MPU AT89S51/52 section " _VERSION_ );

		textattr( ( BLUE << 4 ) + WHITE ); clscrn( 0,40, 6,79 );

		ShowType();

		textattr( ( BLUE << 4 ) + WHITE ); _window( 1,40, 6,79 );
		locate( 1,53 ); cprintf( " TARGET ZONE " );
		locate( 2,41 ); cprintf( "Buffer start addr.: %04lX", BufStart );
		locate( 3,41 ); cprintf( "       end   addr.: %04lX", BufEnd );
		locate( 4,41 ); cprintf( "       Check Sum  : %04X", Chks );
		locate( 5,41 ); cprintf( "Device start addr.: %04lX", DevStart );

		_window( 3,69, 6,79 );
		locate( 4,71 ); cprintf( "COUNTER" );
		ShowCounter( 0 );

		_window( 7,40, 10,79 );
		locate( 7,43 ); cprintf( " Device ID & Configuration bits " );
		locate( 8,42 ); cprintf( "Manuf./Device ID : " );
		locate( 9,42 ); cprintf( "Lock bits        : " );
		ShowConfig();

		textattr( ( CYAN << 4 ) | WHITE ); clscrn( 3,0, 23,38 );

		locate( 3,0 ); cprintf( "-------------  Main Menu  -------------" );
		locate( 4,0 ); cprintf( "1. DOS SHELL                           " );
		locate( 5,0 ); cprintf( "2. Load BIN or HEX file to buffer      " );
		locate( 6,0 ); cprintf( "3. Save buffer to disk                 " );
		locate( 7,0 ); cprintf( "4. Edit buffer     7. Display buffer   " );
		locate( 8,0 ); cprintf( "5. Change I/O base address             " );
		locate( 9,0 ); cprintf( "6. Display loaded file history         " );
		locate(10,0 ); cprintf( "W. Swap hi-low bytes in buffer         " );
		locate(11,0 ); cprintf( "T. Type select     Z. Target zone      " );
		locate(12,0 ); cprintf( "B. Blank check     D. Display          " );
		if( _DATASIZE != 0 ) {
		locate(13,0 ); cprintf( "P. Program (Program Mem & Data Mem)    " );
		locate(14,0 ); cprintf( "A. Auto(B&S&P&V&L)                     " );
		locate(15,0 ); cprintf( "S. Erase Program & Data memory         " );
		} else {
		locate(13,0 ); cprintf( "                                       " );
		locate(14,0 ); cprintf( "P. Program         A. Auto(B&S&P&V&L)  " );
		locate(15,0 ); cprintf( "S. Erase Program memory                " );
		}
		locate(16,0 ); cprintf( "R. Read            V. Verify           " );
		locate(17,0 ); cprintf( "                                       " );
                locate(18,0 ); cprintf( "E. Configure protect bits              " );
                locate(19,0 ); cprintf( "L. Program protect bits                " );
		locate(20,0 ); cprintf( "Q. Quit                                " );
		locate(21,0 ); cprintf( "---------------------------------------" );
		locate(22,0 ); cprintf( "Allocation Buffer size : %uK bytes", BUFSIZE/1024 );
		if( _DATASIZE != 0 ) {
		locate(23,0 ); cprintf( "Data memory buffer at %04lX ~ %04lX", _CODESIZE, _CODESIZE + _DATASIZE - 1 );
		}

		for( redraw = 0; !redraw; )
		{
			int c;
			
			textattr( ( BLUE << 4 ) + WHITE ); clscrn( 24,0, 24,38 );

			locate( 24,0 ); cprintf( "Select function ? " );

			for(;;)
			{
				if( ( c = getch() ) != 0 )
					break;
				getch();				 /* neglect extended code */
			}

			switch( c = toupper(c) )
			{
			case '1': dos_shell( "" ); redraw = 1; break;
			case '2': tmpval = bufsize; bufsize = 0x8000;
					  memset( (void far *)buffer, 0, BUFSIZE );		// clear buffer
					  addr = 0;
					  load_file();
					  bufsize= tmpval;
					  redraw = 1; break;
			case '3': save_file(); break;
			case '4': tmpval = bufsize; bufsize = 0x8000;
					  addr = 0;
					  edit_buffer();
					  bufsize= tmpval;
					  redraw = 1; break;
			case '5': set_io_adr(); first = redraw = 1; break;
			case '7': disp_buffer(); redraw = 1; break;

			case 'M': mfr_select(); redraw = first = 1; break;
			case 'T': type_select(); redraw = first = 1; break;
			case 'E': edit_config(); redraw = 1; break;

			case 'R': flash_read(); break;
			case 'B': flash_check(); break;
			case 'S': flash_erase(); break;
			case 'P': flash_program(); break;
			case 'V': flash_verify(); break;
			case 'L': flash_protect(); break;
			case 'A': flash_auto(); break;

//			case '\n':
//			case '\r': redraw = 1; break;		// refresh
			}

			setport( USERBITS, 0, 0 );

			if( c == 'Q' )
			{
				WriteConfig();
				textattr( LIGHTGRAY ); clrscr();
				chdir( oldpath );
				if( buffer ) farfree( (void far *)buffer );
				return( 0 );
			}

			textattr( ( LIGHTGRAY << 4 ) | YELLOW ); clscrn( 11,40, 23,79 );
		}
	}
}

