/*
 * An adaption of EPROMmer.bas
 * By Udi Finkelstein (C) 1990
 *
 * Changes by Carsten Rose in November 1992 - June 1993:
 *  - changed to compile with DICE 2.07.54R
 *  - GUI now generated with GadToolsBox V2.0c
 *  - WB 2.0 Look
 *  - Use default Screen Font (done by GTB)
 *  - Notice Font Size		  (done by GTB)
 *  - Ability to Load and Save Files in 'intelhex' Format 
 *                   (Pseudo Standard Object Code Format)
 *  - req.library no longer used (use ASL FileRequest and EasyRequest)
 *     if you don't like it use any of the 'ASL to REQ' Patches
 *  - Integer Gadgets changed to String Gadgets , to allow input of HEX-Numbers
 *	- Settings will be saved temporaly in ENV:Eprommer.Prefs or in ENVARC:Eprommer.Prefs
 *	- Test of VPP more 'Eprom friendly' ;-)
 *  - some little hacks more
 */

#include "defs.h"
#include "eprommer.h"
#include "gad_eprommer.h"
#include "intelhex.h"

#include "eprommer_protos.h"

/*#define DEBUG_PROG*/

#define CIA_MINUSEC	5	/* Minimum CIA access time (in micro seconds) */

#define DELAY {volatile u_char c;int i;for(i=0;i<waitcnt;i++) c=*ParPort;}

char *vers = "\0$VER: Eprommer 3.2d (November,14,1993)";

#define OE  0x01
#define PGM 0x02

#define VOLTS12 (223 - 128)
#define VOLTS21 (223 - 64)
#define VOLTS25 (223 - 192)

/* PA2 = SEL  */
#define Clock0 0
#define Clock1 4

#define MAXDATABUFFER 0x10000	/* 64kB */

struct	Window *window_locked=NULL;  /* If program stops, check fisrt if any Window is locked, release them */
struct	IntuiText IT_counting; 		/* IntuiText struct which shows counting */
BOOL    pp_flag=FALSE;ppb_flag=FALSE;

u_char *PEmemBase;
char *EmemTop,*RamBot;

extern __far struct CIA ciaa,ciab;

volatile u_char *ParPort  = &ciaa.ciaprb;  /*(volatile u_char *)0xbfe101;*/
volatile u_char *ParDDR   = &ciaa.ciaddrb; /*(volatile u_char *)0xbfe301;*/
volatile u_char *CtrlPort = &ciab.ciapra;  /*(volatile u_char *)0xbfd000;*/
volatile u_char *CtrlDDR  = &ciab.ciaddra; /*(volatile u_char *)0xbfd200;*/

u_char volt_tbl[] = { VOLTS25, VOLTS25, VOLTS21, VOLTS21,
	VOLTS12, VOLTS12, VOLTS12, VOLTS12, VOLTS12, VOLTS12 };

/* PA0 = /OE, PA1 = /PGM */
u_char rp_tbl[] = { 0, 0, 0, PGM, PGM, PGM, 0, 0, 0, 0 ,0};

u_char wp_tbl[] = { OE+PGM, OE, OE, OE, OE, OE, OE, OE, OE, OE, OE };

u_char lp_tbl[] = { OE+PGM, OE+PGM, OE+PGM, OE+PGM, OE+PGM,
					OE+PGM, OE+PGM, OE+PGM, OE+PGM, OE+PGM, OE+PGM };
u_char ip_tbl[] = { OE, OE+PGM, OE+PGM, OE+PGM, OE+PGM, OE+PGM,
					OE+PGM, OE+PGM, OE+PGM , OE+PGM, OE+PGM };

	/* 1==EProm, 0==Static RAM (used to check only EProm's if any bits in the eprom are already 0) */
u_char e_flag[] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 };

u_short ad_tbl[] = { 0, 0, 0, 0, 0, 0, 0, 0,0, 0x2000, 0 };

	/* Table of default program Alorythm */
short ag_tbl[] = {AG_50MS,AG_50MS,AG_50MS,AG_INTE,AG_INTE,AG_INTE,AG_INTE,AG_INTE,AG_NDEL,AG_NDEL,AG_NDEL};

	/* Table of 'Programing' Functions Adresses */
short (*wa_tbl2[])(u_char) = { Wr50ms, WrIntel, WrEprommer, WrFast };

short (*WriteAlg)(u_char);

	/* Table of Eprom default sizes */
long size_tbl[] = { 2048, 4096, 4096, 8192, 8192, 16384, 32768, 65536, 2048, 8192, 32768 };

	/* Global Variables to do Eprom Handling */
u_char VoltVal, ReadParam, WriteParam, LeaveParam, InhibitParam;

u_char 	Addr0, /* Prepare Clock for High Byte of Address */ 
		Addr1, /* Clock for High Byte of Address */ 
		Addr2, /* Prepare Clock for Low Byte of Address */  
		Addr3; /* Clock for Low Byte of Address */
u_char Write1, Write2, Write3, Write4, Write5;
u_char Read0, Read1;

long LastAddr;		/* Contains always the latest switched address */
u_short AdParam;	/* Eprommer Hardware Offset. All Eprom Addresses will */
					/*  be added with this value. */

	/* These are all Mirrors of the appropriate String Gadget */
u_char RAMValInt=0;	/* RAM Buffer Value */
LONG   RAMLowInt=0,	/* Logical RAM Buffer Start Address */
	   EPROMLowInt=0, /* logical Eprom Start Address */
       LengthInt=0;	/* Eprom Length */

#ifdef _DCC
 extern struct IntuitionBase *IntuitionBase;
 extern struct GfxBase *GfxBase;
#else
 struct IntuitionBase *IntuitionBase;
 struct GfxBase *GfxBase;
#endif

struct Library *MiscBase=NULL;
struct MsgPort *tp;
struct timerequest *tr;
long Size;
ULONG waitcnt=20;		/* Delay Counter for CIA Access. Depends of your Amiga 500/600/... */
short EPType = -1;		/* Eprom Type EP_xxxx  / See Eprommer.h */

char LDir[DSIZE + 1], LFile[FCHARS + 1], LFullPath[DSIZE+FCHARS+2];
char SDir[DSIZE + 1], SFile[FCHARS + 1], SFullPath[DSIZE+FCHARS+2];

u_char testvpp=VOLTS12;

struct Config conf,newconf;			/* contains User settings */
BOOL epromout = FALSE;				/* Eprom in Socket ? */
BOOL newepromout = FALSE;			/* Eprom in Socket ? */

BOOL abort_flag=FALSE;					/* set, if User abort Operation */

APTR aslreq;
struct Requester *plockreq=NULL;

/* Static Data for Busy Pointer */
__chip UWORD waitpointer[] = {
        0x0000, 0x0000,
        0x0400, 0x07C0,
        0x0000, 0x07C0,
        0x0100, 0x0380,
        0x0000, 0x07E0,
        0x07C0, 0x1FF8,
        0x1FF0, 0x3FEC,
        0x3FF8, 0x7FDE,
        0x3FF8, 0x7FBE,
        0x7FFC, 0xFF7F,
        0x7EFC, 0xFFFF,
        0x7FFC, 0xFFFF,
        0x3FF8, 0x7FFE,
        0x3FF8, 0x7FFE,
        0x1FF0, 0x3FFC,
        0x07C0, 0x1FF8,
        0x0000, 0x07E0,
        0x0000, 0x0000};             /* reserved, must be NULL */

static long speedtest(struct timerequest *tr,long cnt)
{
	int i;
	u_char c;
	long sec1,sec2,micro1,micro2;


	tr->tr_node.io_Command = TR_GETSYSTIME;
	DoIO((struct IORequest *)tr);
	sec1=tr->tr_time.tv_secs;
	micro1=tr->tr_time.tv_micro;
	
	for(i=0;i<cnt;i++)
		c = *ParPort;
	
	DoIO((struct IORequest *)tr);
	sec2=tr->tr_time.tv_secs;
	micro2=tr->tr_time.tv_micro;

	micro1 = (sec2 - sec1)*1000000 + micro2-micro1;

	return(micro1);
}

static void checkspeed(void)
{

	long cnt=10000,tm=0;

	while(tm<100000)
	{
		cnt*=5;
		tm=speedtest(tr,cnt);
	}

	waitcnt=cnt/(tm/CIA_MINUSEC);
}

static void lockwindow(struct Window *wnd,int flag)
{
	/* Locks a window for User input and changes MousePointer for this Window
     * like the Workbench BusyPointer.
	 * If flag is
	 *  TRUE: Opens an unvisible Requester.
	 *  FALSE: Close this Requester.
	 */

#ifdef DEBUG_PROG
puts("lockwindow");
#endif
	if(flag)
	{
		SetPointer(wnd,waitpointer,16,16,0,0);
		window_locked = wnd;
		if(!Request(plockreq,wnd))
			DisplayBeep(NULL);     /* Just if request can't open/lock the Window*/
	}
	else
	{
		EndRequest(plockreq,wnd);
		window_locked = NULL;
		ClearPointer(wnd);
	}
}

static void simplerequest(char *ctl, ...)
{
	/* Call this routine like printf().
	 *  Simply open a requester with your message.
	 */

	va_list va;

	struct EasyStruct easy = {
		sizeof (struct EasyStruct),
	    0,
	    "Message",
	    NULL,   /* ctl */
	    "OK"
	};

    easy.es_TextFormat = ctl;

	va_start(va,ctl);
	
	EasyRequestArgs(EprommerWnd,&easy,NULL,va);
	va_end(va);
}

static int twogadrequest(char *buttons,char *ctl, ...)
{
	/* Call this routine like printf(), but a special string 'buttons'.
	 * This string must contain the text for the 2 Buttons (mostly "OK|CANCEL").
	 * The 2 Buttons must be seperated with '|'.
	 *  Simply open a requester with your message.
	 *
	 * RC: 1 if the left Button is pushed
	 *     0 if the right Button is pushed
	 */

	va_list va;
	int rc;
	struct EasyStruct easy = {
		sizeof (struct EasyStruct),
	    0,
	    "Message",
	    NULL,   /*ctl,*/
	    NULL
	};
	

    easy.es_TextFormat = ctl;
    easy.es_GadgetFormat = buttons;

	va_start(va,ctl);
	rc = EasyRequestArgs(EprommerWnd,&easy,NULL,va);
	va_end(va);
	return(rc);
}

static void updatevalgad(int gadnr,int value)
{
	/* Update (Display) 'value' in 'gadnr' String Gadget */

	char tmp[256];

	switch(conf.numericalbase)
	{
		case CY_DEC:    sprintf(tmp, "%d", value);
						break;
		case CY_DECHEX:
		case CY_HEX:	sprintf(tmp, "0x%x", value);
						break;
		default:		simplerequest("Error numerical Base, Type:%d. File:%s Line:%d",conf.numericalbase,__FILE__,__LINE__);
	}
	GT_SetGadgetAttrs(EprommerGadgets[gadnr],EprommerWnd,NULL,GTST_String,tmp,TAG_DONE);
}

static void updateallvalgad(void)
{
	/* Updae all String Gadgets */
	updatevalgad(GD_RamAddress,RAMLowInt);
	updatevalgad(GD_EpromAddress,EPROMLowInt);
	updatevalgad(GD_Length,LengthInt);
	updatevalgad(GD_RamVal,(int)RAMValInt);
}

/*
 * Timer initialization done here
 */
static int OpenTimer(void)
{

	if ((tp = CreatePort(0, 0)) == 0) return 1;
	if ((tr = (struct timerequest *)CreateExtIO(tp, sizeof(struct timerequest)))
		== NULL) return 1;

	if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *)tr, 0))
		return 1;

    return(NULL);
}

/*
 * Timer freed done here
 */
static void CloseTimer(struct timerequest *tr)
{

	if (tp)
    {
		DeletePort(tp);
    	tp = NULL;
	}

	if (tr) 
	{
		CloseDevice((struct IORequest *)tr);
		DeleteExtIO((struct IORequest *)tr);
        tr = NULL;
	}
}

/*
 * A MicroSeconds precision delay routine
 */
static void uDelay(ULONG delay)
{

	tr->tr_node.io_Command = TR_ADDREQUEST;
	tr->tr_time.tv_secs = 0;
	tr->tr_time.tv_micro = delay;
	DoIO((struct IORequest *)tr);
}

static void SetAddr(long Address)
{
	LastAddr = Address;
	*ParDDR = 255;      /* Parallel Port Direction Output */
	*ParPort = 253;     /* Prepare selection of High Byte Address Latch */
	DELAY

	*CtrlPort = Addr0;	/* Select High Byte Address Latch and inactivate Address Latch Clock */
	DELAY

	*ParPort = Address >> 8;  /* Write High Byte Address */
	DELAY

	*CtrlPort = Addr1;  /* Clock High Byte Address into Latch*/
	DELAY

	*ParPort = 254;		/* Prepare selection of Low Byte Address Latch */
	DELAY

	*CtrlPort = Addr2;  /* Select Low Byte Address Latch and inactivate Address Latch Clock */
	DELAY

	*ParPort = Address & 0xff; /* Write Low Byte Address */
	DELAY

	*CtrlPort = Addr3;  /* Clock Low Byte Address into Latch */ 
}

static void InitPorts(void)
{

      /* Prepare VPP DataBits and Lo/Hi Address Latch to take Data */
    *CtrlDDR  = Clock1 + OE + PGM;	/* Set to OutPut: 0000 0111 */
	*CtrlPort = Clock0 + OE + PGM;  /* Write        : 0000 0011 */     
	DELAY

	testvppoff();
	SetAddr(0);
}

static void testvppon(u_char volt)
{
	*ParDDR = 255;
	*ParPort = volt;

	*CtrlPort = Clock0;
	DELAY
	*CtrlPort = Clock1;
}

static void testvppoff(void)
{
    
	*CtrlPort = LeaveParam + Clock0;
	DELAY
	*ParDDR  = 255;
	*ParPort = 255;
	DELAY
	*CtrlPort = LeaveParam + Clock1;
}

static void ThatsIt(void)
{
    /* Set Eprom Adress to 0, Init CIA */
	SetAddr(0);
    testvppoff();
	*CtrlDDR = 0;
	*ParDDR = 0;
}

static u_char ReadByte(void)
{
u_char DataIn;


/* If A15 is used for /WR, make sure it's high now - used for 62256,6264 */
	if( (EPType==EP_6216) || (EPType==EP_6264)|| (EPType==EP_62256) )
      SetAddr(LastAddr | 0x8000);

/* port is now input */
	*ParDDR = 0;
	*CtrlPort = Read0;
	DELAY
	DataIn = *ParPort;

	*CtrlPort = Read1;

	return DataIn;
}

static void WriteByte(u_char DataOut, u_long delay)
{

/* If A15 is used for /WR, make sure it's low now - used for 62256,6264 */
	if( (EPType==EP_6216) || (EPType==EP_6264) || (EPType==EP_62256) )
      SetAddr(LastAddr & ~0x8000);

	*ParDDR = 255;
	*ParPort = VoltVal;
	DELAY

	*CtrlPort = Write1;
	DELAY

	*ParPort = DataOut;
	DELAY

	*CtrlPort = Write2;
	uDelay(delay);

	*CtrlPort = Write3;
	DELAY

	*ParPort = 255;
	DELAY

	*CtrlPort = Write4;
	DELAY

	*CtrlPort = Write5;
}

static void SetCycleParams(void )
{
	Write1 = InhibitParam + Clock1;
	Write2 = WriteParam + Clock1;
	Write3 = InhibitParam + Clock0;
	Write4 = InhibitParam + Clock1;
	Write5 = LeaveParam + Clock0;
	Read0 = ReadParam + Clock0;
	Read1 = LeaveParam + Clock0;
	Addr0 = LeaveParam + Clock1;
	Addr1 = LeaveParam + Clock0;
	Addr2 = LeaveParam + Clock1;
	Addr3 = LeaveParam + Clock0;
}

/*
 * Write a single byte
 */
static short WriteCycle(u_char DataOut)
{
u_char c;

#ifdef DEBUG_PROG
	puts("WriteCycle");
#endif

/* Any bit set in DataOut which is already reset in the EPROM? */
	c = ReadByte();

	if (e_flag[EPType] && (~c & DataOut)) /* e_flag==0 -> EProm is a static RAM, NO check */ 
		return 1;

/* Is data already identical? */
	if (DataOut == c) return 0;

#ifdef DEBUG_PROG
	printf("call WriteAlg: %x\n",WriteAlg);
#endif

	return (*WriteAlg)(DataOut);
}

/*
 * The Eprommer algorythm - a variation on the intel algorythm.
 */
static short WrEprommer(u_char  DataOut)
{
short n;
u_long l;

#ifdef DEBUG_PROG
	puts("WriteEprommer");
#endif
/* Programming loop */
	n = 0;
	while(++n < 20) {
		WriteByte(DataOut, 1000);
		if (ReadByte() == DataOut) {
/* Overprogramming section */
			l = n * 5000;
			if (l > 20000) l = 20000;
			WriteByte(DataOut, l);
			return 0;
		}
	}
/* Too many retries, must be EPROM failure */
	return 2;
}

/*
 * The IntEligent programming algorythm algorythm.
 */
static short WrIntel(u_char DataOut)
{
short n;

#ifdef DEBUG_PROG
	puts("WriteIntel");
#endif
/* Programming loop */
	n = 0;
	while(++n < 15) {
		WriteByte(DataOut, 1000);
		if (ReadByte() == DataOut) {
/* Overprogramming section */
			WriteByte(DataOut, n * 4000);
			return 0;
		}
	}
/* Too many retries, must be EPROM failure */
	return 2;
}

/*
 * The fastest algorythm - use for RAMs only!
 */
static short WrFast(u_char DataOut)
{
/* No loop! */
#ifdef DEBUG_PROG
	puts("WriteFast");
#endif
	WriteByte(DataOut, 10);
	if (ReadByte() == DataOut) return 0;

/* RAM failure */
	return 2;
}

/*
 * The basic 50ms pulse algorythm.
 * Use for 2716, 2732 and 2732A chips which requires it. not all
 * brands of chips require this slow algorythm (Intel does, TI don't).
 */
static short Wr50ms(u_char DataOut)
{
/* No loop! */
#ifdef DEBUG_PROG
	puts("Wr50ms");
#endif
	WriteByte(DataOut, 50000);
	if (ReadByte() == DataOut) return 0;

/* RAM failure */
	return 2;
}

static void LoadFile(void)
{
	/* Load the file 'LFullPath' in  buffer pointed by 'PEmemBase+RAMLowInt'.
	 * Notice the 'conf.dataformat' and 'conf.clearbuffer'.
	 */

	FILE *fin;
	char *perror;	/* Pointer to Error Message */
	int  loadmode;

	if ((fin = fopen(LFullPath, "r")) == NULL) {
		simplerequest("Could not open input file '%s'!", LFullPath);
		return;
	}
	
	if(conf.clearbuffer)
		setmem(PEmemBase,MAXDATABUFFER,0xFF);

	lockwindow(EprommerWnd,TRUE);
	if(conf.dataformat==INTELHEX)
	{
		loadmode = conf.bufferfragmented?FRAGMENTED:CONTINOUS;
		if(perror=readintelhex(fin,PEmemBase+RAMLowInt,MAXDATABUFFER-RAMLowInt,&LengthInt,loadmode,&EPROMLowInt))
		{
			simplerequest(perror);
			LengthInt=0;
		}
	}
	else
	{
		LengthInt=fread(PEmemBase+RAMLowInt,1,MAXDATABUFFER-RAMLowInt,fin);
	}
	fclose(fin);
	
	if( (LengthInt+EPROMLowInt) >size_tbl[EPType])
	{
		if(EPROMLowInt>size_tbl[EPType])
		{
			simplerequest("Start Address:%ls bigger than Eprom size:%ld",EPROMLowInt,size_tbl[EPType]);
			LengthInt=0;
			EPROMLowInt=0;			
		}
		else
		{
			simplerequest("That's too much data.\nRead %ld Bytes, Start's at: %ld\nEprom Size: %ld Bytes",LengthInt,EPROMLowInt,size_tbl[EPType]);
			LengthInt=size_tbl[EPType]-EPROMLowInt;
		}
	}
	else if(conf.inforeq)
		simplerequest("Read %ld Bytes",LengthInt);

	updatevalgad(GD_Length,LengthInt);
	updatevalgad(GD_EpromAddress,EPROMLowInt);
	lockwindow(EprommerWnd,FALSE);
}

static void SaveFile(void)
{
	FILE *fin;
	LONG len;
	char *perror;	/* Pointer to Error Message */

	/* check if File exists */
	if (fin=fopen(SFullPath, "r"))
	{
		fclose(fin);
		if(!twogadrequest("OK|CANCEL","Do you want overwrite\n '%s'",SFullPath))
			return;
	}

	if ((fin = fopen(SFullPath, "w")) == NULL) {
		simplerequest("Could not open output file '%s'!",SFullPath);
		return;
	}

	lockwindow(EprommerWnd,TRUE);
	if(conf.dataformat==INTELHEX)
	{
		if(perror=writeintelhex(fin,PEmemBase + RAMLowInt,RAMLowInt,LengthInt))
		{
			simplerequest(perror);
			return;
		}
		len = LengthInt;
	}
	else
		len = fwrite(PEmemBase + RAMLowInt, 1, LengthInt, fin);

	fclose(fin);
	updatevalgad(GD_Length,LengthInt);
	lockwindow(EprommerWnd,FALSE);

	if (len != LengthInt)
		simplerequest("We wrote only %ld bytes Instead of %ld bytes",len,LengthInt);
	else
		if(conf.inforeq)
			simplerequest("Write %ld Bytes",LengthInt);
}

static char *stoh(u_short value,char *pbuf)
{
	/* Convert 'value' to  4-digit ASCII Hex Notation.
	 * 
	 * Write ASCII Hex Code (4 Chars) at '*pbuf'.
	 *
	 * RC: pbuf+4    (points to next free char  )
     */  

	u_char nibble;	/*  4 Bits of 'value' */

	nibble = 0x000F & value;
	pbuf[3]= (nibble<=9) ? nibble+'0' : (nibble-0x0A)+'A';

	value >>=4;
	nibble = 0x000F & value;
	pbuf[2]= (nibble<=9) ? nibble+'0' : (nibble-0x0A)+'A';

	value >>=4;
	nibble = 0x000F & value;
	pbuf[1]= (nibble<=9) ? nibble+'0' : (nibble-0x0A)+'A';

	value >>=4;
	nibble = 0x000F & value;
	pbuf[0]= (nibble<=9) ? nibble+'0' : (nibble-0x0A)+'A';

	return(&pbuf[4]);
}

static void startamigaguide(UWORD code)
{
	if(code==95)  /* Key: Help */
		system("run SYS:Utilities/AmigaGuide Eprommer.guide");
}

int EprommerRawKey( void )
{
	/* routine for "IDCMP_RAWKEY". */
	startamigaguide(EprommerMsg.Code);
	return(TRUE);
}

int OptionsRawKey( void )
{
	/* routine for "IDCMP_RAWKEY". */
	startamigaguide(OptionsMsg.Code);
	return(TRUE);
}

int BusyRawKey( void )
{
	/* routine for "IDCMP_RAWKEY". */
	startamigaguide(BusyMsg.Code);
	return(TRUE);
}

int TestRawKey( void )
{
	/* routine for "IDCMP_RAWKEY". */
	startamigaguide(TestMsg.Code);
	return(TRUE);
}




int abortClicked(void)
{
	abort_flag = TRUE;

	return(TRUE);
}

static void showaddr_checkabort(long addr,BOOL flag)
{
	int rc;
	struct IntuiMessage	*m;
	int			(*func)(void);

	if(!BusyWnd)	/* Busy Window already opened ? */
	{
#ifdef DEBUG_PROG
		puts("OpenBusyWindow");
#endif
		if(rc=OpenBusyWindow())
			cleanup("Error Open Busy Window");
    }

	if(conf.counting && (flag || !(addr&0xFF)))
	{
		stoh((u_short)addr,&IT_counting.IText[2]);
		PrintIText( BusyWnd->RPort, &IT_counting, 0,0 );
	}

		/* check if 'abort' is pushed (through IDCMP_Class GadgetUp*/
	while( m = GT_GetIMsg( BusyWnd->UserPort )) 
	{
		CopyMem(( char * )m, ( char * )&BusyMsg, (long)sizeof( struct IntuiMessage ));

		GT_ReplyIMsg( m );

		switch ( BusyMsg.Class ) 
		{

			case	IDCMP_REFRESHWINDOW:
				GT_BeginRefresh( BusyWnd );
				BusyRender();
				GT_EndRefresh( BusyWnd, TRUE );
				break;

			case	IDCMP_GADGETUP:
				func = (int (*)(void)) (( struct Gadget * )BusyMsg.IAddress )->UserData;
				func();
				break;
		}
	}
}

/*
 * Read the EPROM
 */
static void ReadEPROM(void)
{
	long addr,s_addr, len;
	u_char *c;

	len    = LengthInt;
	s_addr = EPROMLowInt;
	addr   = EPROMLowInt + AdParam;
	c      = PEmemBase+RAMLowInt;

#ifdef DEBUG_PROG
printf("ReadEprom len:%x addr:%x c:%x 1000:%d\n",len,addr,c,1000);
#endif

	while (len--) 
	{
		showaddr_checkabort(s_addr++,FALSE);
		SetAddr(addr++);
		*c++ = ReadByte();
		if(abort_flag)
		{
			CloseBusyWindow();
			abort_flag = FALSE;
			return;
		}
	}
	CloseBusyWindow();
	if(conf.inforeq)
		simplerequest("We read %ld bytes", LengthInt);
}

/*
 * Compare the EPROM with the RAM buffer
 */
static void CompareEPROM(void)
{
	long addr,s_addr, len;
	u_char *c, ce, cr;
	int		flag_failed=FALSE;

	len    = LengthInt;
	s_addr = EPROMLowInt;
	addr   = EPROMLowInt + AdParam;
	c = PEmemBase+(RAMLowInt);
	while (len--) {
		showaddr_checkabort(s_addr++,FALSE);
		SetAddr(addr++);
		if ((cr = *c++) != (ce = ReadByte()))
		{
			flag_failed = TRUE;
			if (!twogadrequest("CONTINUE|CANCEL","Offset %ld, EPROM:%02lx   RAM:%02lx",
				addr - EPROMLowInt - 1, ce, cr) ) 
			  abort_flag = TRUE;
		}
		if(abort_flag)
		{
			CloseBusyWindow();
			abort_flag = FALSE;
			return;
		}
	}
	CloseBusyWindow();
	if(flag_failed)
		simplerequest("Compare failed");
	else
		simplerequest("Compare successfull");
}

/*
 * Make sure EPROM is empty
 */
static void CheckEPROM(void)
{
	long addr,s_addr,end;

	s_addr = EPROMLowInt;
	addr   = EPROMLowInt + AdParam;
	end    = addr + LengthInt;
	
	for (; addr < end;) {
		showaddr_checkabort(s_addr++,FALSE);
		SetAddr((u_short)(addr++));
		if (ReadByte() != 0xff) 
		{
			simplerequest("EPROM contains data at %ld!", s_addr - 1);
			abort_flag = TRUE;
		}

		if(abort_flag)
		{
			CloseBusyWindow();
			abort_flag = FALSE;
			return;
		}
	}
	CloseBusyWindow();
	simplerequest("EPROM is empty");
}

/*
 * Program the EPROM
 */
static void ProgramEPROM(void)
{
long addr,s_addr, len;
u_char *c;

#ifdef DEBUG_PROG
	puts("ProgramEPROM");
#endif

	len = LengthInt;
	s_addr = EPROMLowInt;
	addr = EPROMLowInt+AdParam;
	
	c = PEmemBase+RAMLowInt;

	abort_flag = FALSE;

	while (len-- && !abort_flag) 
	{
		showaddr_checkabort(s_addr++,FALSE);
		SetAddr(addr++);

		switch (WriteCycle(*c++)) 
		{
			case 2:
				simplerequest("Verify error at EPROM address %ld", s_addr - 1);
				abort_flag = TRUE;
				break;

			case 1:
				simplerequest("EPROM already has data at address %ld", s_addr - 1);
				abort_flag = TRUE;
				break;
		}
	}

	CloseBusyWindow();
	abort_flag = FALSE;

	if(conf.inforeq)
		simplerequest("We programmed %ld bytes", LengthInt);
}

static void getstringgads(void)
{
	char *dummy;
	int base;

#ifdef DEBUG_PROG
puts("getstringgads");
#endif
	switch(conf.numericalbase)
	{
		case CY_DEC:    base=10;
						break;
		case CY_DECHEX:	base=0;
						break;
		case CY_HEX:	base=16;
						break;
		default:		simplerequest("Error numerical Base, Type:%d. File:%s Line:%d",conf.numericalbase,__FILE__,__LINE__);
	}

	RAMValInt   = (u_char)strtol(((struct StringInfo *)EprommerGadgets[GD_RamVal]->SpecialInfo)->Buffer,&dummy,base);
	RAMLowInt   = strtol(((struct StringInfo *)EprommerGadgets[GD_RamAddress]->SpecialInfo)->Buffer,&dummy,base);
	EPROMLowInt = strtol(((struct StringInfo *)EprommerGadgets[GD_EpromAddress]->SpecialInfo)->Buffer,&dummy,base);
	LengthInt   = strtol(((struct StringInfo *)EprommerGadgets[GD_Length]->SpecialInfo)->Buffer,&dummy,base);
}

static void dividepath(char *path,char *dir,char *file)
{
	/* divide 'path' in Name of Directory and Name of File */
	
	char *pfile;

	if(!(pfile=strrchr(path,'/')))
	{
		if(!(pfile=strrchr(path,':')))
		{
			strcpy(file,path);
			*dir='\0';
			return;
		}
	}
	strncpy(dir,path,pfile-path+1);
	dir[pfile-path+1]='\0';
	strcpy(file,pfile+1);
}

static void setepromtype(int eptype)
{
	/* set EPROM parameters */
	EPType       = eptype;
	Size         = size_tbl[EPType];
	VoltVal      = volt_tbl[EPType];
	ReadParam    = rp_tbl[EPType];
	WriteParam   = wp_tbl[EPType];
	InhibitParam = ip_tbl[EPType];
	AdParam      = ad_tbl[EPType];
	*CtrlPort    = LeaveParam = lp_tbl[EPType];
	SetCycleParams();
	WriteAlg     = wa_tbl2[ag_tbl[EPType]];

	/* set dafault Program Algorythm for this Eprom */
	GT_SetGadgetAttrs(EprommerGadgets[GD_ProgAlgo],EprommerWnd,NULL,GTMX_Active,ag_tbl[EPType],TAG_DONE);

		/* check if 'Eprom Default Size' is set */
	if(conf.defepsize)
	{
	    /* yes, set default size */
		LengthInt = size_tbl[EPType];
		updatevalgad(GD_Length,LengthInt);
	}
}

static void optionsvalid(void)
{
	/* Copy new configuration to old one.
	 * Update the String Gadgets.
	 * Erase the Vpp if they set.
	 */

	int change;

	/* numericale Base changed ? */
	change = (conf.numericalbase==newconf.numericalbase)?FALSE:TRUE;

	conf = newconf; /* copy complete structure */

		/* Take latest used Directories */
	strcpy(conf.loaddir,LDir);
	strcpy(conf.savedir,SDir);

	if(change)  /* perhaps the base has been changed (Decimal or Hexadecimal) */
		updateallvalgad();

	waitcnt = conf.waitcnt;
}

int DataFormatClicked( void )
{
	/* routine when gadget "Data Format" is clicked. */
	
	conf.dataformat = EprommerMsg.Code;
	return(TRUE);
}

int ProgAlgoClicked( void )
{
	/* routine when gadget "" is clicked. */

	WriteAlg = wa_tbl2[EprommerMsg.Code];
	return(TRUE);
}

int RamAddressClicked( void )
{
	/* routine when gadget "Buffer" is clicked. */
	getstringgads();
	updatevalgad(GD_RamAddress,RAMLowInt);
	return(TRUE);
}

int EpromAddressClicked( void )
{
	/* routine when gadget "Eprom" is clicked. */
	int max;

	getstringgads();

	EPROMLowInt=MIN(EPROMLowInt,size_tbl[EPType]);  /* Adjust Length */
	max=size_tbl[EPType]-EPROMLowInt;
	LengthInt=MIN(max,size_tbl[EPType]);  /* Adjust Length */

	updatevalgad(GD_EpromAddress,EPROMLowInt);
	updatevalgad(GD_Length,LengthInt);
	return(TRUE);
}

int LengthClicked( void )
{
	/* routine when gadget "Length" is clicked. */
	int max;

	getstringgads();

	max=size_tbl[EPType]-EPROMLowInt;
	LengthInt=MIN(max,size_tbl[EPType]);  /* Adjust Length */

	updatevalgad(GD_Length,LengthInt);
	return(TRUE);
}

int EpromTypeClicked( void )
{
	if(conf.inforeq)
		simplerequest("Check for correct Personality Module");
	setepromtype(EprommerMsg.Code);
	conf.epromtype = (int)EPType;
	return(TRUE);
}

int RamValClicked( void )
{
	/* routine when gadget "RAM Value" is clicked. */

	getstringgads();
	updatevalgad(GD_RamVal,(int)RAMValInt);
	return(TRUE);
}

int ReadByteClicked( void )
{
	/* routine when gadget "Read Byte" is clicked. */
	getstringgads();
	RAMValInt = (u_char)(*(PEmemBase+RAMLowInt));
	updatevalgad(GD_RamVal,(int)RAMValInt);
	return(TRUE);
}

int WriteByteClicked( void )
{
	/* routine when gadget "Write Byte" is clicked. */
	getstringgads();
	*(PEmemBase+RAMLowInt) = RAMValInt;
	updatevalgad(GD_RamVal,(int)RAMValInt);
	return(TRUE);
}

int ReadEpromClicked( void )
{
	/* routine when gadget "Read" is clicked. */
#ifdef DEBUG_PROG
	puts("ReadEpromClicked");
#endif
	lockwindow  (EprommerWnd,TRUE);
	getstringgads();
	ReadEPROM();
	lockwindow(EprommerWnd,FALSE);
	return(TRUE);
}

int BlankTestClicked( void )
{
	/* routine when gadget "Check" is clicked. */
	lockwindow(EprommerWnd,TRUE);
	getstringgads();
	CheckEPROM();
	lockwindow(EprommerWnd,FALSE);
	return(TRUE);
}

int CompareEpromClicked( void )
{
	/* routine when gadget "Compare" is clicked. */
	lockwindow(EprommerWnd,TRUE);
	getstringgads();
	CompareEPROM();
	lockwindow(EprommerWnd,FALSE);
	return(TRUE);
}

int ProgramEpromClicked( void )
{
#ifdef DEBUG_PROG
	puts("ProgramEpromClicked");
#endif

	/* routine when gadget "Program" is clicked. */
	lockwindow(EprommerWnd,TRUE);
	getstringgads();
	ProgramEPROM();
	lockwindow(EprommerWnd,FALSE);
	return(TRUE);
}

int LoadFileReqClicked( void )
{
	char *pos;
	struct StringInfo *pstrinfo;
	int len;

	if(!LFullPath[0] && SFullPath[0])
	{
		/* no entry -> fill with 'savepath' */

		strcpy(LFullPath,SFullPath);
		strcpy(LFile,SFile);
		strcpy(LDir,SDir);
	}

	pos= (conf.dataformat==INTELHEX)?"Load IntelHex":"Load Binary";

	if(AslRequestTags(aslreq,ASL_Hail, pos,ASL_Window,EprommerWnd,ASL_File,LFile,
                      ASL_Dir,LDir,ASL_Height,(ULONG)EprommerWnd->Height,TAG_DONE))
	{
		strcpy(LFile,((struct FileRequester *)aslreq)->rf_File);
		strcpy(LDir,((struct FileRequester *)aslreq)->rf_Dir);
		strcpy(LFullPath,LDir);
		if(len=strlen(LFullPath))
		{
			if((LFullPath[len-1]!=':')&&(LFullPath[len-1]!='/'))
				strcat(LFullPath,"/");
		}
		strcat(LFullPath,LFile);

		/* fill LoadFile StringGadget */
		pstrinfo = (struct StringInfo *)(EprommerGadgets[GD_LoadFile]->SpecialInfo);
		pos = pstrinfo->Buffer;
		strcpy(pos,LFullPath);
		pstrinfo->NumChars = strlen(pos);
		pstrinfo->BufferPos = 0;
		RefreshGList(EprommerGadgets[GD_LoadFile], EprommerWnd, NULL, 1);
		getstringgads();
		LoadFile();
	}
	return(TRUE);
}

int LoadBtClicked( void )
{
	/* routine when gadget "LOAD" is clicked. */
	getstringgads();
	if(!LFullPath[0])
		LoadFileReqClicked();
	else
		LoadFile();

    return(TRUE);
}

int SaveBtClicked( void )
{
	/* routine when gadget "SAVE" is clicked. */

	getstringgads();
	if(!SFullPath[0])
		SaveFileReqClicked();
	else
		SaveFile();

    return(TRUE);
}

int SaveFileReqClicked( void )
{
	/* routine when gadget "" is clicked. */
	char *pos;
	struct StringInfo *pstrinfo;
	int len;

	if(!SFullPath[0] && LFullPath[0])
	{
		/* no entry -> fill with 'loadpath' */

		strcpy(SFullPath,LFullPath);
		strcpy(SFile,LFile);
		strcpy(SDir,LDir);
	}

	pos= (conf.dataformat==INTELHEX)?"Save IntelHex":"Save Binary";

	if(AslRequestTags(aslreq,ASL_Hail, pos,ASL_Window,EprommerWnd,ASL_File,SFile,
                      ASL_Dir,SDir,ASL_Height,(ULONG)EprommerWnd->Height,TAG_DONE))
	{
		strcpy(SFile,((struct FileRequester *)aslreq)->rf_File);
		strcpy(SDir,((struct FileRequester *)aslreq)->rf_Dir);
		strcpy(SFullPath,SDir);
		if(len=strlen(SFullPath))
		{
			if((SFullPath[len-1]!=':')&&(SFullPath[len-1]!='/'))
				strcat(SFullPath,"/");
		}
		strcat(SFullPath,SFile);
	
		/* fill SaveFile StringGadget */
		pstrinfo = (struct StringInfo *)(EprommerGadgets[GD_SaveFile]->SpecialInfo);
		pos = pstrinfo->Buffer;
		strcpy(pos,SFullPath);
		pstrinfo->NumChars = strlen(pos);
		pstrinfo->BufferPos = 0;
		RefreshGList(EprommerGadgets[GD_SaveFile], EprommerWnd, NULL, 1);
		getstringgads();
		SaveFile();
	}

    return(TRUE);
}

int LoadFileClicked( void )
{
	/* routine when gadget "Load" is clicked. */

	struct StringInfo *pstrinfo;
	char *pos;

	pstrinfo = (struct StringInfo *)(EprommerGadgets[GD_LoadFile]->SpecialInfo);
	pos = pstrinfo->Buffer;

		/* check if StringGadget isn't empty */
	if(strlen(pos))
	{
		strcpy(LFullPath,pos);
		dividepath(LFullPath,LDir,LFile);
		getstringgads();
		LoadFile();
	}
	return(TRUE);
}

int SaveFileClicked( void )
{
	/* routine when gadget "Save" is clicked. */
	struct StringInfo *pstrinfo;
	char *pos;

	pstrinfo = (struct StringInfo *)(EprommerGadgets[GD_SaveFile]->SpecialInfo);
	pos = pstrinfo->Buffer;
	
		/* check if StringGadget isn't empty */
	if(strlen(pos))
	{
		strcpy(SFullPath,pos);
		dividepath(SFullPath,SDir,SFile);
		getstringgads();
		SaveFile();
	}
	return(TRUE);
}

int EprommerCloseWindow( void )
{
	/* routine for "IDCMP_CLOSEWINDOW". */
	simplerequest("Please remove the EPROM now");
	return(FALSE);
}

int DoOptionsClicked( void )
{
	/* routine when gadget "Options" is clicked. */

	/* - Lock 'EprommerWindow' 
	 * - Mark/Unmark CheckBox Gadgets :
	 *  - GD_NumericalBase
	 *	- GD_SetClearBuffer
	 *	- GD_SetBufferFrag
	 */


	int rc;
	
	/* Lock the Eprommer Window */

	lockwindow(EprommerWnd,TRUE);

	if(rc=OpenOptionsWindow())
		cleanup("Error Open Options Window");

	newconf = conf;		/* copy complete structure */

	GT_SetGadgetAttrs(OptionsGadgets[GD_NumericalBase],OptionsWnd,NULL,GTCY_Active,conf.numericalbase,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_SetBufferFrag],OptionsWnd,NULL,GTCB_Checked,conf.bufferfragmented,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_SetClearBuffer],OptionsWnd,NULL,GTCB_Checked,conf.clearbuffer,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_SetInfoReq],OptionsWnd,NULL,GTCB_Checked,conf.inforeq,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_count],OptionsWnd,NULL,GTCB_Checked,conf.counting,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_setepsize],OptionsWnd,NULL,GTCB_Checked,conf.defepsize,TAG_DONE);
	GT_SetGadgetAttrs(OptionsGadgets[GD_CIADelay],OptionsWnd,NULL,GTIN_Number,conf.waitcnt,TAG_DONE);

	rc=TRUE;
    while( rc ) 
	{
    	WaitPort( OptionsWnd->UserPort );
	    rc=HandleOptionsIDCMP();
	}

	CloseOptionsWindow();
	lockwindow(EprommerWnd,FALSE);

    return(TRUE);
}

int NumericalBaseClicked( void )
{
	/* routine when gadget "Input String Gadget" is clicked. */

	newconf.numericalbase=OptionsMsg.Code;
	return(TRUE);
}

int SetClearBufferClicked( void )
{
	/* routine when gadget "Clear Buffer (0xFF)" is clicked. */
	newconf.clearbuffer=OptionsGadgets[GD_SetClearBuffer]->Flags&SELECTED;
	return(TRUE);
}


int SetBufferFragClicked( void )
{
	/* routine when gadget "Buffer fragmented" is clicked. */
	newconf.bufferfragmented=OptionsGadgets[GD_SetBufferFrag]->Flags&SELECTED;
	return(TRUE);
}

int SavePrefsClicked( void )
{
	/* routine when gadget "SAVE" is clicked. */

	FILE *fpconfig=NULL;

	optionsvalid();

	if( (!(fpconfig=fopen(CONFIG_ENVARC,"w"))) ||
		(fwrite(&conf,sizeof(struct Config),1,fpconfig) != 1))
		simplerequest("Error. Can't write Settings to '%s'",CONFIG_ENVARC);

	if(fpconfig)
		fclose(fpconfig);

	return(TRUE);
}

int SetInfoReqClicked( void )
{
	/* routine when gadget "Info Requester" is clicked. */

	newconf.inforeq = OptionsGadgets[GD_SetInfoReq]->Flags&SELECTED;

	return(TRUE);
}

int countClicked( void )
{
	/* routine when gadget "Counting" is clicked. */

	newconf.counting = OptionsGadgets[GD_count]->Flags&SELECTED;

	return(TRUE);
}

int setepsizeClicked( void )
{
	/* routine when gadget "EProm default size" is clicked. */

	newconf.defepsize = OptionsGadgets[GD_setepsize]->Flags&SELECTED;
	return(TRUE);

}

int CIADelayClicked( void )
{
	/* routine when gadget "CIA Delay" is clicked. */

	newconf.waitcnt = ((struct StringInfo *)(OptionsGadgets[GD_CIADelay]->SpecialInfo))->LongInt;
	return(TRUE);
}

int OptionsOkClicked( void )
{
	/* routine when gadget "OK" is clicked. */
	optionsvalid();
	return(FALSE);
}

int OptionsCancelClicked( void )
{
	/* routine when gadget "CANCEL" is clicked. */

	return(FALSE);	
}

int OptionsCloseWindow( void )
{
	/* routine for "IDCMP_CLOSEWINDOW". */

	return(OptionsCancelClicked());
}

/***************** Routines to check Hardware ************************/

int TestHardwareClicked( void )
{
	/* routine when gadget "Test" is clicked. */
	int rc;
	char title[64];

   	lockwindow  (EprommerWnd,TRUE);
	epromout = FALSE;	/* Safer to think there is an Eprom plugged in */

	if(rc=OpenTestWindow())
		cleanup("Error Open Test Window");

	SetAddr((TestMsg.Code==0)?0x5555:0xAAAA);     /* Switch Eprom Address */

	sprintf(title,"%s",TestWnd->Title);

	SetWindowTitles(TestWnd,title,NULL);

	rc = TRUE;
    while( rc ) 
	{
    	WaitPort( TestWnd->UserPort );
	    rc=HandleTestIDCMP();
	}

	testvppoff();
	testvpp=VOLTS12;
	CloseTestWindow();
	lockwindow  (EprommerWnd,FALSE);
	epromout = FALSE;	/* Safer to think there is an Eprom plugged in */

    return(TRUE);
}

int NoEpromClicked( void )
{
	/* routine when gadget "Eprom removed" is clicked. */
	GT_SetGadgetAttrs(TestGadgets[GD_SetVpp],TestWnd,NULL,GA_Disabled,epromout,TAG_DONE);
	epromout=TestGadgets[GD_NoEprom]->Flags&SELECTED;

	testvppoff();

	if(epromout)
		testvppon(testvpp);

	return(TRUE);
}

int SetVppClicked( void )
{
	/* routine when gadget "" is clicked. */
	switch(TestMsg.Code)
	{
		case CY_VPP12:	testvppoff();
						testvppon(VOLTS12);
						testvpp=VOLTS12;
						break;
		case CY_VPP21:	testvppoff();
						testvppon(VOLTS21);
						testvpp=VOLTS21;
						break;
		case CY_VPP25:	testvppoff();
						testvppon(VOLTS25);
						testvpp=VOLTS25;
						break;
		default:		simplerequest("Error  VPP Cycle Gadget. File:%s Line:%d",__FILE__,__LINE__);
	}
    return(TRUE);
}

int T_CyAddressClicked( void )
{
	/* routine when gadget "Address" is clicked. */

	SetAddr((TestMsg.Code==0)?0x5555:0xAAAA);     /* Switch Eprom Address */
	return(TRUE);
}

int T_CyDataClicked( void )
{
	/* routine when gadget "Data" is clicked. */
	*ParPort = (TestMsg.Code==0)?0x55:0xAA;
	return(TRUE);
}

int TestCloseWindow( void )
{
	/* routine for "IDCMP_CLOSEWINDOW". */
	return(FALSE);
}

/*
 * free every resource we might have allocated
 */
static void cleanup(char *s)
{
	ThatsIt();
	if (window_locked) lockwindow(window_locked,FALSE);
	if (s) puts(s);
	if (pp_flag) FreeMiscResource(MR_PARALLELPORT);
	if (ppb_flag) FreeMiscResource(MR_PARALLELBITS);
	if (tp) CloseTimer(tr);
	CloseEprommerWindow();
	CloseOptionsWindow();
	CloseBusyWindow();
	CloseTestWindow();
	CloseDownScreen();
#ifndef _DCC
	if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	if (GfxBase) CloseLibrary((struct Library *)GfxBase);
#endif
	if (aslreq)  FreeAslRequest(aslreq);
	if (PEmemBase) FreeMem(PEmemBase,MAXDATABUFFER);
	if (plockreq)  FreeMem(plockreq,sizeof(struct Requester));

	if(s)
		exit(RETURN_ERROR);
	else
		exit(RETURN_OK);
}

static void handlesigint(int sig)
{
	cleanup("Ctrl-C detected");

	return;
}

int wbmain(void)
{
	int rc;

	rc=main(0,NULL);
	return(rc);
}

int main(int argc, char **argv)
{
	int rc,def_flag;
	FILE *fpconfig=NULL;
	char *owner;
	char title[64],*pos;

	signal(SIGINT,handlesigint);
/*
 * Open all libraries, as well as the program's window
 */

#ifndef _DCC
	if (!(IntuitionBase = (struct IntuitionBase *)
		OpenLibrary("intuition.library", 33L)))
			cleanup("No intuition.library!\n");

	if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 33L)))
		cleanup("No graphics.library!\n");
#endif

	if(rc=SetupScreen())
		cleanup("Error Setting Screen");

	if(rc=OpenEprommerWindow())
		cleanup("Error Open Eprommer Window");

	strcpy(title,&vers[7]);
	strcat(title," © Bob,Udi,Carsten");
	SetWindowTitles(EprommerWnd,title,(char *)-1);

	if(!(PEmemBase = AllocMem(MAXDATABUFFER,MEMF_ANY+MEMF_CLEAR)))
		cleanup("No Memory!\n");

#ifdef DEBUG_PROG
    printf("RAM:%lx\n",PEmemBase);
#endif

	/* Initialization for FileRequester */
	if(!(aslreq=AllocAslRequestTags(ASL_FileRequest,ASL_Hail,"FileRequester",TAG_DONE)))
		cleanup("Allocate ASL Requester failed");

	/* Initialization for Lock Requester */
	if(!(plockreq=AllocMem(sizeof(struct Requester),MEMF_ANY+MEMF_CLEAR)))
	{
		cleanup("Can't alocate memory for 'Lock Requester'");
		return;
	}
	plockreq->LeftEdge=-1;
	plockreq->TopEdge=-1;
	plockreq->Width=0;
	plockreq->Height=0;


	/* Initialization for timer */
	if (OpenTimer()) 
		cleanup("No timer!");

	/* Clear Load,Save Paths */
	SFullPath[0] = LFullPath[0] = '\0';

	/* Load/Set default Configuration */
	def_flag = TRUE;
	if(fpconfig=fopen(CONFIG_ENV,"r"))
	{
		if(fread(&conf,sizeof(struct Config),1,fpconfig) != 1)
		{
			simplerequest("Error reading '%s'",CONFIG_ENV);
			perror(CONFIG_ENV);
		}
		else
		{
			/* Take latest used Directories */
			strcpy(LDir,conf.loaddir);
			pos = ((struct StringInfo *)(EprommerGadgets[GD_LoadFile]->SpecialInfo))->Buffer;
			strcpy(pos,LDir);

			strcpy(SDir,conf.savedir);
			pos = ((struct StringInfo *)(EprommerGadgets[GD_SaveFile]->SpecialInfo))->Buffer;
			strcpy(pos,SDir);

			RefreshGList(EprommerGadgets[GD_LoadFile], EprommerWnd, NULL, 1);
			RefreshGList(EprommerGadgets[GD_SaveFile], EprommerWnd, NULL, 1);
		
			waitcnt = conf.waitcnt;

			def_flag=FALSE;
		}
		fclose(fpconfig);
		fpconfig=NULL;
	}

		/* determine CIA delay. Is there any commandline Option ? */
	if(argc>=2)
		waitcnt=MAX(1,atoi(argv[1]));
	else
	{
		if(def_flag)
			checkspeed(); /* No, so calculate the CIA-delay */
	}

	/* Defaults, if reading Prefs-File failed */
	if(def_flag)
	{
		conf.epromtype = EP_2716;			/* 2716 */
		conf.dataformat = BINARY;			/* set DataFormat to BINARY */
		conf.bufferfragmented = FALSE;		/* Buffer Fragmentation allowed */
		conf.clearbuffer = FALSE;			/* clear buffer first before load */
		conf.numericalbase = CY_DECHEX;		/* Set Default Input to Decimal and Hexadecimal */	
		conf.inforeq = TRUE;				/* Requester appears for default */
		conf.counting = TRUE;				/* Show address counting for default */
		conf.defepsize = TRUE;				/* Set default Eprom Size */
		conf.loaddir[0] = '\0';				/* Delete Load directory */
		conf.savedir[0] = '\0';				/* Delete Save directory */
		conf.waitcnt    = waitcnt;			/* CIA Delay */
	}

	setepromtype(conf.epromtype);

      /* Initialize hardware */
	InitPorts();

	GT_SetGadgetAttrs(EprommerGadgets[GD_EpromType],EprommerWnd,NULL,GTMX_Active,EPType,TAG_DONE);
	GT_SetGadgetAttrs(EprommerGadgets[GD_DataFormat],EprommerWnd,NULL,GTCY_Active,conf.dataformat,TAG_DONE);

		/* Prepare Address counting (Busy/Abort Window) */
	IT_counting = BusyIText[1];  /* Copy original IntuiText structure */
		/* X,Y-Position depends on Font Size. */
		/* OffX,OffY,Font,FontX,FontY are calculated in gad_eprommer.c during SetupScreen() */
	IT_counting.ITextFont = Font;
	IT_counting.LeftEdge  = OffX + ((( FontX * IT_counting.LeftEdge ) + 4 ) / 8 ) - ( IntuiTextLength( &IT_counting ) >> 1 );
	IT_counting.TopEdge   = OffY + ((( FontY * IT_counting.TopEdge  ) + 4 ) / 8 ) - ( Font->ta_YSize >> 1 );
	

		/* At Program End: MiscBase don't have to be closed - there's no 'CloseResource()' */
	if (!(MiscBase = (struct Library *)OpenResource(MISCNAME)))
		cleanup("Can't open 'misc.resource' !\n");

	if(owner = AllocMiscResource(MR_PARALLELPORT,"Eprommer"))
	{	
		if(!(twogadrequest("OK|CANCEL","Parallel Port already allocated by:%s\nDo you want continue ?",owner)))
			cleanup("PAR: busy");
	}
	else
		pp_flag = TRUE;

	if(pp_flag)
	{	
		if(owner = AllocMiscResource(MR_PARALLELBITS,"Eprommer"))
		{		
			if(!(twogadrequest("OK|CANCEL","Parallel Port Bits already allocated by:%s\nDo you want continue ?",owner)))
				cleanup("PAR: busy");
		}
		else
			ppb_flag = TRUE;
	}
	
/*
 * Event loop
 */
	rc = TRUE;
    while( rc ) 
	{
    	WaitPort( EprommerWnd->UserPort );
	    rc=HandleEprommerIDCMP();
	}

		/* Take latest used Directories */
	strcpy(conf.loaddir,LDir);
	strcpy(conf.savedir,SDir);

	if( (!(fpconfig=fopen("ENV:Eprommer.prefs","w"))) ||
		(fwrite(&conf,sizeof(struct Config),1,fpconfig) != 1))
		simplerequest("Error. Can't write Settings to 'ENV:Eprommer.prefs'");
	
	if(fpconfig)
		fclose(fpconfig);

	cleanup(NULL);
	
	return(0);
}
