/*----------------------------------------------------------------------*/
/*									*/
/*	upload        +++ copy 8086 memory to ./core86 +++		*/
/*									*/
/* This program takes 8086 memory and places it into a file named	*/
/* core86 on the host system						*/
/*									*/
/* Configuration -							*/
/*	VAX tty line connected to 8086 ODT (console) port		*/
/*									*/
/* Usage:								*/
/*	upload [-v] [-s] [-o filename] [low-addr [high-addr]]		*/
/*									*/
/* Options:								*/
/*      -v verbose							*/
/*      -s silent							*/
/*	-o corefilename (default filename core86)			*/
/*									*/
/* Authors:								*/
/*	R. Brown & C. Kent						*/
/*	Purdue University/CS Deptartment				*/
/*	September, 1981							*/
/*	mods by Comer, 11/81-11/83					*/
/*									*/
/* Modified for the 8086 (April 1984) by:				*/
/*	F. J. Newbery							*/
/*	K. L. Rives							*/
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <sgtty.h>
#include <signal.h>
#include <setjmp.h>
#include <a.out.h>
#include "/usr/ftp/pub/Xinu.8086/include/core86.h"
#include "/usr/ftp/pub/Xinu.8086/include/getdev.h"
#include "/usr/ftp/pub/Xinu.8086/include/baud.h"
#include "loader.h"

typedef	unsigned short	wordtype;

#ifndef	TRUE
#define	TRUE		1
#define	FALSE		0
#endif

#define	SOH		'/'
#define	ESC		'\033'
#define	LOADER		"/usr/ftp/pub/Xinu.8086/lib/ul"
#define	MAXPACKSIZE	526
#define	STACKLEN	20
#define	SPEED		B9600
#define	MAXADDR		0xf3ff		/* needs to be as high */
					/* as possible (upload */
					/* stops retrieving    */
					/* code at this point! */
#define LDEND		MAXADDR-1	/* area saved ...      */
#define LDSTART		LDEND-0x7f	/* for loader          */
#define	STACK		10
#define	PSTART		0x1000
#define CORE		"core86"

struct	packet	{
	wordtype	len;
	wordtype  	base;
	wordtype	buf[MAXPACKSIZE];
	wordtype	sum;
};

struct	core86	Corehead;
struct	sgttyb	VAXttyb;
extern	int	errno;
int	machnum, useclass, devfname, baudrate, timeout, tandem, unlock;
char	lockfile[MAXNAMELEN], devname[MAXNAMELEN], class[MAXNAMELEN];
char	*index();
int	Corefd;
int 	fd_86;
int	ultext;				/* size of text section */
int	uldata;				/* size of data section */
int 	cphase0;			/* size of phase 0 */
int	cphase1;			/* size of phase 1 */
wordtype 	*Memory;

/*----------------------------------------------------------------------
 *  upload  -- upload an 8086 memory image
 *----------------------------------------------------------------------
 */
main(argc,argv)
int	argc;
char	*argv[];
{
	int alarmhandler(), inthandler();
	int ldrfd, bytes;
	int base,length,i;
	struct exec bhdr;
	char buf[32];
	char *malloc();

	/*
	 * set up terminal modes...prepare to reset on interrupt
	 */

	procargs(argc, argv);
	if ( (machnum = getdev(useclass,class,machnum,&fd_86,lockfile)) < 0)
		exit(1);
	Openfiles[fd_86] = malloc(MAXNAMELEN);
	if (machnum == 0) {
		printf("Using %s\n", class);
		strcpy(Openfiles[fd_86], class);
	}
	else {
		printf("Using %s#%d\n", class, machnum);
		sprintf(Openfiles[fd_86], DEVNAME, DEVDIR, class, machnum);
	}
	if ((Corefd = creat(A.corefile,0666)) < 0) {
		printf("%s: can't create\n", A.corefile);
		callexit(1);
	}
	ldrfd =	openfile(A.loader,0);
	gtty(fd_86,&VAXttyb);

	/*
	 * trap	software signals so that lines can be restored to their
	 * original state and the core file can	be written
	 */
	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);

	linemode(fd_86,SPEED,RAW); /*|TANDEM*/

	/*
	 * allocate memory array - this	written	to the core file when the
	 * program terminates
	 */
	if (A.verbose)
		printf("Alocating %d (0%o) bytes for image\n",MAXADDR+1,
			MAXADDR+1);
	Memory = (wordtype *)malloc((MAXADDR+1));
	for ( i=0 ; i<(MAXADDR-1)/2 ; i++ )
		Memory[i] = 0;
	for ( i=0; i<REGISTERS; i++)
	    	Corehead.c_regs[i] = 0;
	Corehead.c_magic = COREMAGIC;

        message("Getting ODT...");
	Corehead.c_reg_valid = getodt(fd_86);

	/*
	 * Get the contents of the registers
	 */
	if (A.verbose)
		message("Getting registers...");
	getregs(fd_86,Corehead.c_regs);

	getfl(fd_86, &Corehead.c_fl);
	gethdr(ldrfd, &bhdr);
#ifdef undef
	/* Punted 'cause it's completely AFU */
	/* The getmem is CORRECTLY done in odtload */
	/* R. Droms and T. Narten, 12/17/84  */
	getmem(LDSTART, LDEND);

	if (A.verbose)
		printmem(LDSTART, LDEND);
#endif

	bytes =	bhdr.a_text+bhdr.a_data+STACKLEN;
	if ( !A.noload ) {
		message("Deposit boot loader...");
		odtload(ldrfd, &bhdr, 0, Memory);
	} else {
		message("Not loading boot loader...");
		read(ldrfd, Memory, bytes*sizeof(wordtype) );
	}

	/*
	 * Set up arguments to upl and start it	running...
	 */
	if (!A.silent)
		printf("Starting load of %x-%x...\n",A.lowaddr,A.highaddr);
	Corehead.c_size = A.highaddr + 1;	/* bytes */
	base = (A.lowaddr < bytes ? bytes : A.lowaddr);
	/* length is measured in words */
	length = (A.highaddr-base + 1)/2;
	if (A.verbose)
		message("Setting argument registers");
	setreg(fd_86,"AX",A.highaddr - A.lowaddr);	/* # bytes */
	setreg(fd_86,"CX",A.lowaddr);			/* base addr */
	if (A.verbose)
		message("Starting 8086 execution");
	sendodt(fd_86,"G\r",TRUE);
	fastread(fd_86);

	message("Writing core file...");
/*
 put other stuff in Corehead
*/
	write(Corefd,&Corehead,sizeof Corehead);
	write(Corefd,Memory,A.highaddr+1);
	touch(lockfile);
	message("Done");

}
/*
 *===================================================
 * procargs - process argument in global structure
 *===================================================
 *
 * This	procedure contains the logic for converting the	UNIX argument
 * list	into global variables
 */
procargs(argc, argv)
int argc;
char *argv[];
{
	int arg, unswitched, more;
	int  afd, i;
	struct exec ahdr;
	char *swptr;
	/*
	 * assign default values
	 */
	A.lowaddr = 0;
	A.highaddr = MAXADDR;
	A.corefile = CORE;
	A.odtline = NULL;
	A.loader = LOADER;
	A.verbose = FALSE;
	A.silent = FALSE;
	A.noload = FALSE;
	unswitched = 0;
	machnum = ANYDEV;
	strcpy(class, "8086");
	for ( arg=1 ; arg<argc ; arg++ ) {
		if ( argv[arg][0] == '-' ) {
			more = 1;
			swptr =	&argv[arg][1];
			while (	more &&	*swptr!='\0' ) {
				switch ( *swptr	) {
				case 'c':
					strcpy(class, ++argv[0]);
					useclass = TRUE;
					break;
				case 'l':
					if (sscanf(++argv[0], "%d", &machnum) != 1) {
						fprintf(stderr, "Illegal device number: %s\n",
							argv[0]);
						exit(1);
					}
					break;
				case 't':
					strcpy(class, ++argv[0]);
					machnum = TTYNAME;
					break;
				case 'B':
					if (*++argv[0] == '\0') {
						baudrate = NOBAUD;
						break;
					}
					for (i = 0; i < NBAUD; ++i)
						if (strcmp(baudlist[i].name, argv[0]) == 0)
							break;
					if (i < NBAUD)
						baudrate = baudlist[i].rate;
					else {
						fprintf(stderr, "Unknown baud rate: %s\n",
							argv[0]);
						exit(1);
					}
					break;
				case 'T':
					if (*++argv[0] == '\0')
						timeout = 0;
					else if (sscanf(argv[0], "%d", &timeout) != 1 ||
							timeout < 0) {
						fprintf(stderr,"Illegal timeout period: %s\n",
							argv[0]);
						exit(1);
					}
					break;
				case 'f':
					tandem = FALSE;
					break;
				case 'u':
					unlock = TRUE;
					break;
				case 'o':
					if ( arg+1 >= argc )
						usagexit(argv[0]);
					A.corefile = argv[arg++];
					more = 0;
					break;
				case 'v':
					A.verbose = TRUE;
					break;
				case 's':
					A.silent = TRUE;
					break;
				case 'n':
					A.noload = TRUE;
					break;
				case 'a':
					afd = openfile(argv[arg++],0);
					if ( arg+1 >= argc )
						usagexit(argv[0]);
					gethdr(afd,&ahdr);
					A.lowaddr = ahdr.a_text;
					A.highaddr = ahdr.a_text+ahdr.a_data+ahdr.a_bss;
					more = FALSE;
					break;

				default:
					usagexit(argv[0]);
				}
				swptr++;
			}
		} else { /* there's no dash in front */
			switch ( unswitched++ )	{
			case 0:
				sscanf(argv[arg], "%x", &A.highaddr);
				printf("highaddr=%04x\n",A.highaddr);
				break;
			case 1:
				A.lowaddr = A.highaddr;
				sscanf(argv[arg], "%x", &A.highaddr);
				printf("lowaddr =%04x\n",A.lowaddr);
				printf("highaddr=%04x\n",A.highaddr);
				break;
			default:
				usagexit(argv[0]);
			}
		}
	}
}
/*
 *========================================
 * usagexit - give usage message and exit
 *========================================
 */
usagexit(pgm)
char *pgm;
{
	fprintf(stderr,"usage: %s [-options] [[lowaddr] highaddr]\n",pgm);
	exit(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 ((fd=open(name,mode)) < 0 ) {
		perror(name);
		exit(1);
	}
	canceltimer();
	Openfiles[fd] =	name;
	return(fd);
}
/*
 *=========================================
 * alarmhandler	- return from alarm calls
 *=========================================
 */
alarmhandler()
{
	signal(SIGALRM,	alarmhandler);
	fprintf(stdout, "%s\n", msgval);
	exit(1);
}

/*
 *=====================================
 * 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(fd_86,&VAXttyb);
	exit(ret);
}

/*
 *===========================================
 * odtload - load bootstrap loader via ODT
 *===========================================
 * input state: after ODT prompt
 * output state: same
 */
odtload(fd, hdr, loc,outaddr)
int fd;
int loc;
struct exec *hdr;
wordtype *outaddr;
{
	int length, stklen, i, cbytes, addr, sp;
        char byte, old;
	char buf[32], *stack;
	char c;
	FILE *infile;

	if ((infile = fdopen(dup(fd),"r")) == NULL) {
	    	printf("error, can't open file %s\n", Openfiles[fd]); 
		exit(0);
	}
	
	fseek(infile, sizeof (struct exec), 0);
	stack = (char *)malloc(hdr->a_text + hdr->a_data);
	stklen = 0;
	byte = 0;
	do {			/* get phase0 until word ffff is found */
		old = byte;
		byte  = (fgetc(infile)&0xff);
		stack[stklen++] = byte;
	} while (((old&0xff) != 0xff)  ||  ((byte&0xff) != 0xff));

	cphase0 = stklen-1;
	stklen = stklen-3;	/* discard the ffff word */

	getmem(fd_86, MAXADDR-cphase0+2, MAXADDR);
	if(A.verbose)
		printmem(MAXADDR-cphase0+2, MAXADDR);

	/*
	 * load first part of the boot loader (phase0) at high memory 
         */
	if (A.verbose)
		printf("\nSending down phase 0 to high memory\n");
	addr = MAXADDR;
	for (i=stklen; i>=0; i--,addr--) {
		sprintf(buf,"S %4x = %2x\r", addr, stack[i]&0xff);
		sendodt(fd_86,buf,TRUE);
		readuntil(fd_86, ".", buf, 5);
		if ( !A.silent && !A.verbose ) {
			displayval(addr);
			printf("%02x \r", stack[i]&0xff);
			fflush(stdout);
		}
	} 
	if ( !A.silent && ! A.verbose ) {
                printf("\n");
		fflush(stdout);
	}
	cphase1 = hdr->a_text+hdr->a_data-cphase0-1;

	/*
	 * send phase1 in reverse order
	 */

	/* get the text section */
        for (stklen=0; stklen < cphase1 - hdr->a_data + 1; stklen++) {
		stack[stklen] = (fgetc(infile)&0xff);
	}

	/* get the data section */
	for (i=1; i < hdr->a_data; i++) {
		stack[stklen++] = (fgetc(infile)&0xff);
	}

	setreg(fd_86,"AX",addr);
	sp = addr;
	setreg(fd_86,"IP",addr+1);
	setreg(fd_86,"FL", 0);		/* set flag register to zero */
					/* to ensure interrupts are  */
					/* off                       */
	sendodt(fd_86,"G\r",FALSE);
	readuntil(fd_86, "\n", buf, 5);	/* skip over echoed G\r\n    */

	/* read in the extra stack space between phases 0 and 1 */
	for (i=0; i<STACK; i++){
		read(fd_86,&c,1);
		*(((char *)outaddr) + addr) = c;
		if ( !A.silent && ! A.verbose ) {
			printf("%04x %02x\r", addr, c & 0xff);
			fflush(stdout);
		}
		addr--;
	}

	readuntil(fd_86,".",buf,5);
	setreg(fd_86,"CX",cphase1);
	setreg(fd_86,"SP",sp);
	sendodt(fd_86,"G\r",FALSE);
	readuntil(fd_86, "\n", buf, 5);	/* skip over echoed G\r\n    */

	/* send down phase1 in reverse order.  Data section first */
	for ( i=stklen-1 ; i>=0; i-- ){
		c = stack[i];
		write(fd_86,&c,1);
		read(fd_86,&c,1);
		*(((char *)outaddr)+addr) = c;
		if ( !A.silent && ! A.verbose ) {
			printf("%04x %02x\r", addr, c & 0xff);
			fflush(stdout);
		}
		addr--;
	}

	sendodt(fd_86,"\r",TRUE);
	readuntil(fd_86,".", buf, 8);
	fclose(infile);
}


/*
 *===============================================
 * getfl - get	the current contents of	the PSW
 *===============================================
 */
getfl(fd,fl)
int fd;
short *fl;
{
	char buf[60];
	char	*reply;
	long val;

	sendodt(fd,"XFL\r",TRUE);
	readuntil(fd,"-", buf, 5);
	cvtregval(buf,&fl);
	if (A.verbose)
		printf("the value of flags register is %04x\n",fl);
	sendodt(fd,"\r",TRUE);
	readuntil(fd,".",buf,5);
}
/*
 *==========================================
 * getregs - get contents of the registers
 *==========================================
 */
getregs(fd,regs)
int fd;
short *regs;
{
	int r;
	char buf[32];
	long val;
	sprintf(buf, "\r");		/* Make sure ODT's happy */
	sendodt(fd, buf, TRUE);
	readuntil(fd, ".", buf, 20);
	for ( r=0 ; r<NREGS	; r++ )	{
			sprintf(buf,"X%s\r",regname[r]);
			sendodt(fd,buf,TRUE);
			readuntil(fd, "-", buf, 7);
			cvtregval(buf,&val);
			regs[r] = val&0x00ffff;
			if (A.verbose)
				printf("the value of register %s is %04x\n",regname[r],regs[r]&0xffff);
			sendodt(fd,"\r",TRUE);
			readuntil(fd,".",buf,5);
	}
	if ( !A.silent && !A.verbose )
		printf("\r");
	sendodt(fd,"\r",TRUE);
	readuntil(fd, ".", buf, 5);
}

/*
 *===============================================
 * cvtregval - convert register value s into val
 *===============================================
 */
cvtregval(s,val)
char *s;
int *val;
{
   	int i,j,k;
	char *t;
	k = strlen(s);
	j = 0;
	t = (char *) malloc (4);
	for (i=k-5;i<=k-2;i++,j++)
		t[j] = s[i];
	sscanf(t,"%4x",val);
}
/*
 *===============================================
 * getmem - get memory from low to high address
 *===============================================
 */
getmem(fd,low,high)
int fd;
int low;
int high;
{
 	int addr;
	char *space;
	char buf[32];
	wordtype val;
	int low2,high2;
	low2 = ( low%2==0 ? low: low-1);
	high2 = ( high%2==0 ? high: high-1);
	for (addr = low2; addr <= high2; addr+=2) {
		sprintf(buf,"DW %4x\r",addr);
		sendodt(fd,buf,TRUE);
		readuntil(fd,".",buf,7);
		cvtmemval(buf,&val);
		Memory[addr/2] = val;
		sendodt(fd,"\r",TRUE);
		readuntil(fd,".",buf,5);
	}
}
/*
 *===============================================
 * cvtmemval - convert memory value s into val
 *===============================================
 */
cvtmemval(s,val)
char *s;
wordtype *val;
{
   	int i,j,k;
	char *t;
	t = (char *) malloc (4);
	k = strlen(s);
	for (i=0; ((s[i] != ' ') && (i < k)); i++);
	t[0]=s[i+1];
	t[1]=s[i+2];
	t[2]=s[i+3];
	t[3]=s[i+4];
	sscanf(t,"%4x",val);
/*printf("cvtmmval %s  = %04x\r", t, *val);*/
	
}

/*
 *==================================================
 * printmem - print memory from low to high address
 *==================================================
 */
printmem(low,high)
int low,high;
{
	int i;
	int j;
	int low2,high2;
	low2 = (low%2 == 0? low: low-1);
	high2 = (high%2 == 0? high: high-1);
	printf("Contents of Memory from %x to %x:\n",low2,high2);
	j=0;
	for (i=low2; i<=high2; i+=2,j++) {
		if (j%10 == 0) 
			printf("\n%04x: ",i);
		printf("%04x ",Memory[i/2]);
	}
}

/*
 *=================================================
 * message - conditionally display status message
 *=================================================
 */
message(str)
char *str;
{
	if ( !A.silent )
		puts(str);
}


/*
 *=======================================
 * linemode - set up linemode and speed
 *=======================================
 */
linemode(fd,speed,mode)
int fd,	speed, mode;
{
	struct sgttyb tcb;
	if ( gtty(fd,&tcb) < 0 ) {
		perror(Openfiles[fd]);
		callexit(1);
	}
	tcb.sg_ispeed =	tcb.sg_ospeed =	speed;
	tcb.sg_flags = mode;
	stty(fd,&tcb);
}


/*
 *=================================================================
 * gethdr - read the header info from a.out file into hdr structure
 *=================================================================
 */
gethdr(fd, hdr) 
    struct exec *hdr;
    int fd;
{
    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 != 0444) {		/* Should be a magic number */
	fprintf(stderr, "%s: bad magic number (%o)\n", Openfiles[fd],
		hdr->a_magic);
	callexit(1);
    }
}


/*
 *============================================
 * displayval - display a number on one line
 *============================================
 */
displayval(val)
int val;
{
	char buf[32];
	sprintf(buf,"%4x",val);
	strncat(buf,"        ",8-strlen(buf));
	printf("\r%s",buf);
	fflush(stdout);
}

/*
 *==============================================
 * displayreg - display a register on one line
 *==============================================
 */
displayreg(reg,value)
char *reg;
int value;
{
	char buf[32];
        sprintf(buf,"X%2s %05x   ",reg,value);
        strncat(buf,"        ",8-strlen(buf));
	printf("%s",buf);
	fflush(stdout);
}

/*
 *==================================================
 * fastread - receive packets from high	speed line
 *==================================================
 */
fastread(fd)
int fd;
{
	struct packet packet;
	FILE *in;
	wordtype	sum;
	int i;
	char c;

	in = fdopen(fd,"r");
	message("\nReading 8086 memory...");
	do {
		read(fd,&c,1);
		if (c == 'n') {
			if (A.verbose)
				printf("already have all I need in Memory\n");
			return;
		}
	} while (c != 'y');
	
	do {
		do {
			getpack(in,&packet);
			if ( packet.len	<= MAXPACKSIZE ) {
				sum = (packet.len&0x00ffff)+(packet.base&0x00ffff);
				for ( i=0 ; i<(packet.len/2) ; i++ ){
				    sum += (packet.buf[i] & 0x00ffff);
				}
				if ( A.verbose ) 
					printf("\nCHECKSUM: exp./actual sum=%x/%x (decimal)", (int)sum, (int)packet.sum);
				if ( sum == packet.sum ) 
					break;
			}
			if ( !A.silent ) 
				fprintf(stdout,"\nNAK\n");
			write(fd,"n",1);
		} while	( TRUE ) ;
		write(fd,"y",1);
		for ( i=0 ; i<(packet.len/2) ; i++ )
			Memory[i+(packet.base/sizeof(wordtype))]	= packet.buf[i];
		if ( !A.silent )
			displayval((int)(packet.base&0xffff));
	
	} while	( packet.len !=	0 );
	if ( !A.silent ) {
	    	printf("\rUploading of packets completed successfully\n");
		displayval(0);
		printf("\r");
	}
}
/*
 *=======================================
 * getpack - read next packet from line
 *=======================================
 */
getpack(fd,pptr)
FILE *fd;
struct packet *pptr;
{
	char c;
	Bool success;
	int i;
	success	= FALSE;
	do {
		while (	(c=getc(fd)) !=	SOH ) /* nil */ ;  /* wait for start of header character */
		pptr->len = getword(fd);
		pptr->base = getword(fd);
		if ( A.verbose ) 
			fprintf(stdout,"\n\n\nlen, base = %o(OCTAL) %x(HEX)\n",pptr->len, pptr->base);
		if ( pptr->len > MAXPACKSIZE ) {
			return;
		}
		if (A.verbose)
			printf("\nContents of packet (in HEX)\n");
		for ( i=0 ; i<(pptr->len/2); i++ )	{
			pptr->buf[i] = getword(fd);
			if (A.verbose)
				printf("%x  ",pptr->buf[i]);
		}
		pptr->sum = getword(fd);
		success	= TRUE;
	} while	( !success );
}
/*
 *====================================
 * getword - read next word from line
 *====================================
 */
getword(fd)
FILE *fd;
{
	wordtype	word;

	word = getbyte(fd);
	word = (word&0x00ffff) | ((getbyte(fd)<<8)&0x00ffff);
	return(word);
}
/*
 *=====================================================
 * getbyte - read next byte from line honoring ESC'ing
 *=====================================================
 */
getbyte(fd)
FILE *fd;
{
	char c;
	c = getc(fd);
	if (A.verbose) fprintf(stdout,"%02x ",c&0xff); 
	if ( c==ESC ) {
		c = getc(fd);
	if (A.verbose) fprintf(stdout,"(%02x) ",c&0xff); 
	}
	return(c&0xff);
}
