/*----------------------------------------------------------------------*/
/*									*/
/*	download	+++ load programs into 8086 +++	         	*/
/*									*/
/* This program takes 8086 a.out format files and downloads them	*/
/* into a 8086.								*/
/*									*/
/* Usage:								*/
/*	download [filename] [-lLSI#] [-b filename] [-v] [-s]		*/
/*									*/
/* Options:								*/
/*      default filename is a86.out					*/
/*      -v verbose option						*/
/*	-s silent option						*/
/*									*/
/* Authors:								*/
/*	R. Brown, Doug Comer						*/
/*	Purdue University/CS Deptartment				*/
/*	September, 1981 - Nov. 1983					*/
/*                                                                      */
/* Modified for the 8086 by:                                            */
/*      F. J. Newbery                                                   */
/*      K. L. Rives                                                     */
/*	Purdue University/CS Deptartment				*/
/*      January, 1984 - March, 1984                                     */
/*									*/
/* (c) Copyright, 1982, All rights reserved.				*/
/*									*/
/*									*/
/*----------------------------------------------------------------------*/

#include <stdio.h>
#include <sgtty.h>
#include <signal.h>
#include <errno.h>
#include <a.out.h>
#include <sys/param.h>
#include "{Xinu-directory}/include/getdev.h"
#include "{Xinu-directory}/include/baud.h"
#include "loader.h"

#define DEFFILE	        "a86.out"
#define LOADER        	"{Xinu-directory}/lib/dl"
#define PSTART           0x1000

#ifndef TRUE
#define TRUE	1
#define FALSE	0
#endif
typedef unsigned short wordtype;

#define SOH		'/'
#define ACK		'y'
#define NAK		'n'
#define ESC		'\033'
#define	SPEED   	B9600		/* speed of 8086 line for stty	*/
#define MAXADDR		0xefff		/* high address on the 8086 */
#define LOADSTART	0xe000 		/* address at which to put loader */
#define PACKSIZE	0200 		/* size of data packets */



int fd_86;
int bsize, asize;
long	segaddr;
long	offaddr;
int	machnum, useclass, devfname, baudrate, timeout, tandem, unlock;
char	lockfile[MAXNAMELEN], devname[MAXNAMELEN], class[MAXNAMELEN];

/*
 * MAIN PROGRAM...
 */
main ( argc, argv )
int argc;
char *argv[];
{
	int aoutfd, ldrfd;
	int alarmhandler(), inthandler();
	char buf[32];
	struct exec ahdr, bhdr;
	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 ( 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);
	/*
	 * open lines and files - set line mode
	 */
	aoutfd = openfile(A.filename,0);
	ldrfd = openfile(A.loader,0);
	linemode(fd_86,SPEED,RAW);
	/*
	 * download sequence -
	 *    get ODT's attention
	 * load bootstrap, enter
	 * load program
	 */

	gethdr(aoutfd, &ahdr);
	gethdr(ldrfd,&bhdr);
	segaddr = 0;			/* always 0 - 64Kb ram	 */
	offaddr = ahdr.a_entry;
	printf("Entry point is %0lx\n", offaddr);

	bsize = bhdr.a_text+bhdr.a_data;
	asize = ahdr.a_text+ahdr.a_data;

	if (!A.silent) printf("getting ODT...\n");
	getodt(fd_86);

	if (!A.silent) printf("loading boot program...\n");
	bootload(ldrfd,&bhdr,0);	/* load phase0 and phase1 */

	sleep(2);
	setreg(fd_86,"AX",ahdr.a_bss);
		/* setreg(fd_86,"CX",(A.autostart ? A.startdelay : -1));*/
	setreg(fd_86,"DX",ahdr.a_entry & (~1));	/* entry pt of users program */

        if (!A.silent) printf("\rstarting boot loader Phase 1...\n");
	sendodt(fd_86,"G\r",TRUE);	/* start phase1 */

	sleep(1);
        fastload(aoutfd, &ahdr);	/* download users file */

	sleep(2);
	/* set up registers for user program */
	setreg(fd_86,"SP",0x0420); 
	setreg(fd_86,"FL",0);
	setreg(fd_86,"CS", (short)segaddr);
	setreg(fd_86,"IP", (short)offaddr);
	touch(lockfile);
	callexit(0);
}
/*
 *====================================================================
 * procargs - parse and check command line, setup global A structure
 *====================================================================
 */
procargs(argc, argv)
int argc;
char *argv[];
{
	int arg, i;
	int unswitched;
	A.filename  = DEFFILE;
	machnum = ANYDEV;
	strcpy(class, "8086");
	A.loader    = LOADER;
	A.verbose = FALSE;
	A.silent = FALSE;
	A.autostart = FALSE;
	A.startdelay = 0;
	unswitched = 0;
	for ( arg=1 ; arg<argc ; arg++ ) {
		if ( argv[arg][0] == '-' ) {
			switch ( argv[arg][1] ) {
			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 'b':
				if ( ++arg >= argc )
					usagexit(argv[0]);
				A.loader = argv[arg];
				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:
				usagexit(argv[0]);
			}
		} else {              /* ( argv[arv][1] != '-' ) */ 
			switch ( unswitched++ ) {
			case 0:
				A.filename = argv[arg];
				break;
			default:
				usagexit(argv[0]);
			}
		}
	}
}


/*
 *==========================================
 * usagexit - print usage message and exit
 *==========================================
 */
usagexit(pgm)
char *pgm;
{
	fprintf(stderr,
	"usage: %s [filename] [-v] [-s] ", pgm);
	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 ((fd=open(name,mode)) < 0 ) {
		perror(name);
		callexit(1);
	}
	canceltimer();
	Openfiles[fd] = name;
	return(fd);
}
/*
 *=======================================
 * 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);
}

/*
 *=========================================
 * alarmhandler - return from alarm calls
 *=========================================
 */
alarmhandler()
{
	signal(SIGALRM, alarmhandler);
	fprintf(stdout, "%s\n", msgval);
	exit(1);
}

/*
 *=====================================
 * inthandler -- interrupt processing
 *=====================================
 */
inthandler()
{
	callexit(2);
}
/*
 *=======================================
 * callexit - restore ttymodes and exit
 *=======================================
 */
callexit(ret)
int ret;
{
	exit(ret);
}
/*
 *===========================================
 * bootload - load bootstrap loader via ODT
 *===========================================
 * input state: after ODT prompt
 * output state: same
 */
bootload(fd, hdr, loc)
int fd;
int loc;
struct exec *hdr;
{
	int length, stklen, i, cbytes, addr;
        char byte, old;
	char buf[32], *stack;
	FILE *infile;

	infile = fdopen(dup(fd), "r");
	fseek(infile, sizeof(struct exec), 0);
	length = hdr->a_text; 
	addr = LOADSTART;
	/*
	 * load first part of the boot loader (phase0) at location LOADSTART
         */
        byte = 0;
	do {		/* do until the word ffff is encountered */
                old = byte;
	        byte = fgetc(infile);
		sprintf(buf,"S %4x = %2x\r",addr+loc,byte&0xff);
		sendodt(fd_86,buf,TRUE);
		readuntil(fd_86,".",buf,5);
		if ( !A.silent && ! A.verbose ) {
			displayval(addr);
			printf("%02x \r",byte&0xff);
		}
		addr += 1;
	} while (((old&0xff) != 0xff)  ||  ((byte&0xff) != 0xff));
	if ( !A.silent && ! A.verbose )
 
               printf("\r");
	/*
	 * send the boot loader (phase1) in reverse order
	 */

	/* get the text section */
	stack = (char *)malloc(hdr->a_text + hdr->a_data);
	stklen = 0;
	cbytes = (length-(addr-LOADSTART)+2);	/* KLUDGE */
        for (i=0; i< cbytes; i++) {
		stack[stklen++] = (fgetc(infile)&0xff);
	}

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

	/* load registers and start phase0 */
	setreg(fd_86,"BX",MAXADDR);
	setreg(fd_86,"CX",stklen);
        setreg(fd_86,"IP",LOADSTART);
	setreg(fd_86,"FL",0);		/* set flags to zero to ensure */
					/* interrupts are turned off   */
        sendodt(fd_86,"G\r", FALSE);
        sleep(1);

	/* send phase1 in reverse order */
	for ( i=stklen-1 ; i>=0 ; i-- ) {
		write(fd_86, &stack[i], 1);
		if ( A.verbose ) 
			printf("  %02x",stack[i]&0xff);
	}
        if ( A.verbose ) 
                printf("\n");
}

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

/*
 *================================
 * fastload - load user program
 *================================
 */

FILE *line;
char Fastbuf[BUFSIZ];

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

	words = asize / sizeof (wordtype);
	line = fdopen(fd_86,"w");
	setbuf(line,Fastbuf);
	infile = fdopen(fd, "r");
	addr = PSTART;
	while ( words > 0 ) { 
		length = (words>PACKSIZE ? PACKSIZE : words);
		fread(buf,sizeof(wordtype),length,infile);
		do {
			pause(1);
			sendpack(addr,length,buf);
			cNAK = 0;
			do {	/* wait for response ACK or NAK */
			    cNAK++;
                            settimer(20, "fastload read timed out");
                            if ( read ( fd_86, &resp, 1) != 1 ) {
				    exit(1);
                            }
                            canceltimer();
			} while ((resp != ACK) && (resp != NAK) && (cNAK <= 20 /* KLUDGE */));
		} while ( resp != ACK ); /* resend if NAK was received */

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

	/* send "end-of-file" packet */
        sendpack ( addr, 0, buf );
	cNAK = 0;
	do {	/* wait for response ACK or NAK */
	    cNAK++;
            settimer(20, "fastload read timed out");
            if ( read ( fd_86, &resp, 1) != 1 ) {
		    exit(1);
          	    }
           canceltimer();
	} while ((resp != ACK) && (resp != NAK) && (cNAK <= 20 /* KLUDGE */));
}

/*
 *=========================================
 * sendpack - send a packet to the 8086
 *=========================================
 */

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

sendpack(addr, length, buf)
int addr, length;
wordtype *buf;
{
	int sum, i;
        if ( A.verbose ) { 
                printf("Sending packet addr=%6x len=%o\n",addr,length);
                for ( i=0 ; i<length ; i++ ) {
                        printf(" %04x",buf[i]&0xffff);
                        }
                printf("\n");
        } 
 	/* send out SOH, base address, and length */
	rawputc(SOH);
	sum = 0;
	putwsum(addr);
	putwsum(length);
	/* send out packet contents */
	for ( i=0 ; i<length ; i++ ) {
		putwsum(buf[i]);
                }
        if ( A.verbose )
                printf("sum is %d\n",(-sum)&0xffff);
        putwsum((-(wordtype)sum));  /* send out the negative of the checksum */
	fflush(line);
}
/*
 *=====================
 * putw - write a word 
 *=====================
 */
putw(word)
wordtype word;
{
	putch((char)(word&0xff));	/* write out low byte  */
	putch((char)((word>>8)&0xff));	/* write out high byte  */
}
/*
 *==========================================
 * putch - put a character honoring ESC'ing
 *==========================================
 */
putch(ch)
char ch;
{
	if ( ch == ESC || ch == SOH )
		rawputc(ESC);
	rawputc(ch);
}

/*
 *========================================
 * autostart - implement startup options
 *========================================
 */
autostart(fd,hdr)
int fd;
struct exec *hdr;
{
	int i;
	char buf[32];

	if ( !A.autostart ) {
		readuntil(fd, ".", buf, 2);
		setreg(fd, "IP", (i=hdr->a_entry & (~1)) );
		setreg(fd, "SP", hdr->a_text + hdr->a_data + hdr->a_bss + 6);
		if ( !A.silent )
			printf("\rStart at 0%o\n",i);
	}
}
/*
 *============================================
 * 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);
}
/*
 *=================================================================
 * gethdr - read the header info from a.out file into hdr structure
 *=================================================================
 */
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 == 0) {		/* FIX THIS CHECK!! */
		fprintf(stderr,"%s: wrong magic number\n",Openfiles[fd]);
		callexit(1);
	}
}

/*
 *=================================================================
 * pause - pause for a little while
 *=================================================================
 */
pause(t)
int t;
{
    int i;
    for (i=0; i<t*1000; i++);
}
