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

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

/*----------------------------------------------------------------------*/
/*									*/
/*	edl	--  Download to PDP-11 over Ethernet			*/
/*									*/
/* This program takes PDP-11 a.out format files and downloads them	*/
/* into a PDP-11.							*/
/*									*/
/* Usage:								*/
/*  (This program is exec'd by download; the following arguments are	*/
/*   accepted by download:)						*/
/*  download [file] [-cCLASS] [-lLSI#] [-a[delay]]			*/
/*	     [-Bbaudrate] [-a[# of Secs]] [-h] [-v] [-s]		*/
/*									*/
/* Authors:								*/
/*	Version One: R. Brown, Doug Comer				*/
/*	Purdue University/CS Deptartment				*/
/*	September, 1981 - Nov. 1983					*/
/*									*/
/*      Version Two: Dan Hachigian					*/
/*		     Doug Comer, eload (LSI side)			*/
/*	Modified to handle ethernet downloading, February, 1986		*/
/*	Modified to use new device database alloc. scheme, Spring 86	*/
/*	Significant structure reorganization, Spring 86			*/
/*									*/
/*	Version Three: Tom Stonecypher 10/86				*/
/*		Implemented stateless memory server protocol		*/
/*		Longjmp botches fixed, output format fixed, power-up	*/
/*		Initialization of memory-map unit fixed			*/
/*		Modified to allow download to exec this program		*/
/*		Structure reorganized: serial downloader modularized	*/
/*									*/
/* (c) Copyright, 1982, All rights reserved.				*/
/* (c) Copyright, 1986, All rights reserved.				*/
/*									*/
/*----------------------------------------------------------------------*/

/*----------------------------------------------------------*/
/*    Include files - Unix domain                           */
/*----------------------------------------------------------*/
#include "/usr/include/errno.h"
#include "/usr/include/setjmp.h"
#include "/usr/include/signal.h"
#include "/usr/include/sgtty.h"
#include "/usr/include/stdio.h"
#include "/usr/include/sys/file.h"
#include "/usr/include/sys/types.h"
#include "/usr/include/ctype.h"

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


/*----------------------------------------------------------*/
/*    Include files - XINU domain                           */
/*----------------------------------------------------------*/
#include <a.out.h>
#include <getdev.h>
#include <download.h>
#include <dlpack.h>
#include <lsidl.h>
#include <ethdown.h>
#include "../eload/src/.version"
#include "../eload/src/.entry"

struct	arglist A;
jmp_buf	env;			/* used to return from read time-outs	*/
static	char *alarm_msg;	/* message printed when alarm signalled	*/
char	*Openfiles[MAXFILES];	/* open file names for error reporting	*/
char	*locknm;
extern	int errno;
int	Sfd;			/* socket descriptor for upd write/reads*/

/*
 * MAIN -- download user's program from host into LSI's memory
 */
main ( argc, argv )
int argc;
char *argv[];
{
	char	*machine, *cputype;
	int	eloadfd, fMap, retry;
	u_long	lsi_ip;
	struct	exec ehdr, aouthdr;

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

	Openfiles[DEVFD] = get_name(machine);
	Openfiles[AOUTFD] = A.filename;

	getudp_port(&lsi_ip, machine);

	if (lsi_ip != 0) {		/* do an ethernet download */
	   retry = 0;
	   fMap = MAPMEM;
	   gethdr(AOUTFD, &aouthdr);
	   do {
		if ( A.reloadether || retry == 1 ) {
			if (!A.verbose && !A.silent)
				fprintf(stderr,"%sloading eload...\n",
						(A.reloadether?NULL:"re"));
			eloadfd = openfile(ELOAD, O_RDONLY);
			gethdr(eloadfd, &ehdr);
			serial_load(eloadfd, &ehdr, NOAUTOSTART);
			readuntil('@', 10);
			fMap = NOMAP;
		}
		if (ethload(&aouthdr, lsi_ip, fMap) == ETH_OK)
			break;
		lseek(AOUTFD, sizeof(aouthdr), 0);
	   } while (++retry == 1 && !A.reloadether);
	}
	if (lsi_ip == 0 || retry == 2 || (retry == 1 && A.reloadether)) {
		message("performing serial download...");
		serial_load(AOUTFD, &aouthdr, A.startdelay);
		initstart(&aouthdr);
	}
	touch(locknm);
	message("\rdone         ");
	callexit(0);
}

/*
 *====================================================================
 * procargs - parse and check command line, setup global A structure
 *====================================================================
 */
procargs(argc, argv)
int argc;
char *argv[];
{
	int arg, i;

	A.filename  = DEFFILE;
	A.baudrate = B9600;
	A.class = NULL;
	A.machnum = GD_ANYDEV;
	A.devflags = FALSE;
	A.reloadether = FALSE;
	A.verbose = FALSE;
	A.silent = FALSE;
	A.autostart = FALSE;
	A.startdelay = NOAUTOSTART;

	for ( arg=1 ; arg<argc ; arg++ ) {
		if ( argv[arg][0] == '-' ) {
			switch ( argv[arg][1] ) {
			case 'l':
				A.machnum=atoi(argv[arg]+2);
				break;
			case 'h':
				A.devflags |= GD_HLDDEVS;
				break;
			case 'e':
				A.reloadether = TRUE;
				break;
			case 'v':
				A.verbose = TRUE;
				break;
			case 's':
				A.silent = TRUE;
				break;
			case 'a':
				A.autostart = TRUE;
				A.startdelay = atoi(&argv[arg][2]);
				break;
			default:
				break; /* ignore other args */
			}
		} else
			A.filename = argv[arg];
	}
}

/*
 *=================================================================
 * initsigs - prepare to reset on an interrupt & setup for alarms
 *=================================================================
 */
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);
}

/*=======================================================================
 * serial_load - download an a.out image over serial line
 *=======================================================================
 * input  state: <undefined>
 * output state: a.out in memory, executing if autostart else cpu halted
 */
serial_load(aout_fd, aout_hdr, delay)
	int	aout_fd;
	struct	exec *aout_hdr;
	int	delay;
{
	int ldrfd;
	struct exec ldr_hdr;

	/* place bootloader in memory */
	ldrfd = openfile(LSILOADER, O_RDONLY);
	gethdr(ldrfd, &ldr_hdr);
	getodt();
	setmem(SR0_ADDR, DISABLE_MMU);
	message("\rloading boot program...");
	bootload(ldrfd, &ldr_hdr, 0);
	close(ldrfd);
	start_ldr(aout_hdr, delay);
	slowload(aout_fd, aout_hdr);
}

/*
 *===========================================
 * bootload - load bootstrap loader via ODT
 *===========================================
 * input state: after ODT prompt
 * output state: same
 */
bootload(fdin, hdr, loc)
int fdin;
int loc;
struct exec *hdr;
{
	FILE *infile;
	int length, stklen, i, j, addr;
	lsiword word;
	char buf[32], *stack;

	infile = fdopen(dup(fdin),"r");
	fseek(infile,sizeof(struct exec),0);
	length = hdr->a_text + hdr->a_data;
	addr = LOADSTART;
	/*
	 * load first part of the boot loader at location LOADSTART
	 */
	do {
		fread ( &word, sizeof word, 1, infile);
		if ( addr == LOADSTART ) {
			sprintf(buf,"%o/",addr+loc);
			sendodt(buf,TRUE);
		} else
			sendodt("\n",TRUE);
		readuntil(' ', 7);
		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('@',5);
	/*
	 * send the boot loader in reverse order
	 */
	stack = (char *)malloc(length);
	stklen = 0;
	while ( addr+stklen < length )
		stack[stklen++] = getc(infile);
	setreg(0,MAXADDR);
	setreg(1,stklen);
        setreg(7,LOADSTART);
        setreg(-1,INTDISABLE);
        sendodt("P", FALSE);
	usleep(200000);
	for ( i=stklen-1 ; i>=0 ; i-- ) {
		write(DEVFD, &stack[i], 1);
		if ( A.verbose )
			fprintf(stderr,"%03o ",stack[i]&0xff);
	}
        if ( A.verbose )
                fprintf(stderr,"\r");
	readuntil('@',5);
}

/*==================================================================
 * start_ldr - load arguments to serial downloader and start it up
 *==================================================================
 * input  state: after odt prompt, serial loader in memory
 * output state: downloader running, ready to receive packets
 */
start_ldr(hdr, delay)
struct exec *hdr;
int delay;
{
	setreg(1, hdr->a_bss);
	setreg(2, delay);
	setreg(3, hdr->a_entry & (~1));
	setreg(4, LSIPORT);
        message("\rstarting boot loader...");
	sendodt("P", TRUE);
}

/*===============================================================
 * getserack - get an ack from serial downloader over serial line
 *===============================================================
*/
char getserack()
{
	char	resp;

	settimer(6, "slowload read timed out");
	if (setjmp(env) == EINTR)
		callexit(1);
	if ( read ( DEVFD, &resp, 1) != 1 ) {
		perror(Openfiles[DEVFD]);
		exit(1);
	}
	canceltimer();
	if (resp == NAK)
		fprintf(stderr,"NAK\n");
	else if (A.verbose && resp == ACK)
		fprintf(stderr, "ACK\n");
	else if (A.verbose)
		fprintf(stderr,"response is %s\n",unctrl(resp));
	return(resp);
}

/*============================================================
 * slowload - load user program via packets over serial line
 *============================================================
 * input  state: serial downloader running, ready to receive packets
 * output state: a.out file loaded into memory; a.out running if auto-
 *		 start, before odt prompt if no autostart
 */

FILE *Fastline;
char Fastbuf[BUFSIZ];

slowload(fd,ahdr)
int	fd;
struct exec *ahdr;
{
        int words, addr, length, total;
	FILE *infile;
	lsiword buf[PACKSIZE];
	char resp;

	words = (ahdr->a_text+ahdr->a_data) / sizeof (lsiword);
	Fastline = fdopen(DEVFD,"w");
	setbuf(Fastline,Fastbuf);

	infile = fdopen(fd,"r");
	fseek(infile, (long) sizeof (struct exec), 0);
	addr = LOADSTART;
	while ( words > 0 ) {
		length = (words>PACKSIZE ? PACKSIZE : words);
		fread(buf,sizeof(lsiword),length,infile);
		do
			sendserpack(addr,length,buf);
		while ( getserack() != ACK );

		addr += length * sizeof (lsiword);
		words -= length;
                if ( !A.silent && !A.verbose )
                        displayval(words);
	}
        if ( !A.silent && !A.verbose ) {
                displayval(0);
                fprintf(stderr,"\r");
        }

	do {
	        sendserpack ( addr, 0, buf );
	} while (getserack() != ACK);
}

/*
 *=================================================================
 * initstart - initialize startup register value for serial loads
 *=================================================================
 * input  state: before odt prompt
 * output state: after odt, a.out setup to run
 */
initstart(hdr)
struct exec *hdr;
{
	int i;

	if ( !A.autostart ) {
		readuntil('@', 2);
		setreg(7, (i=hdr->a_entry & (~1)) );
		setreg(6, hdr->a_text + hdr->a_data + hdr->a_bss + 6);
		setreg(-1, INTDISABLE);
		if ( !A.silent )
			fprintf(stderr,"\nStart at 0%o\n",i);
	}
}

/*
 *============================================================================
 * 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 (have memory mapped if requested)
 *============================================================================
 * input  state: after the odt prompt
 * output state: undefined (waiting for an ack from eload)
 */
start_eload(ip_addr, fMapMem)
u_long	ip_addr;
int	fMapMem;
{
	getodt();
	message("\rstarting resident loader...");
	setreg(PSW, INTDISABLE);
	if (fMapMem == MAPMEM)
		map_mem();/* phys 7 to virt 0, I/O to virt 7 */
	else
		load_args(ip_addr);
	setreg(SP, ETH_INITSP);
	setreg(PC, ETH_INITPC & (~1));
	sendodt("P", TRUE);
}

/*
 *===============================================================
 * load_args - put arguments to ELOAD into the appropriate locs
 *===============================================================
 */
load_args(lsi_ip)
u_long lsi_ip;
{
	setmem(0, lsi_ip & 0xffff);
	setmem(2, lsi_ip >> 16 & 0xffff);
}

/*
 *========================================================
 * 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);
}

/*
 *================================
 * 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);
	}
}

/*
 *==========================================================
 * 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);
		callexit(1);
	}
	canceltimer();
	Openfiles[fd] = name;
	return(fd);
}

/*
 *===============================
 * getodt - get ODT's attention
 *===============================
 * Input state - undefined -- nothing valid in memory
 * Output state - immediately after ODT prompt
 */
getodt()
{
	message("\rgetting odt...");
	ioctl(DEVFD, TIOCSBRK, NULL);
	usleep(300000);
	ioctl(DEVFD, TIOCCBRK, NULL);
	/* reset machine state -- necessary after power-up */
	readuntil('@',10);
	setmem(SR0_ADDR, DISABLE_MMU);
	setmem(0, 5);		/* place a reset instruction at 0 */
	setmem(2, 0);		/* place a halt after it */
	setreg(PSW, INTDISABLE);/* (reset disables MMU) */
	setreg(PC, 0);
	sendodt("P", FALSE);	/* reset machine state	*/
	readuntil('@',10);
}

/*
 *==================================================
 * readuntil - read from line until some character
 *==================================================
 */
readuntil ( ch, time )
char	ch;
int	time;
{
	char	lastch, input;

	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);
	}
	input = ' ';
	do {
		lastch = input;
		if ( read(DEVFD, &input, 1) != 1 ) {
			perror(Openfiles[DEVFD]);
			callexit(1);
		}
		if (A.verbose ) {
			fprintf(stderr,"%s",unctrl(input)); fflush(stderr);
		}
	} while ( input == EOS || input != ch);
	canceltimer();
	if ( A.verbose )
		fprintf(stderr,"\n");
	if (ch == '@' && lastch == '^')
		readuntil(ch, time);
}

/*
 *=========================================
 * 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()
{
	touch(locknm);
	callexit(2);
}
/*
 *=======================================
 * callexit - restore ttymodes and exit
 *=======================================
 */
callexit(ret)
int ret;
{
	exit(ret);
}

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

	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 )
                        readuntil(*msg, 10);
		msg++;
	}
}

/*
 *==========================================
 * setreg - preload a register through ODT
 *==========================================
 */
setreg(reg, value)
int reg, value;
{
	char buf[32];
        if ( reg >= 0 )
                sprintf(buf,"R%d/",reg);
        else
                sprintf(buf,"$S/");
	sendodt(buf,TRUE);
	readuntil(' ',5);
	sprintf(buf,"%o\r",value&0xffff);
	sendodt(buf,TRUE);
	readuntil('@',5);
}

/*
 *==============================================
 * setmem - preload a mem location through ODT
 *==============================================
 */
setmem(loc, value)
int loc, value;
{
	char buf[32];
        sprintf(buf,"%o/",loc);
	sendodt(buf,TRUE);
	readuntil(' ',5);
	sprintf(buf,"%o\r",value&0xffff);
	sendodt(buf,TRUE);
	readuntil('@',5);
}

/*
 *=================================================
 * message - conditionally display status message
 *=================================================
 */
message(str)
char *str;
{
	if ( !A.silent ) {
		fprintf(stderr,"%s\n",str);
	}
}

/*
 *===========================================================
 * sendserpack - send a packet to the LSI-11 over serial line
 *===========================================================
 */

#define putwsum(w) putw(w), sum += w
#define rawputc(c) putc(c,Fastline)

sendserpack(addr, length, buf)
int addr, length;
lsiword *buf;
{
	int sum, i;
        if ( A.verbose ) {
                fprintf(stderr,"Sending packet addr=%6o len=%d\n",addr,length);
                for ( i=0 ; i<length ; i++ )
                        fprintf(stderr,"%03o ",buf[i]&0xff);
                fprintf(stderr,"\n");
        }
	rawputc(SOH);
	sum = 0;
	putwsum(addr);
	putwsum(length);
	for ( i=0 ; i<length ; i++ )
		putwsum(buf[i]);
        if ( A.verbose )
                fprintf(stderr,"sum is %d\n",(-sum)&0xffff);
        putwsum((-(lsiword)sum));
	fflush(Fastline);
}
/*
 *=============================================
 * putw - write a word to the high speed line
 *=============================================
 */
putw(word)
lsiword word;
{
	putch((char)(word&0xff));
	putch((char)((word>>8)&0xff));
}
/*
 *=================================================
 * putch - put a character on the high speed line
 *=================================================
 */
putch(ch)
char ch;
{
	if ( ch == ESC || ch == SOH )
		rawputc(ESC);
	rawputc(ch);
}

/*
 *============================================
 * displayval - display a number on one line
 *============================================
 */
displayval(val)
int val;
{
	fprintf(stderr,"\r%d         ",val);
	fflush(stderr);
}
/*
 *==============================================
 * displayreg - display a register on one line
 *==============================================
 */
displayreg(val)
int val;
{
        if ( val < 0 )
		fprintf(stderr,"\rPS         ");
        else
		fprintf(stderr,"\rR%d        ",val);
	fflush(stderr);
}

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;
		default:
			ackmsg = "invalid ack command";
			break;
		}
		fprintf(stderr,"IN(getack): %s, addr = 0%o\n", ackmsg,
			ntohl(ackdl.dl_hdr.dl_addr));
	}
	return(&ackdl);
}

/*
 *===========================================
 * ethload - download program over ethernet
 *===========================================
 */
ethload(aouthdr, lsi_ip, fMapMem)
int lsi_ip, fMapMem;
struct exec *aouthdr;
{
	u_short	addr, len, i;
	int	aoutsize, iNak, version, bsslen;
	struct	dl_pack packet, *pDlAck;

	start_eload(lsi_ip, fMapMem);
	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");
		return(ETH_FAILED);
	}
	if ((version = pDlAck->dl_hdr.dl_vers & LOWBYTE) != VERSION &&
		version != ROMVERS) {
		message("version mismatch");
		return(ETH_FAILED);
	}
	aoutsize = aouthdr->a_text + aouthdr->a_data;
	addr = LOADSTART;
	while (aoutsize > 0) {
		len = aoutsize > DLMAXD ? DLMAXD : aoutsize;
		build_dlpack(DLDEP, 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 && pDlAck->dl_hdr.dl_op == DLACK &&
			    ntohl(pDlAck->dl_hdr.dl_addr) == addr)
				break;
		}
		if (iNak == ETHRETRY) {
			message("ethernet download failed");
			return(ETH_FAILED);
		}
		if (!A.silent && !A.verbose)
			if ((addr % 020000) == 0 ) {
				fprintf(stderr,
					"\rpage %d       ",
					addr/020000);
					fflush(stderr);
			}
		addr += len;
		aoutsize -= len;
	}

	/* now zero out the bss */

	bsslen = aouthdr->a_bss;
	for (i = 0; i <= DLMAXD; i++)
		packet.dl_pt.dl_data[i] = '\0';
	packet.dl_hdr.dl_vers = VERSION;
	packet.dl_hdr.dl_op = DLDEP;
	while (bsslen > 0) {
		len = bsslen > DLMAXD ? DLMAXD : bsslen;
		packet.dl_hdr.dl_len = htons(len);
		packet.dl_hdr.dl_addr = htonl(addr);
		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 && pDlAck->dl_hdr.dl_op == DLACK &&
			    ntohl(pDlAck->dl_hdr.dl_addr) == addr)
				break;
		}
		if (iNak == ETHRETRY) {
			message("ethernet download failed");
			return(ETH_FAILED);
		}
		if (!A.silent && !A.verbose)
			if ((addr % 020000) == 0 ) {
				fprintf(stderr,
					"\rpage %d   ",
					addr/020000);
					fflush(stderr);
			}
		addr += len;
		bsslen -= len;
	}
	/* now instruct eload to perform specified autostart option */
	packet.dl_pt.dl_srt.dl_entry = htonl(aouthdr->a_entry &(~1));
	packet.dl_pt.dl_srt.dl_delay = htonl(A.startdelay);
	build_dlpack(DLSRT, 0, 0, &packet);
	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 == DLQAK)
			break;
	}
	return(iNak < ETHRETRY ? ETH_OK : ETH_FAILED);
}

/*
 *=============================================================
 * 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;
{
	int	i;

	packet->dl_hdr.dl_op = op;
	packet->dl_hdr.dl_vers = VERSION;

	switch (op) {

	case DLDEP:
		packet->dl_hdr.dl_len = htons(datalen);
		packet->dl_hdr.dl_addr = htonl(addr);
		if (datalen > 0)
		  if ( read(AOUTFD, packet->dl_pt.dl_data, datalen)
		      < datalen ) {
			fprintf(stderr, "read error\n");
			callexit(1);
		  }
		break;

	case DLSRT:
	case DLID:
		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 DLDEP:
		pktsize = sizeof(struct dl_pack);
		break;

	case DLSRT:
		pktsize = sizeof(struct dl_header)
					+ sizeof(struct dl_srtval);
		break;

	default:
		pktsize = sizeof(struct dl_header);
		break;
	}
	if (A.verbose) {
		fprintf(stderr,"packet:  ");
		switch(dlpacket->dl_hdr.dl_op) {
		case DLDEP:
			addr =  ntohl(dlpacket->dl_hdr.dl_addr);
			len  =  ntohs(dlpacket->dl_hdr.dl_len);
			fprintf(stderr, "deposit, 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);
	}
}