
/*
 *  ROMABLE.C
 *
 *  Romable exefile -o outfile [-o outfile2] [-pi] [-C addr -D addr]
 *
 *  (c)Copyright 1990, Matthew Dillon, All Rights Reserved
 */

#include <stdio.h>
#include <stdlib.h>

long	NumHunks;
long	FirstHunk;
long	LastHunk;
short	PIOpt;

long	CodeStart = -1;
long	DataStart = -1;

short	DDebug;

typedef struct {
    long    Type;
    long    Len;	/*  initialized data length only    */
    long    Pc;
    long    RomPc;
    char    *Data;
} Hunk;

Hunk	*Hunks;

long	fgetl(FILE *);
void	LoadHeaderInfo(FILE *);
void	ScanHunks(FILE *);
void	RelocHunks(FILE *);
void	DumpHunks(FILE *, FILE *);

main(ac, av)
char *av[];
{
    short i;
    char *inFile = NULL;
    char *outFile1= NULL;
    char *outFile2= NULL;
    FILE *fi;
    FILE *fo1;
    FILE *fo2 = NULL;

    if (ac == 1) {
	puts("Romable exe -o out1 [-o out2] -C addr -D addr -d[#]");
	exit(1);
    }

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];
	char *dummy;

	if (*ptr != '-') {
	    inFile = ptr;
	    continue;
	}
	ptr += 2;
	switch(ptr[-1]) {
	case 'o':
	    if (*ptr == 0)
		ptr = av[++i];

	    if (outFile1 == NULL) {
		outFile1 = ptr;
	    } else if (outFile2 == NULL) {
		outFile2 = ptr;
	    } else {
		puts("Only up to two image files may be specified");
		exit(1);
	    }
	    break;
	case 'p':   /*  -pi */
	    PIOpt = 1;	    /*	position independant	*/
	    break;
	case 'C':
	    if (*ptr == 0)
		ptr = av[++i];
	    CodeStart = strtol(ptr, &dummy, 0);
	    break;
	case 'D':
	    if (*ptr == 'C') {
		DataStart = -2;
		break;
	    }
	    if (*ptr == 0)
		ptr = av[++i];
	    DataStart = strtol(ptr, &dummy, 0);
	    break;
	case 'd':
	    if (*ptr == 0)
		DDebug = 1;
	    else
		DDebug = atoi(ptr);
	    break;
	default:
	    printf("Bad option: %s\n", av[i]);
	    exit(1);
	}
    }
    if (i > ac) {
	puts("expected argument");
	exit(1);
    }
    if (PIOpt) {
	DataStart = 0;	    /*	all relative accesses	*/
	CodeStart = 0;
    } else {
	if (DataStart == -1 || CodeStart == -1) {
	    puts("-D and/or -C options not specified!");
	    exit(1);
	}
    }
    if (inFile == NULL) {
	puts("expected input file");
	exit(1);
    }
    if (outFile1 == NULL) {
	puts("expected output file");
	exit(1);
    }
    fi = fopen(inFile, "r");
    if (fi == NULL) {
	puts("unable to open input file");
	exit(1);
    }
    if (fgetl(fi) != 0x3F3) {
	puts("Not an executable");
	exit(1);
    }

    /*
     *	skip name
     */
    {
	int n = fgetl(fi);
	while (n--)
	    fgetl(fi);
    }

    /*
     *	header info
     */

    NumHunks	= fgetl(fi);
    FirstHunk	= fgetl(fi);
    LastHunk	= fgetl(fi);

    Hunks = malloc(NumHunks * sizeof(Hunk));
    if (Hunks == NULL) {
	puts("malloc failed");
	exit(1);
    }
    LoadHeaderInfo(fi);
    {
	long pos = ftell(fi);
	if (DDebug)
	    puts("scan");

	ScanHunks(fi);

	fseek(fi, pos, 0);
	if (DDebug)
	    puts("reloc");

	RelocHunks(fi);
    }
    fclose(fi);

    fo1 = fopen(outFile1, "w");
    if (fo1 == NULL) {
	printf("Unable to open %s\n", outFile1);
	exit(1);
    }
    if (outFile2) {
	fo2 = fopen(outFile2, "w");
	if (fo2 == NULL) {
	    printf("Unable to open %s\n", outFile2);
	    fclose(fo1);
	    remove(outFile1);
	    exit(1);
	}
    }
    if (DDebug)
	puts("dump");
    DumpHunks(fo1, fo2);
    fclose(fo1);
    if (fo2)
	fclose(fo2);
    return(0);
}

void
LoadHeaderInfo(fi)
FILE *fi;
{
    int n;
    Hunk *h;

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	h->Len = fgetl(fi) * 4;
	h->Data= malloc(h->Len + 1);    /*  +1 so can malloc 0 wo/error    */
	if (h->Data == NULL) {
	    printf("malloc failed hunk %d (%d)\n", n, h->Len);
	    exit(1);
	}
    }
}

long	CodePc;
long	DataPc;

/*
 *  Determine PC start and TYPE
 */

void
ScanHunks(fi)
FILE *fi;
{
    int n;
    int i;
    Hunk *h;

    n = 0;
    h = Hunks;
    while (n < NumHunks) {
	long len;
	long t = fgetl(fi);

	if (DDebug)
	    printf("Header %08lx\n", t);

	switch(t) {
	case 0x3EB:	/*  BSS     */
	    puts("Romable does not understand BSS hunks, must use -r option");
	    h->Type = t;
	    setmem(h->Data, h->Len, 0);
	    fgetl(fi);  /*  skip length */
	    break;
	case 0x3E9:	/*  CODE    */
	case 0x3EA:	/*  DATA    */
	    h->Type = t;
	    len = fgetl(fi) * 4;
	    if (len > h->Len || len < 0) {
		printf("Hunk Error: Text len %d/%d\n", len, h->Len);
		exit(1);
	    }
	    h->Len = len;
	    if (fread(h->Data, 1, len, fi) != len) {
		puts("Unexpected EOF");
		exit(1);
	    }
	    break;
	case 0x3EC:	/*  HUNK_RELOC32    */
	    while (len = fgetl(fi) * 4) {
		if (DDebug)
		    printf("len = %d\n", len);
		fgetl(fi);      /*  skip hunk#  */
		fseek(fi, len, 1);
	    }
	    break;
	case 0x3F0:	/*  SYMBOLS */
	    printf("I don't understand symbols yet\n");
	    exit(1);

	case 0x3F2:	/*  ignore HUNK_END */
	    ++n;
	    ++h;
	    break;
	default:
	    printf("Hunk type %08lx unknown\n", h->Type);
	    exit(1);
	}
    }

    CodePc = CodeStart;

    /*
     *	CODE PC's
     */

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	if (h->Type == 0x3E9) {
	    h->Pc = CodePc;
	    h->RomPc = CodePc;
	    CodePc += h->Len;
	}
    }

    /*
     *	DATA PC's
     */

    if (DataStart < 0)          /*  actual data goes after code, no duplication */
	DataStart = CodePc;

    DataPc = DataStart;

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	if (h->Type == 0x3EA) {
	    h->Pc = DataPc;
	    h->RomPc = CodePc;
	    DataPc += h->Len;
	    CodePc += h->Len;
	}
    }

    /*
     *	BSS PC's
     */

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	if (h->Type == 0x3EB) {
	    h->Pc = DataPc;
	    DataPc += h->Len;
	}
    }
}

void
RelocHunks(fi)
FILE *fi;
{
    int n;
    int dhno;
    Hunk *h;
    Hunk *dh;

    n = 0;
    h = Hunks;

    while (n < NumHunks) {
	long len;
	long t;

	t = fgetl(fi);
	switch(t) {
	case 0x3EB:	/*  BSS     */
	    fgetl(fi);  /*  skip length */
	    break;
	case 0x3E9:	/*  CODE    */
	case 0x3EA:	/*  DATA    */
	    len = fgetl(fi) * 4;
	    fseek(fi, len, 1);
	    break;
	case 0x3EC:	/*  HUNK_RELOC32    */
	    if (PIOpt) {
		puts("32 bit relocations exist, cannot make position independant!");
		exit(1);
	    }
	    while (len = fgetl(fi)) {
		dhno = fgetl(fi);       /*  hunk to relocate to */
		if (dhno < FirstHunk || dhno >= NumHunks + FirstHunk) {
		    printf("hunk# in reloc32 oob: %d/%d\n", dhno, NumHunks);
		    exit(1);
		}
		dh = Hunks + dhno - FirstHunk;
		while (len) {           /*  offsets to relocate */
		    long off = fgetl(fi);

		    if (off < 0 || off > h->Len - 4) {
			printf("Offset out of bounds: %d/%d\n", off, h->Len);
			exit(1);
		    }
		    *(long *)(h->Data + off) += dh->Pc;
		    if (DDebug)
			printf("Reloc in %d to %d offset %d add %04x\n", n, dhno, off, dh->Pc);
		    --len;
		}
	    }
	    break;
	case 0x3F0:	/*  SYMBOLS */
	    printf("I don't understand symbols yet\n");
	    exit(1);
	case 0x3F2:	/*  ignore HUNK_END */
	    ++n;
	    ++h;
	    break;
	default:
	    printf("Hunk type %08lx unknown\n", h->Type);
	    exit(1);
	}
    }
}

/*
 *  Dump the stuff.  CODE is dumped first, then DATA is dumped after the
 *  code.
 */

void
DumpHunks(fo1, fo2)
FILE *fo1;
FILE *fo2;
{
    long endpc = CodeStart;
    short n;
    short nextIsEven = 1;
    Hunk *h;

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	if (h->Type == 0x3E9) {
	    if (DDebug)
		printf("$%04x bytes text @ %08lx\n", h->Len, h->Pc);
	    endpc = h->Pc + h->Len;
	    if (fo2) {
		nextIsEven = fwrite_2(nextIsEven, fo1, fo2, h->Data, h->Len);
	    } else {
		fwrite(h->Data, 1, h->Len, fo1);
	    }
	}
    }

    for (n = 0, h = Hunks; n < NumHunks; ++n, ++h) {
	if (h->Type == 0x3EA) {
	    if (DDebug)
		printf("$%04x bytes data @ %08lx reloc to %08lx\n", h->Len, endpc, h->Pc);
	    endpc = h->Pc + h->Len;
	    if (fo2) {
		nextIsEven = fwrite_2(nextIsEven, fo1, fo2, h->Data, h->Len);
	    } else {
		fwrite(h->Data, 1, h->Len, fo1);
	    }
	}
    }
}


long
fgetl(fi)
FILE *fi;
{
    long v;

    v = getc(fi) << 24;
    v |= getc(fi) << 16;
    v |= getc(fi) << 8;
    v |= getc(fi);

    if (feof(fi)) {
	puts("unexpected EOF");
	exit(1);
    }
    if (DDebug)
	printf("get %08x\n", v);
    return(v);
}

int
fwrite_2(nextIsEven, fo_even, fo_odd, ptr, len)
int nextIsEven;
FILE *fo_even;
FILE *fo_odd;
char *ptr;
long len;
{
    while (len--) {
	if (nextIsEven)
	    putc(*ptr, fo_even);
	else
	    putc(*ptr, fo_odd);
	nextIsEven = 1 - nextIsEven;
	++ptr;
    }
    return(nextIsEven);
}


