/************************************************************************/
/* Copyright 1990 MBARI							*/
/************************************************************************/

/************************************************************************/
/* Summary  : Program to split S-Record file into Odd/Even halfs	*/
/* Filename : romsplit.c						*/
/* Author   : Bob Herlien (rah)						*/
/* Project  : ROV 1.5							*/
/* Version  : 1.0							*/
/* Created  : 04/18/90							*/
/* Comments : This program understands only S1 and S2 type records!	*/
/************************************************************************/
/* Modification History:						*/
/* 18sep91 rah - changed defaults					*/
/* 18apr90 rah - created						*/
/************************************************************************/

#include <stdio.h>
#include <mbari/types.h>
#include <string.h>

#define DFLT_ROMS	2		/* Default # of roms to split into*/
#define MAX_ROMS	8		/* Max # roms to split into	*/
#define DFLT_SIZE	0x20000		/* Default size of roms		*/

typedef struct
{
    Nat16		stype;
    Nat16		reclen;
    Nat32		addr;
    Char		data[64];
    Char		chksum;
} SRecord;


typedef union
{
    Nat32		laddr;
    Char		caddr[4];
} A_un;


/*				*/
/*	Global Data		*/
/*				*/

Global SRecord		srec;			/* S-Record structure	*/

Global FILE		*infp, *outfp[MAX_ROMS]; /* Input, output file ptrs*/

Global Int		nroms = DFLT_ROMS;	/* Number of ROMS	*/

Global Nat32		romsize = DFLT_SIZE;	/* Size of ROMS		*/

Global Nat32		rombase = 0;		/* Current ROM base	*/


/************************************************************************/
/* Function    : read_srec						*/
/* Purpose     : Read one S Record					*/
/* Inputs      : Ptr to SRecord						*/
/* Outputs     : 0 if more, 2 = EOF, -1 = Failure			*/
/************************************************************************/
	Int
read_srec( srp )
     Reg SRecord	*srp;
{
    Reg Nat16		checksum;
    Reg Int16		i, datalen;
    A_un		addr_union;
    Int			j;

    if ( fscanf(infp, " S%1hd%2hx", &srp->stype, &srp->reclen) != 2 )
	return( 2 );

    datalen = 0;

    switch (srp->stype)
    {
      case 1:
	datalen = srp->reclen - 3;
      case 8:
	if ( fscanf(infp, "%4x", &srp->addr) != 1 )
	    return( -1 );
	break;

      case 2:
	datalen = srp->reclen - 4;
      case 9:
	if ( fscanf(infp, "%6x", &srp->addr) != 1 )
	    return( -1 );
	break;

      default:
	return( -1 );
    }

    checksum = srp->reclen;

    addr_union.laddr = srp->addr;
    for ( i = 0; i < 4; i++ )
	checksum += addr_union.caddr[i];

    if ( srp->stype < 8 )
	srp->addr -= rombase;

    if ( (datalen < 0) || (datalen > 64) )
	return( -1 );

    for ( i = 0; i < datalen; i++ )
    {
	if ( fscanf(infp, "%2x", &j) != 1 )
	    return( -1 );
	srp->data[i] = (j & 0xff);
	checksum += (j & 0xff);
    }

    if ( fscanf(infp, "%2x", &j) != 1 )
	return( -1 );

    srp->chksum = (j & 0xff);

    if ( ((checksum + srp->chksum) & 0xff) != 0xff )
    {
	printf("Bad checksum\n");
	return( -1 );
    }

    return( 0 );

} /* read_srec() */


/************************************************************************/
/* Function    : write_srec						*/
/* Purpose     : Write one S Record					*/
/* Inputs      : Ptr to SRecord						*/
/* Outputs     : 0 = OK, 1 = past romsize, -2 = Failure			*/
/************************************************************************/
	Int
write_srec( srp )
     Reg SRecord	*srp;
{
    Reg Nat16		checksum;
    Reg Int16		datalen, reclen;
    Reg Nat32		i, j, start;
    A_un		addr_union;

    if ( (srp->addr >= romsize) && (srp->stype < 8) )
    {
	srp->addr -= romsize;
	rombase += romsize;
	return( 1 );
    }

    for ( i = 0; i < nroms; i++ )
    {
	start = (i - srp->addr) % nroms;	/* First byte of data out */

	switch ( srp->stype )
	{
	  case 1:
	    datalen = (srp->reclen - 3 - start + nroms - 1) / nroms;
	    if ( fprintf( outfp[i],"S1%02x%04x", datalen + 3,
			  (srp->addr + start)/nroms) != 8 )
		return( -2 );
	    checksum = datalen + 3;
	    break;

	  case 2:
	    datalen = (srp->reclen - 4 - start + nroms - 1) / nroms;
	    if ( fprintf( outfp[i],"S2%02x%06x", datalen + 4,
			  (srp->addr + start)/nroms) != 10 )
		return( -2 );
	    checksum = datalen + 4;
	    break;

	  case 8:
	  case 9:
	    return( 0 );

	  default:
	    return( -2 );
	}

	addr_union.laddr = (srp->addr + start)/nroms;
	for ( j = 0; j < 4; j++ )
	    checksum += addr_union.caddr[j];

	for ( j = start; datalen--; j += nroms )
	{
	    if ( fprintf(outfp[i], "%02x", (Nat32)srp->data[j] & 0xff) != 2 )
		return( -1 );
	    checksum += (srp->data[j] & 0xff);
	}

	checksum = -1 - checksum;

	if ( fprintf(outfp[i], "%02x\n", checksum & 0xff) != 3 )
	    return( -2 );
    }

    return( 0 );

} /* write_srec() */


/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main routine						*/
/* Inputs      : argc, argv						*/
/* Outputs     : Integer, 0 for success, 1 for failure			*/
/************************************************************************/
	Int
main( argc, argv )
     Int	argc;
     char	*argv[];
{
    char		outname[80];
    Reg Int		result;		/* 0 = do more; 1 = EOF; -1 = fail*/
    Reg	Int		i, rom_number;

    if ( (argc < 2) || (*argv[1] == 'h') || (*argv[1] == '?') )
    {
	printf("usage: romsplit <filename> <#roms / split>");
	printf(" <size of roms in hex>\n");
	printf("  Output files are <filename>.n, n = 0, 1, ...\n\n");
	exit(1);
    }

    if ( argc > 2 )
	nroms = atoi(argv[2]);

    if ( nroms < 0 )
	nroms = DFLT_ROMS;

    if ( nroms > MAX_ROMS )
    {
	printf("romsplit: Cannot split greater than 8 ways\n");
	exit(1);
    }

    if ( argc > 3 )
	sscanf(argv[3], "%x", &romsize);

    infp =  fopen(argv[1], "r" );
    if ( infp == (FILE *)NULL )
    {
	printf("romsplit: Cannot open input file %s\n", argv[1]);
	exit(1);
    }

    for ( result = 0, rom_number = 0; (result == 0) || (result == 1);
	  rom_number += nroms)
    {
	for ( i = 0; i < nroms; i++ )
	{
	    sprintf( outname, "%s.%d\0", argv[1], rom_number + i );
	    outfp[i] = fopen( outname, "w" );
	    if ( outfp[i] == (FILE *)NULL )
	    {
		printf("romsplit: Cannot open output file%s\n", outname);
		exit(1);
	    }
	}

	if ( result == 1)			/* Write record from	*/
	    if ( (result = write_srec(&srec)) != 0 )
		break;				/* previous pass	*/

	while ( (result = read_srec(&srec)) == 0 )
	    if ( (result = write_srec(&srec)) != 0 )
		break;

	if ( result == -1 )
	    printf("romsplit: Invalid S-Record format on input\n");
	else if ( result == -2 )
	    printf("romsplit: Error on output\n");

	for ( i = 0; i < nroms; i++ )
	    if (fclose(outfp[i]) != 0)
	    {
		printf("romsplit: Failure on closing %s.%d\n", 
		        argv[1], rom_number + i);
		result = -1;
	    }

    }

    fclose(infp);

    exit( (result == 1) ? 0 : 1 );

} /* main() */
