Xinu7/src/cmd/download/lsi11/src/eul.c

Compare this file to the similar file:
Show the results in this format:

/*----------------------------------------------------------------------*/
/*									*/
/*	eul	--  Upload from PDP-11 to UNIX machine over Ethernet	*/
/*									*/
/* This program uploads a memory image from a PDP-11 into a core11 file	*/
/* on a UNIX machine.							*/
/*									*/
/* Configuration -							*/
/*	VAX tty line connected to LSI 11 ODT (console) port		*/
/*									*/
/* Usage:								*/
/*	upload [-lx] [-o filename] [ [minaddr] max addr ]		*/
/*									*/
/* Authors:								*/
/*	Version 1 : Serial Uploader					*/
/*		R. Brown & C. Kent					*/
/*		Purdue University/CS Department				*/
/*		September, 1981						*/
/*		mods by Comer, 11/81-11/83				*/
/*	Version 2 : Ethernet Uploader					*/
/*		Tom Stonecypher						*/
/*		Purdue University CS Department				*/
/*		November, 1986						*/
/*									*/
/*----------------------------------------------------------------------*/

/* UNIX include files */
#include "/usr/include/stdio.h"
#include "/usr/include/sgtty.h"
#include "/usr/include/signal.h"
#include "/usr/include/errno.h"
#include "/usr/include/setjmp.h"
#include "/usr/include/errno.h"
#include "/usr/include/sys/types.h"
#include "/usr/include/sys/file.h"
#include "/usr/include/ctype.h"

/* 4.3 Network include files */
#include "/usr/include/netinet/in.h"
#include "/usr/include/sys/socket.h"
#include "/usr/include/netdb.h"

/* Xinu domain include files */
#include <core11.h>
#include <a.out.h>
#include <baud.h>
#include <getdev.h>
#include <ethdown.h>
#include <lsiul.h>
#include <dlpack.h>
#include <upload.h>
#include "../eload/src/.version"
#include "../eload/src/.entry"

char 	*msgval;
char	*Openfiles[20];
struct	core11	Corehead;
jmp_buf	env;			/* environment to return to on timeout	*/
static	char *alarm_msg;	/* message printed when alarm signalled	*/
struct	arglist	A;		/* parsed argument values		*/
char	*lockfile;		/* path name of lockfile		*/
struct	sgttyb	VAXttyb;
char	*Memory;		/* the LSI-11 memory image uploaded	*/
char	*index();
int	touch();
extern	int	errno;
int	Sfd;			/* socket descriptor for udp read/writes*/

/*
 * MAIN -- upload memory image from PDP-11 to UNIX machine
 */
main ( argc, argv )
int argc;
char *argv[];
{
	char	*machine, *cputype;
	u_long	lsi_ip;
	int	i, core_fd;

	lockfile = argv[--argc];
	machine = argv[--argc];
					/* cputype = get_cputype(machine); */
	procargs(argc, argv);		/* process arguments into struct A */
	initsigs();

	if ((core_fd = creat(A.corefile, 0666)) < 0) {
		perror(A.corefile);
		exit(1);
	}
	dup2(core_fd, COREFD);	/* write to the core file using COREFD	*/
	Openfiles[DEVFD] = get_name(machine);
	Openfiles[COREFD] = A.corefile;

	/*
	 * allocate memory array - this	written	to the core file when the
	 * program terminates
	 */
	if (A.verbose)
		fprintf(stderr,"Allocating %d (0%o) bytes for image\n",
			MAXADDR+1, MAXADDR+1);
	Memory = (char *)malloc(MAXADDR+1);
	for ( i=0 ; i <= MAXADDR ; i++ )
		Memory[i] = (char)0;
	Corehead.c_zero1 = Corehead.c_zero2 = 0;
	Corehead.c_zero3 = Corehead.c_zero4 = Corehead.c_zero5 = 0;
	Corehead.c_magic = COREMAGIC;

	getodt();
	message("getting registers...\n",0,0);
	getregs(Corehead.c_regs);
	getpsw(&Corehead.c_psw);

	getudp_port(&lsi_ip, machine);

	/* attempt to perform ethernet upload */
	if (lsi_ip == 0 || ethuload(lsi_ip) != ETH_OK ) {
		/* can't do ethernet upload */
		message("performing serial upload...\n",0,0);
		seruload();
	}
	Corehead.c_size = A.highaddr+1 - A.lowaddr;
	write(COREFD, &Corehead, sizeof Corehead);
	write(COREFD, Memory, A.highaddr+1);
	touch(lockfile);
	message("\rdone        \n",0,0);
	callexit(0);
}

/*----------------------------------------------------------------------
 * initsigs -- trap software signals so that lines can be restored to
 * their original state and the core file can be written
 *----------------------------------------------------------------------
*/
initsigs()
{
	int	alarmhandler(), inthandler();

	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
		signal(SIGINT,inthandler);
	if ( signal(SIGTERM,SIG_IGN) !=	SIG_IGN	)
		signal(SIGTERM,inthandler);
	if ( signal(SIGQUIT,SIG_IGN) !=	SIG_IGN	)
		signal(SIGQUIT,inthandler);
	signal(SIGALRM,	alarmhandler);
}

/*----------------------------------------------------------------------
 *  seruload -- upload an LSI 11 memory image serially
 *----------------------------------------------------------------------
 */
seruload()
{
	int ldrfd, bytes;
	int base,length,i;
	struct exec bhdr;
	short entry;

	getodt();
	/* already got the contents of the registers */
	if (A.verbose)
		message("Getting a.out header\n",0,0);
	ldrfd = openfile(A.bootloader, O_RDONLY);
	gethdr(ldrfd,&bhdr);
	bytes =	bhdr.a_text+bhdr.a_data+STACKLEN;
	if (A.verbose)
		message("Number of bytes = %d\n",bytes,0);
	if ( !A.noload ) {
		message("Deposit boot loader...\n",0,0);
		entry =	odtload(ldrfd,&bhdr,0,Memory);
	} else {
		message("Not loading boot loader...\n",0,0);
		read ( ldrfd, Memory, bytes*sizeof(lsiword) );
	}
	/*
	 * Set up arguments to upl and start it	running...
	 */
	getodt();
	message("Starting load of 0%o-0%o...\n",A.lowaddr,A.highaddr);
	base = (A.lowaddr < bytes ? bytes :	A.lowaddr);
	length = (A.highaddr-base + 1)/2;
	if (A.verbose)
		message("Setting argument registers\n",0,0);
	setreg(0,base);
	setreg(1,length);
	setreg(4,CSRBASE);
	setreg(6,base);
	if (A.verbose)
		message("Starting LSI 11 execution...\n",0,0);
	sendodt("P",TRUE);
	slowread(Memory);
}

/*
 *======================================================================
 * procargs - parse and check arguments, modifying global A structure
 *======================================================================
 */
procargs(argc, argv)
int argc;
char *argv[];
{
	int	arg, i, unswitched, afd;
	struct	exec	ahdr;
	/*
	 * assign default values
	 */
	A.uploader = NULL;
	A.lowaddr = 0;
	A.highaddr = MAXADDR;
	A.corefile = DEFFILE;
	A.baudrate = B9600;
	A.class = NULL;
	A.machnum = GD_ANYDEV;
	A.devflags = GD_MYDEV;
	A.bootloader = LOADER;
	A.corefile = DEFFILE;
	A.verbose = FALSE;
	A.silent = FALSE;
	A.noload = FALSE;
	unswitched = 0;
	for ( arg=1 ; arg<argc ; arg++ ) {
		if ( argv[arg][0] == '-' )
			switch ( argv[arg][1] ) {
			case 'v':
				A.verbose = TRUE;
				break;
			case 's':
				A.silent = TRUE;
				break;
			case 'n':
				A.noload = TRUE;
				break;
			case 'o':
				A.corefile = argv[arg++];
				break;
			case 'a':
					afd = openfile(argv[arg++],0);
					gethdr(afd,&ahdr);
					A.lowaddr = ahdr.a_text;
					A.highaddr = ahdr.a_text+ahdr.a_data+
							ahdr.a_bss;
					break;
			default:	/* generic uploader already parsed: */
				break;	/* don't complain about arguments */
			}
	else /* there's no dash in front */
		switch ( unswitched++ )	{
		case 0:
			A.highaddr = atoi(argv[arg]);
			message("highaddr=%o\n",A.highaddr,0);
			break;
		case 1:
			A.lowaddr = A.highaddr;
			A.highaddr = atoi(argv[arg]);
			message("lowaddr=%o\n",A.lowaddr,0);
			message("highaddr=%o\n",A.highaddr,0);
			break;
		default:
			break;		/* don't complain -- already parsed */
		}
	}
}

/*
 *==========================================================
 * openfile - try to open file,	exit with message if error
 *==========================================================
 */
openfile(name,mode)
char *name;
int mode;
{
	int fd;
	settimer(10, "open timed out");
	if (setjmp(env) == EINTR)
		callexit(1);
	if ((fd=open(name,mode)) < 0 ) {
		perror(name);
		exit(1);
	}
	canceltimer();
	Openfiles[fd] =	name;
	return(fd);

}

/*
 *=========================================
 * alarmhandler - return from alarm calls
 *=========================================
 */
alarmhandler()
{
	signal(SIGALRM, alarmhandler);
	if (alarm_msg != NULL)
		fprintf(stderr, "%s\n", alarm_msg);
	longjmp(env, EINTR);
	/* not reached */
}

/*
 *=====================================
 * inthandler -- interrupt processing
 *=====================================
 */
inthandler()
{
	write(COREFD,&Corehead,sizeof Corehead);
	write(COREFD,Memory,A.highaddr+1);
	touch(lockfile);
	callexit(2);
}
/*
 *=======================================
 * callexit - restore ttymodes and exit
 *=======================================
 */
callexit(ret)
int ret;
{
	/* stty(DEVFD,&VAXttyb); */
	exit(ret);
}

/*
 *==========================================
 * odtload - load bootstrap loader via ODT
 *==========================================
 * input state:	after ODT prompt
 * output state: same
 *
 * fdin	- a.out	file to	download
 * hdr - a.out header for file to load
 * loc - location to begin loading (overrides default 0)
 * outaddr - ptr to int	array to hold current contents,	or NULL
 */
odtload(fdin, hdr, loc,	outaddr)
int fdin;
int loc;
lsiword	*outaddr;
struct exec *hdr;
{
	int addr, lsival;
	int i, length;
	lsiword	word;
	char buf[32], filebuf[BUFSIZ],c,*val;
	FILE *infile;

	infile = fdopen(dup(fdin),"r");
	fseek(infile,sizeof *hdr,0);
	setbuf(stderr,NULL);
	setbuf(infile,filebuf);
	length = hdr->a_text+hdr->a_data+STACKLEN;
	addr = LOADSTART;
	do {
		fread(&word, sizeof word, 1, infile);
		if ( addr == LOADSTART ) {
			sprintf(buf,"%o/",addr+loc);
			sendodt(buf,TRUE);
			readuntil(" ?", buf, 7, 32 );
			val = buf;
		} else {
			sendodt("\n",TRUE);
			readuntil(" ?", buf, 7, 32);
			val = index(buf,'/')+1;
		}
		if ( outaddr !=	NULL ) {
			sscanf(val,"%o ",&lsival);
			outaddr[(addr+loc)/2] =	lsival & 0xffff;
		}
		sprintf(buf, "%o", word	& 0xffff);
		sendodt(buf,TRUE);
		if ( !A.silent && !A.verbose )
			displayval(addr);
		addr +=	sizeof (lsiword);
	} while	( word != 0177777 );
	if ( !A.silent && !A.verbose )
		fprintf(stderr,"\r");
	sendodt("\r",TRUE);
	readuntil("@", buf, 5, 32);
	/*
	 * now,	run this half to load the other half faster
	 */
	fseek(infile,sizeof(struct exec),0);
	setreg(1,length);
	setreg(-1,0340);
	setreg(7,LOADSTART);
	sendodt("P",FALSE);
        sleep(1);
	for ( i=0 ; i<length ; i++ ){
		c = getc(infile);
		if (A.verbose){
			fprintf(stderr,"AT: %06o ", i&0xffff);
			fprintf(stderr,"OUT: %03o ",c&0xff);
		}
		write(DEVFD,&c,1);
		read(DEVFD,&c,1);
		if (A.verbose) fprintf(stderr,"IN: %03o\n",c&0xff);
		if ( i>=addr && outaddr != NULL )
			*(((char *)outaddr)+i) = c;
	}
	readuntil("@", buf, 8, 32);
	return(addr);
}
/*
 *===============================================
 * getpsw - get	the current contents of	the PSW
 *===============================================
 */
getpsw(psw)
short *psw;
{
	char buf[60];
	char	*reply;

	sendodt("$S/",TRUE);
	readuntil(" ?", buf, 5, 60);
	reply = index(buf,'/');
	sscanf( ((0==(int)reply) ? buf : reply+1),"%o ",psw);
	sendodt("\r",TRUE);
	readuntil("@", buf, 5, 60);
}
/*
 *==========================================
 * getregs - get contents of the registers
 *==========================================
 */
getregs(regs)
short *regs;
{
	int r;
	int lsival;
	char buf[32], *val;
	for ( r=0 ; r<REGISTERS	; r++ )	{
		if ( r==0 ) {
			sprintf(buf,"R%d/",r);
			sendodt(buf,TRUE);
			readuntil(" ?", buf, 7, 32);
			val = buf;
		} else {
			sendodt( "\n",TRUE);
			readuntil(" ?", buf, 7, 32);
			val = index(buf,'/')+1;
		}
		sscanf(val,"%o ",&lsival);
		regs[r]	= lsival & 0xffff;
		if ( !A.silent && !A.verbose )
			displayval(r);
	}
	if ( !A.silent && !A.verbose )
		fprintf(stderr,"\r");
	sendodt("\r",TRUE);
	readuntil("@", buf, 5, 32);
}

/*
 *===============================
 * getodt - get ODT's attention
 *===============================
 * Input state - undefined -- nothing valid in memory
 * Output state - immediately after ODT prompt
 */
getodt()
{
	char buf[32];

	message ("getting odt...\n",0,0);
	ioctl(DEVFD, TIOCSBRK, NULL);
	usleep(300000);
	ioctl(DEVFD, TIOCCBRK, NULL);
	readuntil("@",buf,10,32);
	setmem(SR0_ADDR, DISABLE_MMU);	/* disable memory management */
}

/*
 *==================================================
 * readuntil - read from line until some character
 *==================================================
 */
readuntil ( ch, buf, time, nchars )
int time, nchars;
char *ch, *buf;
{
	settimer(time, "read timed out");
	if (setjmp(env) == EINTR) {
		fprintf(stderr, "readuntil did not find %s\n", unctrl(*ch));
		callexit(1);
	}
	if ( A.verbose ) {
		fprintf(stderr,"IN: "); fflush(stderr);
	}
	do {
		if ( read(DEVFD, buf, 1) != 1 ) {
			perror(Openfiles[DEVFD]);
			callexit(1);
		}
		if (A.verbose )
			fprintf(stderr,"%s",unctrl(*buf));
	} while	( (*buf == '\0' || index(ch,*buf++) == NULL) &&
		  --nchars > 0 );
	*buf = EOS;
	canceltimer();
	if ( A.verbose )
		fprintf(stderr,"\n");
}


/*
 *==============================================
 * setmem - preload a mem location through ODT
 *==============================================
 */
setmem(loc, value)
int loc, value;
{
	char buf[32];

        sprintf(buf, "%o/", loc);
	sendodt(buf, TRUE);
	readuntil(" ?", buf, 5, 32);
	sprintf(buf, "%o\r", value&0xffff);
	sendodt(buf, TRUE);
	readuntil("@", buf, 5, 32);
}

/*
 *==============================================
 * sendodt - send a message to ODT half duplex
 *==============================================
 */
sendodt(msg, wait)
char *msg;
Bool wait;
{

	char buf[32], *ptr, tmpstr[2];

	if ( A.verbose ) {
		fprintf(stderr,"OUT: ");
		for ( ptr=msg ; *ptr!=EOS ; ptr++ )
			fprintf(stderr,"%s",unctrl(*ptr));
		fprintf(stderr,"\n");
	}
	while (*msg!=EOS) {
		write(DEVFD,msg,1);
                if ( wait ) {
			tmpstr[0] = *msg;
			tmpstr[1] = EOS;
                        readuntil(tmpstr, buf, 5, 32);
		}
		msg++;
	}
}

/*
 *==========================================
 * setreg - preload a register through ODT
 *==========================================
 */
setreg(reg, value)
int reg, value;
{
	char buf[32];

	if (A.verbose)
		message("Setting register %d to 0%o\n",reg,value&0xffff);
	if ( reg >= 0 )
		sprintf(buf,"R%d/",reg);
	else
		sprintf(buf,"$S/");
	sendodt(buf,TRUE);
	readuntil(" ?", buf, 5, 32);
	sprintf(buf,"%o\r",value&0xffff);
	sendodt(buf,TRUE);
	readuntil("@", buf, 5, 32);
}

/*
 *=================================================
 * message - conditionally display status message
 *=================================================
 */
message(str,arg,arg2)
int	arg, arg2;
char	*str;
{
	if ( !A.silent && !A.verbose)
		fprintf(stderr,str,arg,arg2);
	fflush(stderr);
}

/*
 *================================
 * gethdr - read an a.out header
 *================================
 */
gethdr(fd,hdr)
int fd;
struct exec *hdr;
{
	if ( read(fd, hdr, sizeof *hdr)	!= sizeof *hdr)	{
		fprintf(stderr,"%s: EOF	reading	a.out header\n",Openfiles[fd]);
		callexit(1);
	}
	if ( hdr->a_magic != A_MAGIC1 )	{
		fprintf(stderr,"%s: wrong magic	number\n",Openfiles[fd]);
		callexit(1);
	}
}

/*
 *============================================
 * displayval -	display	a number on one	line
 *============================================
 */
displayval(val)
int val;
{
        fprintf(stderr,"\r%o         ",val);
	fflush(stderr);
}

/*
 *======================================================
 * slowread - receive packets from low speed serial line
 *======================================================
 */
slowread(mem)
lsiword	mem[];				/* treat memory as array of	*/
{					/* shorts (historical)		*/
	struct spkt packet;
	FILE *in;
	lsiword	sum;
	int i;
	in = fdopen(DEVFD,"r");
	message("Reading LSI 11 memory...\n",0,0);
	do {
		do {
			getpack(in,&packet);
			if ( packet.len	<= MAXSERPKT ) {
				sum = packet.len + packet.base;
				for ( i=0 ; i<packet.len ; i++ )
					sum += packet.buf[i];
				if ( A.verbose )
					message("exp./actual sum=0%o/0%o\n",
						sum, packet.sum);
				if ( sum == packet.sum )
					break;
			}
			if ( !A.silent )
				fprintf(stderr,"NAK\n");
			write(DEVFD,"n",1);
		} while	( TRUE ) ;
		write(DEVFD,"y",1);
		for ( i=0 ; i<packet.len ; i++ )
			mem[i+(packet.base/sizeof(lsiword))] = packet.buf[i];
		if ( !A.silent )
			displayval((int)(packet.base&0xffff));
	} while	( packet.len !=	0 );
	if ( !A.silent ) {
		displayval(0);
		fprintf(stderr,"\r");
	}
}

/*
 *=======================================
 * getpack - read next packet from line
 *=======================================
 */
getpack(fd,pptr)
FILE *fd;
struct spkt *pptr;
{
	char c;
	Bool success;
	int i;
	/*
	 * wait	for start of heading character
	 */
	success	= FALSE;
	do {
		while (	(c=getc(fd)) !=	SOH )
			;	/* NULL */
		if (A.verbose)
			fprintf(stderr,"SOH\n");
		pptr->len = getword(fd);
		pptr->base = getword(fd);
		if ( A.verbose )
			fprintf(stderr,"len, base = %o %o\n",pptr->len,
			pptr->base);
		if ( pptr->len > MAXSERPKT )
			return;
		for ( i=0 ; i<pptr->len	; i++ )
			pptr->buf[i] = getword(fd);
		pptr->sum = getword(fd);
		success	= TRUE;
	} while	( !success );
}
getword(fd)
FILE *fd;
{
	lsiword	word;
	word = getbyte(fd);
	word = word | (getbyte(fd)<<8);
	return(word);
}
getbyte(fd)
FILE *fd;
{
	char c;
	c = getc(fd);
	if (A.verbose)
		fprintf(stderr,"%03o ",c&0xff);
	if ( c==ESC ) {
		c = getc(fd);
		if (A.verbose)
			fprintf(stderr,"(%03o) ",c&0xff);
	}
	return(c&0xff);
}

/*
 *============================================================================
 * getudp_port - get a udp port connected to the lsi port for packet transfer
 *============================================================================
 */
getudp_port(lsi_ip, machine)
u_long	*lsi_ip;
char	*machine;
{
	struct	sockaddr_in sin, sout;
	struct	hostent	*phost;
	char	*hostname;

	hostname = get_hname(machine);
	if (isdigit(*hostname))	/* IP dot notation instead of name */
		*lsi_ip = inet_addr(hostname);
	else {			/* hostname format */
		phost = gethostbyname(hostname);
		if ((char *)phost != NULL && phost->h_addrtype == AF_INET)
			bcopy(phost->h_addr, lsi_ip, sizeof lsi_ip);
		else
			*lsi_ip = 0;
	}
	if ((Sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
		perror("creating socket");
		callexit(1);
	}
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	if (bind(Sfd, (caddr_t) &sin, sizeof(sin)) < 0) {
		perror("bind");
		callexit(1);
	}
	sout.sin_family = AF_INET;
	sout.sin_addr.s_addr = *lsi_ip;
	sout.sin_port = htons((u_short) DLUDP);

	if (connect(Sfd, &sout, sizeof(sout)) < 0) {
		perror("connect");
		callexit(1);
	}
}

/*==========================================
 * start_eload - init lsi and start up eload
 *==========================================
 * input  state: after the odt prompt
 * output state: undefined (waiting for an ack from eload)
 */
start_eload(lsi_ip)
int	lsi_ip;
{
	message("starting resident loader...\n",0,0);
	setreg(PSW, INTDISABLE);
	map_mem();		/* phys 7 to virt 0, I/O to virt 7 */
	setreg(SP, ETH_INITSP);
	setreg(PC, ETH_INITPC);
	sendodt("P", TRUE);
}

/*
 *========================================================
 * map_mem - set up virtual memory before starting eload
 *========================================================
 */
map_mem()
{
	setmem(PAR0_ADDR, PAR0_VAL);	/* par0 -> physical 7 */
	setmem(PAR7_ADDR, PAR7_VAL);	/* par7 -> I/O space */
	setmem(PDR0_ADDR, PDR_VAL);	/* page len=8K, RW access */
	setmem(PDR7_ADDR, PDR_VAL);
	setmem(SR3_ADDR, 0);		/* 18-bit bus addresses	*/
	setmem(SR0_ADDR, ENABLE_MMU);
}

struct	dl_pack	ackdl;

/*
 *=============================================
 * getack - get an ack from eload over ethernet
 *=============================================
 */
struct	dl_pack	*getack()
{
	char	*ackmsg;

	settimer(ACKTIME, NULL);
	if (setjmp(env) == EINTR)		/* timed out		*/
		return(NULL);
	if (read(Sfd, &ackdl, sizeof ackdl) < 0) {
		perror("udp packet read");
		callexit(1);
	}
	canceltimer();
	if (A.verbose) {
		switch (ackdl.dl_hdr.dl_op) {

		case DLACK:
			ackmsg = "ACK";
			break;
		case DLNAK:
			ackmsg = "NAK";
			break;
		case DLRAK:
			ackmsg = "ready-ack";
			break;
		case DLQAK:
			ackmsg = "quit-ack";
			break;
		case DLEAK:
			ackmsg = "uploaded memory packet";
			break;
		case DLENK:
			ackmsg = "upload request out of bounds";
			break;
		default:
			ackmsg = "invalid ack command";
			break;
		}
		fprintf(stderr,"IN(getack): %s, addr = 0%o\n", ackmsg,
			ntohl(ackdl.dl_hdr.dl_addr));
	}
	return(&ackdl);
}

/*
 *=============================================
 * ethuload - upload memory image over ethernet
 *=============================================
 */
ethuload(lsi_ip)
int lsi_ip;
{
	u_short	addr,	len;
	int	iNak,	version, eloadfd;
	struct	dl_pack packet,	*pDlAck;

	start_eload(lsi_ip);
	build_dlpack(DLID, 0, 0, &packet);
	usleep(400000);
	for (iNak = 0; iNak < ETHRETRY; iNak++ ) {
		if (iNak > 0)
			fprintf(stderr,"\rretrying...");
		send_dlpack(Sfd, &packet);
		pDlAck = getack();
		if (pDlAck != NULL && pDlAck->dl_hdr.dl_op == DLRAK)
			break;
	}
	if (iNak == ETHRETRY) {
		message("can't start eload\n",0,0);
		return(ETH_FAILED);
	}
	if ((version = pDlAck->dl_hdr.dl_vers & LOWBYTE) != VERSION &&
	     version != ROMVERS && !A.silent)
		fprintf(stderr,"version mismatch, trying upload anyway\n");
	for ( addr = A.lowaddr ; addr <= A.highaddr  ; addr += len ) {
		len = addr+DLMAXD > A.highaddr ? A.highaddr-addr+1 : DLMAXD;
		build_dlpack(DLEXT, len, addr, &packet);
		for (iNak = 0; iNak < ETHRETRY; iNak++ ) {
			if (iNak > 0)
				fprintf(stderr,"\rretrying...");
			send_dlpack(Sfd, &packet);
			pDlAck = getack();
			/* test for ACK & correct address sequence number */
			if (pDlAck != NULL)
				if ( pDlAck->dl_hdr.dl_op == DLEAK &&
				    ntohl(pDlAck->dl_hdr.dl_addr) == addr)
					break;
				else if (pDlAck->dl_hdr.dl_op == DLENK)
					break;	/* done uploading */
		}
		if (pDlAck->dl_hdr.dl_op == DLENK) {
			A.highaddr = addr - 1;
			break;
		}
		if (iNak == ETHRETRY) {
			message("ethernet upload failed\n",0,0);
			return(ETH_FAILED);
		}
		if ( (addr % 020000) == 0 )
			message("\rpage %d       ", addr/020000, 0);
		/* copy uploaded memory into place */
		len = (int) ntohs(pDlAck->dl_hdr.dl_len);
		bcopy(pDlAck->dl_pt.dl_data, &Memory[addr], len);
	}
	/* instruct eload to halt lsi */
	packet.dl_pt.dl_srt.dl_entry = 0; /* dummy value */
	packet.dl_pt.dl_srt.dl_delay = htonl(NOAUTOSTART);
	build_dlpack(DLSRT, 0, 0, &packet);
	for ( iNak = 0; iNak < ETHRETRY; iNak++ ) {
		send_dlpack(Sfd, &packet);
		pDlAck = getack();
		if (pDlAck != NULL && pDlAck->dl_hdr.dl_op == DLQAK)
			break;
	}
	/* don't complain if got no ACK */
	return(ETH_OK);
}

/*
 *=============================================================
 * build_dlpack - construct a packet in ether download format
 *=============================================================
 */
build_dlpack(op, datalen, addr, packet)
char	 op;
unsigned short	 datalen;
unsigned long	 addr;
struct	 dl_pack *packet;
{
	packet->dl_hdr.dl_op = op;
	packet->dl_hdr.dl_vers = VERSION;

	switch (op) {
	case DLEXT:
		packet->dl_hdr.dl_len = htons(datalen);
		packet->dl_hdr.dl_addr = htonl(addr);
		break;
	case DLID:			/* do nothing, don't complain */
	case DLSRT:
		break;
	default:
		fprintf(stderr, "build_dlpack: invalid output packet type\n");
		break;
	}
}

/*
 *============================================================
 * send_dlpack - send a downloader format packet out via UDP
 *============================================================
 */
send_dlpack(sockfd, dlpacket)
int sockfd;
struct dl_pack *dlpacket;
{
	unsigned short	len;
	unsigned long	addr;
	unsigned int	pktsize;

	switch (dlpacket->dl_hdr.dl_op) {
	case DLSRT:
		pktsize = sizeof(struct dl_header) +
					sizeof(struct dl_srtval);
		break;
	case DLEXT:
	case DLID:
	default:
		pktsize = sizeof(struct dl_header);
		break;
	}
	if (A.verbose) {
		fprintf(stderr,"packet:  ");
		switch(dlpacket->dl_hdr.dl_op) {

		case DLEXT:
			addr =  ntohl(dlpacket->dl_hdr.dl_addr);
			len  =  ntohs(dlpacket->dl_hdr.dl_len);
			fprintf(stderr, "extract, addr = %#o, len = %#o\n",
				addr, (int) len);
			break;
		case DLSRT:
			fprintf( stderr,
				"start program, delay = %d, entry pt = %o\n",
				ntohl(dlpacket->dl_pt.dl_srt.dl_delay),
				ntohl(dlpacket->dl_pt.dl_srt.dl_entry) );
			break;
		case DLID:
			fprintf(stderr, "ID packet\n");
			break;
		default:
			fprintf(stderr, "send_dlpack: invalid packet type\n");
			return;
		}
	}
	if (send(sockfd, dlpacket, pktsize, 0) < 0) {
		perror("udp packet send");
		callexit(1);
	}
}