#include "all03.h"

/*
 * Flash section: 29Fxx0
 * 
 * Changelog
 * - 2003.10.07: 1.00 initial release
 * - 2019.09.21: 1.0D changed addressing from 0x555 and 0x2AA to 0x5555 and 0x2AAA, introduced "safe mode" with additional delays, introduced optional "data/!data polling" on DQ7 bit during program, introduced different programming algorithms, introduce SST flash models
 *
 * TODO:
 * - data protection/security functions
 * - other flash models
 * - speed improvements in flash programming (using "original" Hi-Lo sw set as Atmel AT49F020 is much faster on SST39SF020A...)
 *
 */

#define _VERSION_		"1.0D"

// AMD AM29{F,LV,BV}0{1,2,4}0/A/B
//
// MFG ID  AA->5555  55->2AAA  90->5555  X00->id (01)
// DEV ID  AA->5555  55->2AAA  90->5555  X01->id (6E)
// SECERA  AA->5555  55->2AAA  80->5555  AA->5555  55->2AAA  30->aaaaa
//
// PROG    5555,AA  2AAA,55  5555,A0  aaaaa,dd
// CHPERA  5555,AA  2AAA,55  5555,80  5555,AA  2AAA,55  5555,10
//

//0.4  5: A18		VCC  :36 4.3
//0.5  6: A16		/WE	 :35 4.2
//0.6  7: A15		A17  :34 4.1
//0.7  8: A12		A14	 :33 4.0
//1.0  9: A7		A13	 :32 3.7
//1.1 10: A6		A8	 :31 3.6
//1.2 11: A5		A9	 :30 3.5 VID=+12V (from VOP) \	SECTOR (UN)PROTECT
//1.3 12: A4		A11	 :29 3.4					  + 
//1.4 13: A3		/OE	 :28 3.3 VID=+12V (from VOP) /  SECTOR PROTECT VERIFY
//1.5 14: A2		A10	 :27 3.2
//1.6 15: A1		/CE	 :26 3.1
//1.7 16: A0		D7	 :25 3.0
//2.0 17: D0		D6	 :24 2.7
//2.1 18: D1		D5	 :23 2.6
//2.2 19: D2		D4	 :22 2.5
//    20: GND		D3	 :21 2.4


#define _CE		26
#define _OE		28
#define _WE		35

static char apin[] = { 16,15,14,13,12,11,10,9,31,30,27,29,8,32,33,7,6,34,5 };
static char dpin[] = { 17,18,19,21,22,23,24,25 };

struct DEV {
	char name[20];
	long Size;
	char VCC;
	char algo[6];
};

struct DEV AMD_devs[] = {
	{ "Am29LV010/A/B",      0x20000L, 33, "TOUT" },
	{ "Am29LV020/A/B",      0x40000L, 33, "TOUT" },
	{ "Am29LV040/A/B",      0x80000L, 33, "TOUT" },
	{ "Am29F010",           0x20000L, 50, "TOUT" },
	{ "Am29F020",           0x40000L, 50, "TOUT" },
	{ "Am29F040",           0x80000L, 50, "TOUT" }
};

struct DEV MXIC_devs[] = {
	{ "MX29F010",   0x20000L, 50, "TOUT" },
	{ "MX29F020",   0x40000L, 50, "TOUT" },
	{ "MX29F040",   0x80000L, 50, "TOUT" }
};

struct DEV SST_devs[] = {
	{ "SST39SF010", 0x20000L, 50, "BASIC" },
	{ "SST39SF020", 0x40000L, 50, "BASIC"  },
	{ "SST39SF040", 0x80000L, 50, "BASIC"  },
	{ "SST39SF010A",        0x20000L, 50, "BASIC"  },
	{ "SST39SF020A",        0x40000L, 50, "BASIC"  },
	{ "SST39SF040A",        0x80000L, 50, "BASIC"  }
};

struct {
	char name[20];
	int numdevs;
	struct DEV *dev;
} mfr[] = {
	{ "AMD/MMI", sizeof(AMD_devs)/sizeof(struct DEV), AMD_devs },
	{ "MXIC", sizeof(MXIC_devs)/sizeof(struct DEV), MXIC_devs },
	{ "SST", sizeof(SST_devs)/sizeof(struct DEV), SST_devs }
};

int mfrno = 0, devno = 0;

char safe_mode = 0;

long DevStart= 0L;
long Counter = 0L;
long lastaddr;

void ShowCounter( long val )
{
	struct text_info ti;

	gettextinfo( &ti );

	textattr( ( BLUE << 4 ) + WHITE );
	locate( 8,71 ); cprintf( " %05lX ", val );

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

void power( int state )
{
	int i;

	// we dont need these...
	setdac( VHHID, 0 );					// VHH = 0V
	setdac( VOPID, 0 );					// VOP = 0V

	setport( OTHERENID, 0, 0 );			// Pin20 = GND

	setport( VHHENCID,0, 0 );			// no VHHC
	setport( VHHENCID,1, 0 );			// no VHHC

	setport( VCCENID, 0, 0 );			// no VCC
	setport( VCCENID, 1, 0 );			// no VCC

	for( i = 0; i <= 4; ++i )
	{
		setport( VOPENID, i, 0 );		// no VOP
		setport( VHHENID, i, 0 );		// no VHH
		setport( TTLID,   i, 0xFF );	// TTL hi on all pins
	}

	lastaddr = -1L;

	// VCC is pin 32 (programmer socket pin 36)

	if( state )
	{
		setdac( VCCID, mfr[mfrno].dev[devno].VCC );	// VCC = 3.3/5.0V
		waitms( 100 );
		setpin( 36, VCCENID, 1 );
	}
	else
	{
		setdac( VCCID, 0 );				// VCC = 0V
		waitms( 100 );
		setpin( 36, VCCENID, 0 );
	}
}

void setaddr( unsigned long addr )
{
	register unsigned char i;
	register unsigned a, b;

	a = (unsigned)( addr & 0xFFFF );
	b = (unsigned)( lastaddr & 0xFFFF );

	for( i = 0; i < 16; ++i )		// A0..A15
	{
		if( ( a & 1 ) != ( b & 1 ) )
			setpin( apin[i], TTLID, a & 1 );
		a >>= 1; b >>= 1;
	}

	a = (unsigned)( addr >> 16 );
	b = (unsigned)( lastaddr >> 16 );

	for( i = 16; i < 19; ++i )		// A16..A18
	{
		if( ( a & 1 ) != ( b & 1 ) )
			setpin( apin[i], TTLID, a & 1 );
		a >>= 1; b >>= 1;
	}

	lastaddr = addr;
}

void f_write( long addr, register unsigned char d )
{
	register int i;

	setaddr( addr );

	for( i = 0; i < 8; ++i )
	{
		setpin( dpin[i], TTLID, d & 1 );
		d >>= 1;
	}
	
        // tas = 0, no delay
	
	setpin( _OE, TTLID, 1 );
	setpin( _CE, TTLID, 0 );
	setpin( _WE, TTLID, 0 );

        // should have a small delay, trying without it...
	if (safe_mode) dly1u();

	setpin( _WE, TTLID, 1 );
	setpin( _CE, TTLID, 1 );
}

void f_prepare( void )
{
	int i;

	for( i = 0; i < 8; ++i )
		setpin( dpin[i], TTLID, 1 );	// set D0..D7 to 1 for reading
}

unsigned char f_read( void )
{
	register unsigned char d = 0;
	register int i;

	setpin( _WE, TTLID, 1 );
	setpin( _CE, TTLID, 0 );
	setpin( _OE, TTLID, 0 );

	for( i = 7; i >= 0; --i )
		d = ( d << 1 ) | getpin( dpin[i] );

	setpin( _OE, TTLID, 1 );
	setpin( _CE, TTLID, 1 );
	
	return d;
}

int do_erase( void )
{
	unsigned char d, n;
	int i;

	for( i = 0; i < 2; ++i )
	{
		f_write( 0x5555L, 0xAA );
		f_write( 0x2AAAL, 0x55 );
		f_write( 0x5555L, 0x80 );

		f_write( 0x5555L, 0xAA );
		f_write( 0x2AAAL, 0x55 );
		f_write( 0x5555L, 0x10 );

		f_prepare();

		d = f_read();

		while( ( ( n = f_read() ) ^ d ) & 0x40 )		// while D6 toggles
		{
			d=n;
			if (mfrno < 2)  // AMD or MXIC
			{
				
				if( ( d ) & 0x20 )						// D5=1 : timeout
				{
					if( ( f_read() ^ f_read() ) & 0x40 )	// still toggling
					{
						f_write( 0L, 0xF0 );				// reset device
						f_prepare();
						waitms( 10 );
						return 0;							// failure
					}
					break;									// just in time
				}
			}
			
		}
	}
	return 1;
}

int do_sector_erase( int block )
{
	unsigned char d, n;
	int i;

	for( i = 0; i < 2; ++i )
	{
		f_write( 0x5555L, 0xAA );
		f_write( 0x2AAAL, 0x55 );
		f_write( 0x5555L, 0x80 );

		f_write( 0x5555L, 0xAA );
		f_write( 0x2AAAL, 0x55 );

		f_write( block * (bufsize/8), 0x30 );

		f_prepare();

		d = f_read();

		while( ( ( n = f_read() ) ^ d ) & 0x40 )		// while D6 toggles
		{
			d=n;
			if (mfrno < 2)  // AMD or MXIC
			{
				
				if( ( d) & 0x20 )						// D5=1 : timeout
				{
					if( ( f_read() ^ f_read() ) & 0x40 )	// still toggling
					{
						f_write( 0L, 0xF0 );				// reset device
						f_prepare();
						waitms( 10 );
						return 0;							// failure
					}
					break;									// just in time
				}
			}
		}
	}
	return 1;
}	

int do_program( long addr, unsigned char val )
{
	unsigned char d, n;
	
	f_write( 0x5555L, 0xAA );
	f_write( 0x2AAAL, 0x55 );
	f_write( 0x5555L, 0xA0 );
	

	f_write( addr, val );

	f_prepare();

	d = f_read();

	while( ( ( n = f_read() ) ^ d ) & 0x40 )		// while D6 toggles
	{
		d=n;
		if (mfrno < 2)  // AMD or Macronix
		{
				
			if( ( d ) & 0x20 )						// D5=1 : timeout
			{
				if( ( f_read() ^ f_read() ) & 0x40 )	// still toggling
				{
					f_write( 0L, 0xF0 );				// reset device
					f_prepare();
					waitms( 10 );
					return 0;							// failure
				}
				n = f_read();
				break;									// just in time
			}
		}
		
		
	}

	// this will check DQ7 to read as "data" and not "!data", present in both AMD, Macornix and SST. 
	if ( safe_mode ){
			// add a small delay, should be 150ns minimum?
			dly1u();

			n = f_read();

			if( ( n ^ val ) & 0x80 )								// data is not OK (D7 still complemented)
			{
					f_write( 0L, 0xF0 );                            // reset device
					f_prepare();
					waitms( 10 );
					return 0;                                       // failure

			}
	}

	return (n == val);
}

void ShowProtect( void )
{
	int i,v;

	// A16..A14 = sector#
	// A13..A10 = x -> 0
	// A9       = Vid (12V)
	// A8 .. A7 = x	-> 0
	// A6       = 0
	// A5 .. A2 = x	-> 0
	// A1 .. A0 = 2
	textattr( ( LIGHTGRAY << 4 ) | WHITE );
	locate( 24, 41 ); cprintf( "*Protect bit[7..0]: " );
	
	if (mfrno < 2){		// AMD or Macronix
		

		for( i = 7; i >= 0; --i )
		{
			setaddr( 0x00202L | 0x04000L * i );	// A9=1, A1=1, all others=0

			setpin( 30, VOPENID, 1 );			// A9 = Vid (12V)
			v = f_read();
			setpin( 30, VOPENID, 0 );

			cprintf( "%c", !v ? '0':'1' );
		}
	}else{
		
		cprintf( "Not supported" );
	}


}

void ShowDevice( void )
{
	int mfr,dev;

	// A16..A14 = x
	// A13..A10 = x -> 0
	// A9       = Vid (12V)
	// A8 .. A7 = x	-> 0
	// A6       = 0
	// A5 .. A2 = x	-> 0
	// A1 .. A0 = 0:MFGID, 1:DEVID

	power(1);
/*

	setaddr( 0x00200L );			// A9=1, all others=0

	setpin( apin[9], VOPENID, 1 );	// A9 = Vid (12V)
	mfr = f_read();
	setpin( apin[9], VOPENID, 0 );

	setpin( apin[0], TTLID, 1 );	// A0=1
	setpin( apin[9], VOPENID, 1 );	// A9 = Vid (12V)
	dev = f_read();
	setpin( apin[9], VOPENID, 0 );

*/
	
	f_write( 0x5555L, 0xAA );
	f_write( 0x2AAAL, 0x55 );
	f_write( 0x5555L, 0x90 );
	f_prepare();
	setaddr( 0L ); mfr = f_read();

	f_write( 0x5555L, 0xAA );
	f_write( 0x2AAAL, 0x55 );
	f_write( 0x5555L, 0x90 );
	f_prepare();
	setaddr( 1L ); dev = f_read();


	power(0);

	
	//textattr( ( LIGHTGRAY << 4 ) | WHITE );
	//locate( 24, 41 ); cprintf( "*MFR/DEV : %02X/%02X", mfr, dev );

	textattr( ( BLUE << 4 ) | WHITE );
	locate( 2, 40 ); cprintf( "*MFR/DEV ID: %02X/%02X", mfr, dev );
}

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

	bufsize = mfr[mfrno].dev[devno].Size;
	BufEnd = bufsize - 1;

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

	locate( 0,40 ); cprintf( "*Mfr.: %s", mfr[mfrno].name );
	locate( 1,40 ); cprintf( "*TYPE: %s", mfr[mfrno].dev[devno].name );
	locate( 2,40 ); cprintf( "*MFR/DEV ID: ");

	locate( 0,62 ); cprintf( "*SAFE MODE: %s", (safe_mode ? "YES" : "NO") );
        locate( 1,62 ); cprintf( "*PGMALGO: %s", mfr[mfrno].dev[devno].algo );
	locate( 2,62 ); cprintf( "*VCP :%3.1fV", 0.1 * mfr[mfrno].dev[devno].VCC );

	locate( 6,41 ); cprintf( "       end   addr.: %05lX", BufEnd );

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

int flash_check( void )
{
	int done;
	long addr;

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

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

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 12, 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( 13,41, 22,78 );
		
		power(1);
		setport( USERBITS, 0, 0 );

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

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			setaddr( addr );
			if( ( done = f_read() ) != 0xFF )
				break;
		}

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

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

		power(0);
	}
}

void flash_program( void )
{
	int done;
	long addr;
	FILE *f;

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

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

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 12, 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( 13,41, 22,78 );

		setport( USERBITS, 0, 0 );
		power(1);

		if( !buffer && ( f = fopen( buffile, "rb" ) ) == NULL )
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			locate( 13, 41 ); cprintf( "File open error !" );
			textattr( ( CYAN << 4 ) | WHITE );
			delay(1000);
			break;
		}

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

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			if( !do_program( addr, ( buffer ? buffer[addr] : getc(f) ) ) )
				break;
		}

		if( !buffer ) fclose( f );

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

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

		power(0);
	}
}

void flash_read( void )
{
	int done;
	long addr;
	unsigned char val;
	FILE *f;

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

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

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 12, 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( 13,41, 22,78 );

		ShowDevice();
		ShowProtect();

		setport( USERBITS, 0, 0 );
		power(1);

		if( !buffer && ( f = fopen( buffile, "wb" ) ) == NULL )
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			locate( 13, 41 ); cprintf( "File open error !" );
			textattr( ( CYAN << 4 ) | WHITE );
			delay(1000);
			break;
		}

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

		Chks = 0;

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			setaddr( addr );

			Chks += ( val = f_read() );

			if( buffer ) buffer[ addr ] = val;
			else putc( val, f );
		}
		ShowCounter( BufEnd );

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

		if( !buffer ) fclose( f );

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

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

		power(0);
	}
}

void flash_verify( void )
{
	int done;
	long addr;
	FILE *f;

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

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

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 12, 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( 13,41, 22,78 );

		setport( USERBITS, 0, 0 );
		power(1);

		if( !buffer && ( f = fopen( buffile, "rb" ) ) == NULL )
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			locate( 13, 41 ); cprintf( "File open error !" );
			textattr( ( CYAN << 4 ) | WHITE );
			delay(1000);
			break;
		}

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

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			setaddr( addr );

			if( ( ( buffer ? buffer[addr] : getc(f) ) & 0xFF ) != f_read() )
				break;
		}

		if( !buffer ) fclose( f );

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

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

		power(0);
	}
}

void flash_erase( void )
{
	int i, done, sel=8, er=0xFF;

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

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

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

		for( done = 0; !done; )
		{
			for( i = 0; i < 9; ++i )
			{
				locate( 12+i, 45 );
				if( i == sel )
				{
					textattr( ( RED << 4 ) | WHITE );
					cprintf( "> " );
				}
				else
				{
					textattr( ( CYAN << 4 ) | WHITE );
					cprintf( "  " );
				}

				if( i == 8 )
					cprintf( "[ 9 ] ALL            %s",
						( er == 0xFF ) ? "erase":"     " );
				else
					cprintf( "[ %d ] %05lX - %05lX  %s",
						i+1, i * (bufsize/8), (i+1) * (bufsize/8) - 1,
						( er & (1<<i) ) ? "erase":"     " );
			}

			textattr( ( CYAN << 4 ) | WHITE );
			locate( 21, 41 ); cprintf( "press space to select erase status" );
			locate( 22, 41 ); cprintf( "press <CR> to go to main menu" );

			switch( i = getch() )
			{
			case 0:
				switch( getch() )
				{
				case 72:	// UP
					if( sel > 0 )
						--sel;
					break;

				case 80:	// DOWN
					if( sel < 8 )
						++sel;
					break;
				}
				break;

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

			case 0x20:
				if( sel == 8 )
				{
					if( er == 0xFF )
						er = 0;
					else
						er = 0xFF;
				}
				else
					er ^= ( 1 << sel );
				break;

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

			default:
				if( i >= '1' && i <= '8' )
					er ^= ( 1 << (i-'1') );
				else if( i == '9' )
				{
					if( er == 0xFF )
						er = 0;
					else
						er = 0xFF;
				}
				break;
			}
		}

		if( !er ) continue;			// nothing to do

		clscrn( 12,41, 22,78 );

		setport( USERBITS, 0, 0 );
		power(1);

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

		if( er == 0xFF )
			done = do_erase();
		else
		{
			textattr( ( CYAN << 4 ) | WHITE );

			for( i = 0; i < 8; ++i )
			{
				if( er & ( 1<<i ) )
				{
					locate( 20, 41 ); cprintf( "Block : %d", i+1 );

					if( ( done = do_sector_erase(i) ) == 0 )
						break;
				}
			}
		}

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

		locate( 13, 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( 11,41, 22,78 );

		power(0);
	}
}

void flash_auto( void )
{
	int done, l;
	long addr;
	FILE *f;

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

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

	for(;;)
	{
		textattr( ( CYAN << 4 ) | WHITE );
		locate( 12, 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( 13,41, 22,78 );

		ShowDevice();
		ShowProtect();

		setport( USERBITS, 0, 0 );
		power(1);

		l = 13;

		// BLANK CHECK

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

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			setaddr( addr );
			if( f_read() != 0xFF )
				break;
		}

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

		locate( l++, 41 );
		if( addr > BufEnd )
		{
			ShowCounter( BufEnd );
			cprintf( " OK !" );
		}
		else
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Blank check error at %05lX", 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... " );

			done = do_erase();

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

				power(0);
				continue;
			}
		}

		// PROGRAM

		if( !buffer && ( f = fopen( buffile, "rb" ) ) == NULL )
		{
			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			locate( 13, 41 ); cprintf( "File open error !" );
			textattr( ( CYAN << 4 ) | WHITE );
			delay(1000);
			break;
		}

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

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			if( !do_program( addr, ( buffer ? buffer[addr] : getc(f) ) ) )
				break;
		}

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

		locate( l++, 41 );
		if( addr > BufEnd )
		{
			ShowCounter( BufEnd );
			cprintf( " OK !" );
		}
		else
		{
			if( !buffer ) fclose( f );

			errbeep();
			textattr( ( RED << 4 ) | WHITE );
			cprintf( "Program error ! at %05lX", addr );
			textattr( ( CYAN << 4 ) | WHITE );

			power(0);
			continue;
		}

		// VERIFY

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

		if( !buffer ) rewind( f );

		ShowCounter( BufStart );
		for( addr = BufStart; addr <= BufEnd; ++addr )
		{
			if( ( addr & 0xFF ) == 0 )
				ShowCounter( addr );

			setaddr( addr );

			if( ( ( buffer ? buffer[addr] : getc(f) ) & 0xFF ) != f_read() )
				break;
		}

		if( !buffer ) fclose( f );

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

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

		power(0);
	}
	power(0);
}

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

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

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

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

	for( i = 0; i < mfr[mfrno].numdevs; ++i )
	{
		locate( 12+i, 41 ); cprintf( "%d.%s", i, mfr[mfrno].dev[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 < 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( 11,40, 23,79 );

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

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

	for( i = 0; i < sizeof(mfr) / sizeof(mfr[0]); ++i )
	{
		locate( 12+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 ch = 0, redraw, first = 1;

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

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

	ReadConfig();

	for( ;; )
	{
		if( first )
		{
			first = 0;
			
			init_hw();
			initdacs();
			setport( USERBITS, 0, 0 );

#ifdef WIN32
			if( !buffer ) buffer = farmalloc( bufsize );
			else buffer = farrealloc( buffer, bufsize );
#else
			if( !buffer ) buffer = (void huge *)farmalloc( bufsize );
			else buffer = (void huge*)farrealloc( (void far*)buffer, bufsize );
#endif
		}

		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( "FLASH section " _VERSION_ );

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

		ShowType();
		power(0);

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

		_window( 6,69, 9,79 );
		locate( 7,71 ); cprintf( "COUNTER" );

		ShowCounter( 0L );

		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( "9. Modify buffer structure             " );
		locate(11,0 ); cprintf( "W. swapping LOW-HI byte in buffer      " );
		locate(12,0 ); cprintf( "T. Type select     M. Mfr. select      " );
		locate(13,0 ); cprintf( "Z. Target zone                         " );
		locate(14,0 ); cprintf( "                                       " );
                locate(15,0 ); cprintf( "B. Blank check     D. Display          " );
                locate(16,0 ); cprintf( "P. Program         A. Auto(B&P&S&V)    " );
		locate(17,0 ); cprintf( "R. Read            V. Verify           " );
		locate(18,0 ); cprintf( "I. Read MFG/DEV ID S. Read data prot.  " );
		locate(19,0 ); cprintf( "E. Erase           F. Toggle safe mode " );
		locate(20,0 ); cprintf( "Q. Quit                                " );
		locate(21,0 ); cprintf( "---------------------------------------" );
		locate(22,0 ); cprintf( "Buffer size      : %ldK bytes", bufsize / 1024 );
		locate(23,0 ); cprintf( "Buffer structure : %s", buffile );

 		

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

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

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

			switch( ch = toupper(ch) )
			{
			case '1': dos_shell( "" ); redraw = 1; break;
			case '2': load_file(); redraw = 1; break;
			case '3': save_file(); break;
			case '4': edit_buffer(); redraw = 1; break;
			case '5': set_io_adr(); redraw = first = 1; break;

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

			case 'B': flash_check(); break;
			case 'E': flash_erase(); break;
			case 'P': flash_program(); break;
			case 'V': flash_verify(); break;
			case 'A': flash_auto(); break;
			case 'R': flash_read(); break;
			case 'S': ShowProtect(); break;
			case 'I': ShowDevice(); break;
			case 'F': safe_mode ^= 0x01; redraw = first = 1; break;

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

			setport( USERBITS, 0, 0 );

			if( ch == 'Q' )
			{
				WriteConfig();
				textattr( LIGHTGRAY ); clrscr();
				chdir( oldpath );
				return( 0 );
			}

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