
/*
 *  LIBREF.C
 *
 *  LIBREF [cmdfile]
 *  (default func.def)
 */

#include <stdio.h>
#include <fcntl.h>
#include <local/typedefs.h>

#define FLIST	struct _FLIST

FLIST {
    MNODE   Node;
    uword   RegMask;
    uword   IsAsm;
    char    FName[64];
};

extern char *MToS();

char LibGlob[64] = { "SomeUnknownLibBase" };
char MName[128] = { "ram:MakeLib.c" };
char LName[128] = { "ram:LinkTag.asm" };
char TName[128] = { "ram:LibTag.asm" };
char LibName[128] = { "ram:Lib.lib" };
MLIST FBase;

main(ac,av)
char *av[];
{
    FILE *fi;
    char *file = "func.def";
    char buf[128];

    puts("LIBREF V1.00 Sept 1988, (c)Copyright 1988 Matthew Dillon, All Rights Reserved");
    puts("  source/executable Freely distributable for non-profit only.  May be USED");
    puts("  in-house to generate commercial libraries.");

    NewList(&FBase);
    if (ac == 2)
	file = av[1];
    fi = fopen(file, "r");
    if (!fi) {
	printf("%s not found\n", file);
	puts("LIBREF [cmdfile]");
	exit(1);
    }
    while (fgets(buf, sizeof(buf), fi)) {
	buf[strlen(buf)-1] = 0;
	if (!buf[0] || buf[0] == ';')
	    continue;
	switch((buf[0]<<8)|buf[1]) {
	case '1=':
	    sscanf(buf+2, "%s", MName);
	    break;
	case '2=':
	    sscanf(buf+2, "%s", LName);
	    break;
	case '3=':
	    sscanf(buf+2, "%s", TName);
	    break;
	case '4=':
	    sscanf(buf+2, "%s", LibGlob);
	    break;
	case '5=':
	    sscanf(buf+2, "%s", LibName);
	    break;
	case '6=':
	case '7=':
	case '8=':
	case '9=':
	    break;
	default:
	    scanfile(buf, buf, sizeof(buf));
	    break;
	}
    }
    fclose(fi);
    {
	FILE *fi = fopen(MName, "w");
	if (!fi) {
	    printf("Unable to open %s for write\n", MName);
	    exit(-1);
	}
	GenerateMakeLib(fi);
	fclose(fi);
	fi = fopen(TName, "w");
	if (!fi) {
	    printf("Unable to open %s for write\n", MName);
	    exit(-1);
	}
	GenerateTags(fi);
	fclose(fi);
	GenerateLinkLib(LName, strlen(LName));
    }
}

/*
 * scan file for functions
 *
 *  *FUNC=NAME REGS	 (C, assembly tag entry, add extra _)
 *  ;FUNC=NAME REGS	 (assembly, direct entry)
 *		 D0-2/A0/A1/A2 ...
 *		 REGISTERS ALWAYS LOADED D0-D7,A0-A7	(A6,A7 cannot be used)
 *
 *  starting within the first 16 lines of the file.
 */

scanfile(file, buf, bufsize)
char *file;
char *buf;
long bufsize;
{
    FILE *fi;
    short i;

    fi = fopen(file, "r");
    if (!fi) {
	printf("Unable to open file %s\n", file);
	return(-1);
    }
    for (i = 0; i < 16; ++i) {
	short isasm;
	short j;
	if (fgets(buf, bufsize, fi) == NULL)
	    return(0);
	for (j = 0; buf[j] == ' ' || buf[j] == 9; ++j);
	isasm = (buf[j] == ';' || buf[j] == 'A');
	++j;
	if (strncmp(buf+j, "FUNC=", 5) == 0) {
	    i = 0;
	    AddFunction(buf+j+5, isasm);
	}
    }
    fclose(fi);
}

/*
 *  FuncName	Regs	(NULL SPECIAL)
 */

AddFunction(buf, isasm)
char *buf;
{
    char fname[64];
    char regs[64];
    uword regmask = 0;	   /*  A7-A0,D7-D0 */

    if (sscanf(buf, "%s %s", fname, regs) != 2) {
	printf("Argument Error: %s\n", buf);
	return(-1);
    }
    if (strcmp(regs, "-") == 0)
	regs[0] = 0;
    {
	register short i;
	register short s, e;
	for (i = 0; regs[i]; ) {
	    s = e = regs[i+1] - '0';
	    if (s < 0 || s > 7)
		goto fail;
	    switch(regs[i]) {
	    case 'A':
		s += 8;
		e += 8;
		i += 2;
		if (regs[i] == '-') {
		    if (regs[i+1] != 'A')
			goto fail;
		    e = regs[i+2] - '0';
		    if (e < 0 || e > 7)
			goto fail;
		    e += 8;
		    i += 3;
		}
		break;
	    case 'D':
		i += 2;
		if (regs[i] == '-') {
		    if (regs[i+1] != 'D')
			goto fail;
		    e = regs[i+2] - '0';
		    if (e < 0 || e > 7)
			goto fail;
		    i += 3;
		}
		break;
	    default:
		goto fail;
	    }
	    while (s <= e) {
		regmask |= 1 << s;
		++s;
	    }
	    if (regs[i]) {
		if (regs[i] != '/')
		    goto fail;
		++i;
	    }
	}
    }
    /*
    printf("Function: %-10s Regs: %04x  Asm: %d\n", fname, regmask, isasm);
    */
    {
	register FLIST *fl = malloc(sizeof(FLIST));
	if (fl) {
	    fl->RegMask = regmask;
	    fl->IsAsm	= isasm;
	    if (strcmp(fname, "NULL") == 0)
		fl->FName[0] = 0;
	    else
		strcpy(fl->FName, fname);
	    AddTail(&FBase, fl);
	}
    }
    return(0);
fail:
    printf("Bad Register Spec: %s\n", buf);
    return(-1);
}

GenerateMakeLib(fi)
FILE *fi;
{
    register FLIST *fl;
    short i;

    fprintf(fi, "\n/*\n * Machine Generated Library Vectors\n */\n\n");
    fprintf(fi, "#ifndef NULL\n#define NULL 0L\n#endif\n\n");
    fprintf(fi, "extern long _LibOpen(), _LibClose(), _LibExpunge();\n");
    for (fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (fl->FName[0]) {
	    if (fl->IsAsm) {    /*  If assembly, direct reference   */
		fprintf(fi, "extern long l%s();\n", fl->FName);
	    } else {		/*  If not, reference to tag	    */
		fprintf(fi, "extern long _l%s();\n", fl->FName);
	    }
	}
    }

    fprintf(fi, "\n\n/*\n * -30-6*X\n */\n\n");
    fprintf(fi, "long (*LibVectors[])() = {\n");
    fprintf(fi, "    _LibOpen, _LibClose, _LibExpunge, NULL,\n\n");
    for (i = 0, fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (fl->FName[0]) {
	    if (fl->IsAsm)
		fprintf(fi, "    l%-16s,    /*  %3ld  %-20s  */\n", fl->FName, -30-6*i, MToS(fl->RegMask));
	    else
		fprintf(fi, "    _l%-15s,    /*  %3ld  %-20s  */\n", fl->FName, -30-6*i, MToS(fl->RegMask));
	} else {
	    fprintf(fi, "    NULL,\n");
	}
	++i;
    }
    fprintf(fi, "    (long (*)())-1\n};\n\n");
}

GenerateTags(fi)
FILE *fi;
{
    register FLIST *fl;

    fprintf(fi, "\n");
    fprintf(fi, "\t; Machine Generated File\n");
    fprintf(fi, "\t; Tags for library routines which are in C\n\n");
    fprintf(fi, "\tFAR\tDATA\n\n");
    for (fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	uword mask;
	if (fl->IsAsm || !fl->FName[0])
	    continue;
	fprintf(fi, "\n");
	fprintf(fi, "\t\tpublic _l%s\n\t\tpublic __l%s\n\n", fl->FName, fl->FName);
	fprintf(fi, "__l%s:\n", fl->FName);

	/*
	 *  Save the set D2/D3/A6, but don't bother saving D2 or
	 *  D2 and D3 if passed as arguments.
	 */

	mask = 0x400C;
	MoveToStack(fi, mask);          /*  Save some args  */
	MoveToStack(fi, fl->RegMask);   /*  Push some args  */
	fprintf(fi, "\t\tbsr\t_l%s\n", fl->FName);
	PopStack(fi, fl->RegMask);
	MoveFromStack(fi, mask);
	fprintf(fi,"\t\trts\n");
    }
}


/*
 *  Generate the link library.	Create N output modules for
 *  each function reference by appending a number to the name
 */

GenerateLinkLib(dir, dirlen)
char *dir;
short dirlen;
{
    char tmp[128];
    short i, nobj, nj;
    FILE *fi;
    FILE *fi2;
    register FLIST *fl;
    register short off;

    strcpy(dir+dirlen, "Makefile");
    fi = fopen(dir, "w");
    if (!fi)
	goto fail;
    strcpy(dir+dirlen, "Ordin");
    fi2 = fopen(dir, "w");
    if (!fi2)
	goto fail;

    fputs("\nAFLAGS = -D\n\n", fi);

    nj = 0;
    nobj = 1;
    for (fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (!fl->FName[0])
	    continue;
	fprintf(fi2, "%s.o\n", fl->FName);
	if (nj == 0)
	    fprintf(fi, "OBJ%d = ", nobj);
	fprintf(fi, "%s.o ", fl->FName);
	nj += strlen(fl->FName) + 3;
	if (nj > 70) {
	    fprintf(fi, "\n");
	    nj = 0;
	    ++nobj;
	}
    }
    fprintf(fi2, "lvo.o\n");
    if (nj == 0)
	fprintf(fi, "OBJ%d = ", nobj);
    fprintf(fi, "lvo.o\n");
    fclose(fi2);
    fprintf(fi, "\nall:\t");
    for (i = 1; i <= nobj; ++i) {
	fprintf(fi, "$(OBJ%d) ", i);
    }
    fprintf(fi, "\n");
    fprintf(fi, "\tord %s ", dir);
    strcpy(dir+dirlen, "Ordout");
    fprintf(fi, "\t%s\n", dir);
    fprintf(fi, "\t-Delete %s\n", LibName);
    fprintf(fi, "\tlb %s -f %s\n", LibName, dir);

    strcpy(dir+dirlen, "lvo.asm");


    fclose(fi);
    fi = fopen(dir, "w");
    if (!fi)
	goto fail;
    for (fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (fl->FName[0])
	    fprintf(fi, "\t\tpublic\t_LVO%s\n", fl->FName);
    }
    for (off = -30, fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (!fl->FName[0]) {
	    off -= 6;
	    continue;
	}
	sprintf(tmp, "_LVO%s", fl->FName);
	fprintf(fi, "%-24s\tequ\t%d\n", tmp, off);
	off -= 6;
    }
    fclose(fi);

    for (fl = (FLIST *)FBase.mlh_Head; (APTR)fl != (APTR)&FBase.mlh_Tail; fl = (FLIST *)fl->Node.mln_Succ) {
	if (!fl->FName[0])
	    continue;
	strcpy(dir+dirlen, "TEMP");
	strcpy(tmp, dir);

	strcpy(dir+dirlen, fl->FName);
	strcat(dir+dirlen, ".asm");
	fi = fopen(tmp, "w");
	if (!fi)
	    goto fail;

	fprintf(fi,"\n; Machine Generated Link Tag\n\n");
	fprintf(fi,"\t\tFAR\tDATA\n");
	fprintf(fi,"\t\tpublic\t_LVO%s\n", fl->FName);
	fprintf(fi,"\t\tpublic\t_%s\n", LibGlob);
	fprintf(fi,"\t\tpublic\t_%s\n\n", fl->FName);
	fprintf(fi,"_%s:\n", fl->FName);

	/*
	 *  Generate linker tag to assembly.  If neither A0 or A1 is
	 *  used as an argument, use one as the link register, else
	 *  save A6 and use that.
	 *
	 *  If A2-A6 (inc A6 above), D2-D6 are required to hold
	 *  parameters, they are saved before the call, restored
	 *  after.   If they are not required to hold any parameters,
	 *  a JMP is issued instead of a JSR.
	 */

	{
	    uword argmask = fl->RegMask;
	    uword savmask = fl->RegMask;
	    short areg = 6;
	    if (!(argmask & 0x0100))   /*  A0 not used */
		areg = 0;
	    if (!(argmask & 0x0200))   /*  A1 not used */
		areg = 1;
	    savmask |= 1 << (areg+8);  /*  Add register usage  */
	    MoveToStack(fi, savmask & 0xFCFC);                          /*  save some regs      */
	    LoadFromStack(fi, argmask, 4+Offset(argmask & 0xFCFC));     /*  load params         */
	    fprintf(fi,"\t\tmove.l\t_%s,A%d\n", LibGlob, areg);
	    if (savmask & 0xFCFC) {
		fprintf(fi, "\t\tjsr\t_LVO%s(A%d)\n", fl->FName, areg);
		MoveFromStack(fi, savmask & 0xFCFC);                    /*  restore some regs   */
		fprintf(fi,"\t\trts\n");
	    } else {
		fprintf(fi, "\t\tjmp\t_LVO%s(A%d)\n", fl->FName, areg);
	    }
	}
	fclose(fi);
	if (cmp_file(tmp, dir) == 0) {  /*  update only if changed  */
	    DeleteFile(dir);
	    Rename(tmp, dir);
	}
    }
    return(0);
fail:
    printf("Unable to open %s\n", dir);
    return(-1);
}

char *
MToS(mask)
register uword mask;
{
    register short i;
    register short j = 0;
    static char buf[64];

    for (i = 0; i < 8; ++i, mask >>= 1) {
	if (mask & 1) {
	    if (j)
		buf[j++] = '/';
	    buf[j++] = 'D';
	    buf[j++] = i + '0';
	}
    }
    for (i = 0; i < 8; ++i, mask >>= 1) {
	if (mask & 1) {
	    if (j)
		buf[j++] = '/';
	    buf[j++] = 'A';
	    buf[j++] = i + '0';
	}
    }
    buf[j++] = 0;
    return(buf);
}


MoveToStack(fi, mask)
FILE *fi;
{
    char *str = MToS(mask);
    if (!str[0])
	return(0);
    if (!str[2]) {
	fprintf(fi, "\t\tmove.l\t%s,-(sp)\n", str);
    } else {
	fprintf(fi, "\t\tmovem.l\t%s,-(sp)\n", str);
    }
}

MoveFromStack(fi, mask)
FILE *fi;
uword mask;
{
    char *str = MToS(mask);

    if (!str[0])
	return(0);
    if (!str[2])
	fprintf(fi, "\t\tmove.l\t(sp)+,%s\n", str);
    else
	fprintf(fi, "\t\tmovem.l\t(sp)+,%s\n", str);
}

LoadFromStack(fi, mask, offset)
{
    char *str = MToS(mask);

    if (!str[0])
	return(0);
    if (!str[2])
	fprintf(fi, "\t\tmove.l\t%d(sp),%s\n", offset, str);
    else
	fprintf(fi, "\t\tmovem.l\t%d(sp),%s\n", offset, str);
}


PopStack(fi, mask)
FILE *fi;
uword mask;
{
    register short j;
    if (j = Offset(mask)) {
	if (j > 8)
	    fprintf(fi,"\t\tadd.w\t#%d,A7\n", j);
	else
	    fprintf(fi,"\t\taddq.l\t#%d,A7\n", j);
    }
}

Offset(mask)
uword mask;
{
    register short i, j;
    for (i = j = 0; i < 16; ++i) {
	if (mask & (1 << i))
	    ++j;
    }
    return(j*4);
}

cmp_file(name1, name2)
char *name1;
char *name2;
{
    short i, fd1, fd2;
    static char buf1[1024];
    static char buf2[1024];

    fd1 = open(name1, O_RDONLY);
    if (fd1 < 0)
	return(0);
    fd2 = open(name2, O_RDONLY);
    if (fd2 < 0) {
	close(fd1);
	return(0);
    }
    while ((i = read(fd1, buf1, sizeof(buf1))) > 0) {
	if (read(fd2, buf2, i) != i)
	    goto fail;
	if (bcmp(buf1, buf2, i) == 0)
	    goto fail;
    }
    if (read(fd2, buf2, 1) != 0)
	goto fail;
    close(fd1);
    close(fd2);
    return(1);
fail:
    close(fd1);
    close(fd2);
    return(0);
}

