/*
 * makehex.c  -  Generate hex boot image file
 *
 * Copyright (C) 1998-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: makehex.c,v 1.11 2007/01/06 18:31:14 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "makerom.h"



/* Size of one hex record */
#define IHEX_RECSIZE	16
#define MHEX_RECSIZE	16
#define THEX_RECSIZE	16



/*
 * Read one byte from temporary rom image file
 */
static inline int readbyte __F((romimage), int romimage)
{
  static __u8 buf[BLKSIZE];
  static size_t buflen = 0;
  static size_t bufofs = 0;

  if (bufofs >= buflen) {
	if ((buflen = nbread(buf, BLKSIZE, romimage)) == 0)
		return(EOF);
	bufofs = 0;
  }
  return(buf[bufofs++]);
}



/*
 * Return a nibble in ASCII hex
 */
static inline char hex __F((c), int c)
{
  if((c &= 0x000f) < 10)
	c += '0';
  else
	c += 'A' - 10;
  return(c);
}



/*
 * Convert bootrom image to Intel hex format
 */
static void makeihex __F((romimage, outfile), int romimage AND FILE *outfile)
{
  char *line, buffer[128];
  int address = 0;
  int c = 1;
  int sum, i;

  /* Process the boot rom image */
  fprintf(outfile, "# netboot bootrom image (Intel hex)\n");
  fprintf(outfile, "# created with %s, %s\n", progname, VERSION);
  fprintf(outfile, ":020000020000FC\n");
  while (c != EOF) {
	sum = 0;
	line = buffer;
	for (i = 0; i < IHEX_RECSIZE && (c = readbyte(romimage)) != EOF; i++) {
		*line++ = hex(c >> 4);
		*line++ = hex(c);
		sum += c;			/* checksum each character */
	}
	*line = '\0';
	if (i) {
		sum += (address >> 8) & 0xff;	/* checksum high address byte */
		sum += address & 0xff;		/* checksum low address byte */
		sum += i;			/* checksum record byte count */
		fprintf(outfile, ":%02X%04X00%s%02X\n", i,
				(address & 0xffff), buffer, ((0 - sum) & 0xff));
	}
	address += i;
  }
  fprintf(outfile, ":00000001FF\n");		/* end record */
}



/*
 * Convert bootrom image to Motorola hex format
 */
static void makemhex __F((romimage, outfile), int romimage AND FILE *outfile)
{
  char *line, buffer[128];
  int address = 0;
  int c = 1;
  int sum, i;

  /* Process the boot rom image */
  fprintf(outfile, "# netboot bootrom image (Motorola hex)\n");
  fprintf(outfile, "# created with %s, %s\n", progname, VERSION);
  while (c != EOF) {
	sum = 0;
	line = buffer;
	for (i = 0; i < MHEX_RECSIZE && (c = readbyte(romimage)) != EOF; i++) {
		*line++ = hex(c >> 4);
		*line++ = hex(c);
		sum += c;			/* checksum each character */
	}
	*line = '\0';
	if (i) {
		sum += (address >> 8) & 0xff;	/* checksum high address byte */
		sum += address & 0xff;		/* checksum low address byte */
		sum += i + 3;			/* checksum record byte count */
		fprintf(outfile, "S1%02X%04X%s%02X\n", i + 3,
				(address & 0xffff), buffer, ((0 - sum) & 0xff));
	}
	address += i;
  }
  fprintf(outfile, "S9030000FD\n");		/* end record */
}



/*
 * Convert bootrom image to Tektronix hex format
 */
static void makethex __F((romimage, outfile), int romimage AND FILE *outfile)
{
  char *line, buffer[128];
  int address = 0;
  int c = 1;
  int psum, dsum, i;

  /* Process the boot rom image */
  fprintf(outfile, "# netboot bootrom image (Tektronix hex)\n");
  fprintf(outfile, "# created with %s, %s\n", progname, VERSION);
  while (c != EOF) {
	psum = dsum = 0;
	line = buffer;
	for (i = 0; i < THEX_RECSIZE && (c = readbyte(romimage)) != EOF; i++) {
		*line++ = hex(c >> 4);
		*line++ = hex(c);
		dsum += (c & 0x0f) + ((c >> 4) & 0x0f);
	}
	*line = '\0';
	if (i) {
		psum += (i & 0x0f) + ((i >> 4) & 0x0f);
		psum += (address & 0x0f) + ((address >> 4) & 0x0f);
		psum += ((address >> 8) & 0x0f) + ((address >> 12) & 0x0f);
		fprintf(outfile, "/%04X%02X%02X%s%02X\n", (address & 0xffff), i,
				(psum & 0xff), buffer, (dsum & 0xff));
	}
	address += i;
  }
  fprintf(outfile, "/00000000\n");		/* end record */
}



/*
 * Make hex output file
 */
void makehex __F((fname, tempfile, outtype),
				char *fname AND
				int tempfile AND
				int outtype)
{
  FILE *outfile;

  /* Open the output file */
  if ((outfile = fopen(fname, "w")) == NULL) {
	prnerr("unable to create %s", fname);
	nbexit(EXIT_CREATE);
  }

  /* Generate the appropriate hex output */
  assert(outtype == OUT_IHEX || outtype == OUT_MHEX || outtype == OUT_THEX);
  switch (outtype) {
	case OUT_IHEX:
		makeihex(tempfile, outfile);
		break;
	case OUT_MHEX:
		makemhex(tempfile, outfile);
		break;
	case OUT_THEX:
		makethex(tempfile, outfile);
		break;
  }

  /* Close output file */
  fclose(outfile);
}

