/*
 * dl.c
 *
 * Convert bin.out file to Motorola S-record text file
 * Copyright (c) 1986  Eric D. Black
 *
 * Usage:
 *		dl [-o oooooo] [-e eeeeee]  binfile [srecfile]
 * where
 *		oooooo is an offset added to to the address of every S-record
 *             (no relocation of addresses in code is done)
 *		eeeeee is the entry point (overrides any found in binfile)
 *		binfile is the binary file in bin.out format
 *		srecfile is the text file to receive S-records, stdout by default
 * addresses oooooo and eeeeee may be in hexadecimal (leading "0x"),
 * octal (leading "0"), otherwise decimal
 */

/*
 * Permission is given to distribute these files and the code they
 * generate, and to modify and/or extend them, providing that:
 *     1) All copyright notices remain intact (you may copyright your
 *        specific changes)
 *     2) All source code accompanies any distribution, including any
 *        changes and/or enhancements you may have made
 *     3) Any changes you make are clearly indicated
 *     4) No direct profit results from that distribution (i.e. you
 *        may not sell this program or anything derived from it, but
 *        you may include it at no charge on a system you sell for profit),
 *        and you may give it away
 *
 */

#include <stdio.h>
#include "bin.out.h"

extern char getopt();
extern char *optarg;
extern int optind;

extern int Enable_Abort;

FILE *infile;
FILE *outfile;

int eflag = 0;
int oflag = 0;
int entrypt = 0;
int offset = 0;

main(argc,argv)
int argc;
char *argv[];
{
char c;

	/*
	 * Process command line using getopt(3)
	 */
    if(argc<2) {
        fprintf(stderr,"Usage: dl [-e xxxx] infile [outfile]\n");
        exit(1);
    }
	while ((c = getopt(argc, argv, "e:o:")) != EOF) {
		switch(c) {
			case 'e':
					entrypt = getnum(optarg);
					eflag++;
					break;
			case 'o':
					offset = getnum(optarg);
					oflag++;
					break;
			default:
					exit(1);	/* error msg is printed by getopt */
		}
	}
	argv += optind;
	argc -= optind;

	/*
	 * Get input, output file names
	 */
    if((infile=fopen(argv[0],"r"))==NULL) {   /* open infile */
       fprintf(stderr,"dl: Can't open %s\n",argv[0]);
       exit(1);
    }
	if (argc>1) {
        if ((outfile = fopen(argv[1],"w")) == NULL) {
                fprintf(stderr,"dl: Can't open %s\n",argv[1]);
                exit(1);
        }
    } else
		outfile = stdout;

	Enable_Abort = 1;

	dofile();

	fclose(infile);
	fclose(outfile);
}

/*
 * Translate input bin.out file to ASCII S-records in output file
 */

#define RECSIZE 32              /* Size of Motorola S-type records */

dofile()
{
struct bin binhdr;
int Saddr;

	/*
	 * get header info
	 */
    if(fread(&binhdr, sizeof(struct bin),1,infile) != 1) {
        fprintf(stderr,"dl: error reading input file\n");
        exit(1);
    }
    if(binhdr.b_magic != BMAGIC) {              /* check magic number */
         fprintf(stderr,"dl: input not proper b.out file\n"); 
         exit(1);
    }

    /*
     * Entry point is:
     *   1) as specified on command line, or
     *   2) as found in input bin.out header (plus offset), or
     *   3) start of text segment (plus offset)
     */
    if (!eflag) {
        entrypt = binhdr.b_entry ? binhdr.b_entry : binhdr.b_text;
		entrypt += offset;
	}

    Saddr = offset;

    /*
     * Output records for TEXT portion
     */
    outbin(infile,Saddr,binhdr.b_text);
    Saddr += binhdr.b_text;    /* adjust output address */

    /*
     * Output records for DATA segment
     */
    outbin(infile,Saddr,binhdr.b_data);
    Saddr += binhdr.b_data;    /* adjust output address */

    /*
     * Output entry point: as in file header,
     * else default to start address
     */
    print_s_rec(7,0,0,entrypt);
}


int checksum;

/*
 * print out one S record of given type
 */
print_s_rec(type,buffer,Dcount,addr)
char *buffer;
int Dcount, addr;
{
         fprintf(outfile,"S%d",type);
	     checksum = 0;
         checkout((char) Dcount+5);
         checkout((char) (addr>>24));
         checkout((char) (addr>>16));
         checkout((char) (addr>>8));
         checkout((char) addr);
         while (Dcount--)
		 	checkout(*buffer++);
         puthex(~(checksum & 0XFF)); 
         fprintf(outfile,"\n");
}

/*
 * output byte as two hex chars, keeping running checksum
 */

checkout(c)
char c;
{
	checksum += c;
	puthex(c);
}


#define hex(c) c>9 ? c+0X37 : c+0X30  /* macro for hex convert */

/*
 * print byte as two hex chars
 */
puthex(b)
char b;
{
char c1,c2;

    c1=(b>>4) & 0XF; c2=b & 0XF;
    c1=hex(c1); c2=hex(c2);
    fprintf(outfile,"%c%c",c1,c2);
}

/*
 * put out records from a buffer, using addr as starting S-record
 * address, for size bytes
 */
outbuf(buffer,addr,size)
char *buffer;
int addr, size;
{
int count,                      /* counts total number of bytes processed*/
    Dcount;                     /* number of bytes in current record */

    for(count=0; count<size; count += RECSIZE) {
        Dcount= (size-count<RECSIZE) ? size-count : RECSIZE;
        print_s_rec(3,buffer,Dcount,addr);
        buffer += Dcount;
        addr += Dcount;
    }
}

/*
 * put out records from a file, reading from current filepos for size bytes,
 * using addr as starting S-record address, for size bytes
 */
outbin(file,addr,size)
FILE *file;
int addr, size;
{
int count,                      /* counts total number of bytes processed*/
    Dcount;                     /* number of bytes in current record */
char buffer[RECSIZE];           /* buffer for data */

    for(count=0; count<size; count += RECSIZE) {
        Dcount= (size-count<RECSIZE) ? size-count : RECSIZE;
        if(fread(buffer,Dcount,1,file) != 1) {
            fprintf(stderr,"Input read error\n");
			exit(1);
		}
        print_s_rec(3,buffer,Dcount,addr);
        addr += Dcount;
    }
}
