/*************************************************
MAIN.C: the main .c file for LOADER.

Written by:  Douglas Braun
             99 Summit Rd.
	     Riverside, CT 06878

July 1, 1985

**************************************************/

#include "loader.h"

char objfile[24] = "$$$.COM";  /* The output file name */
char deflib[24] = "QCC";    /* The default library name */
char libdrive[4] = "B:";		/* The alternate drive for library searches */
char relfile[24];  /* The current input file name */
char symfile[24];  /* The symbol file name */

extern int swapdir;

unsigned base;
unsigned startaddr;
unsigned memry = 0;
int num_errors;
int dosym = FALSE;
int verbose = FALSE;
int num_offsets = D_MAX_OFFSETS;
int num_symbols = D_MAX_SYMBOLS;
unsigned num_chains = D_MAX_CHAINS;

int lsflag;  /* TRUE if load, FALSE if search */
int needed;    /* TRUE is lsflag TRUE or if module needed during search*/

main(argc,argv)
int argc;
char *argv[];
{
    int arg;
    int j;
    int loaded_yet;

    if (argc < 2)
        usage();

    num_errors = 0;
    startaddr = 0;
    swapdir = BOTTOM;
    base = 0x103;
    loaded_yet = FALSE;

    for ( arg = 1; arg < argc; arg++)
    {

        if (argv[arg][0] == '-' )
	{
	    switch (chupper(argv[arg][1]))
	    {
	    case 'C':
	        if (arg > argc-2)
		    usage();
		strncpy(objfile,argv[++arg],14);
		if (index(objfile,'.') == NULL)
	    	    strcat(objfile,".COM");

		break;
	    case 'Y':
	        if (arg > argc-2)
		    usage();
		strncpy(symfile,argv[++arg],14);
		dosym = TRUE;
		break;
	    case 'S':
		if (loaded_yet == FALSE)
		    usage();

	        if (arg > argc-2)
		    usage();
	        strncpy(relfile,argv[++arg],14);
	        addext(relfile,".REL");
	        lsflag = FALSE;
	        load(relfile);
	 	break;
	    case 'L':
		if (loaded_yet == FALSE)
		    usage();
		libsearch(argv[arg]+2,TRUE);
		break;
	    case 'V':
		verbose = TRUE;
		break;
	    case 'M':
		memry = base;
		break;
	    case 'O':
		num_offsets = atoi(argv[++arg]);
		break;
	    case 'T':
		num_symbols = atoi(argv[++arg]);
		break;
	    case 'H':
		num_chains = atoi(argv[++arg]);
		break;
	    case '\0':    /* Just - */
		for (;;)
		{
		    /* Read module names to load from stdin */
		    *relfile = '\0';
		    if (scanf("%14s", relfile) != 1)
			break;
		    if (!*relfile || !strcmp(relfile, "."))
			break;

		    if (loaded_yet == FALSE)
		    {
		        init_vmem(objfile);
		        init_tables(num_offsets, num_symbols);
		        loaded_yet = TRUE;
		    }
		    addext(relfile,".O");
		    lsflag = TRUE;
		    load(relfile);

		}
	    }
        }
	else
	{
	    if (loaded_yet == FALSE)
	    {
	        init_vmem(objfile);
	        init_tables(num_offsets, num_symbols);
	        loaded_yet = TRUE;
	    }
	    strncpy(relfile,argv[arg],14);
	    addext(relfile,".O");
	    lsflag = TRUE;
	    load(relfile);
	}
    }


    if (are_undefs() == TRUE)
    {
	libsearch(deflib,FALSE);
    }

    res_chains(0xc000);
    res_chains(0xa000);
    res_chains(0x8000);
    swapdir = TOP;
    res_chains(0x6000);
    res_chains(0x4000);
    res_chains(0x2000);
    res_chains(0x0000);

    chk_undefs();
    dump_offsets();
    do_start(startaddr, base);
    if (memry == 0)
	memry = base;
    do_memry(memry);
    if (dosym == TRUE)
  	pr_table(symfile);  /* Print the symbol table */
    if (num_errors == 0)
    {
        flush_vmem();
        do_info(base);
        printf("The object file is: %s\n",objfile);
    }
    else
    {
	unlink(objfile);
    }

}   




/* If the filename name does not have an extension,
this adds ext to the end of it. Ext must include the period. */

addext(name, ext)
char *name;
char *ext;
{
    char *ep;

    if (index(name,'.') == NULL)
    {
	ep = name;
 	while (*ep != '\0')
	    ep++;   
 	strcpy(ep,ext);
    }
}


usage()
{
    printf("Usage: loader [-C comfile][-Y symbolfile]");
    printf("[-V][-O offsetsize][-H chainsize]\n");
    printf("    [-T tablesize] relfile [-M][-Llibname][-S libfile]...\n");
    exit(1);
}
    


unsigned codebase;
unsigned database;
unsigned codesize;
unsigned datasize;
unsigned currloc;
AFIELD a_field;
BFIELD b_field;


load(name)
char *name;
{
    register int databyte;
    register unsigned dataword;

    if (bitfopen(name,"rb") == FALSE)
    {
	printf("Can't open input file\n");
	return(1);
    }

    if (verbose == TRUE)
    {
        if (lsflag == TRUE)
            printf("loading %s\n",name);
        else
            printf("searching %s\n",name);
    }

    codebase = database = currloc = base;
    codesize = datasize = 0;
    needed = lsflag;

    /* The main parsing loop */

    while(1)
    {
	switch(getbit())
	{
	    case 0:
		databyte = getbchar();
#ifdef DEBUG
		printf("Load absolute byte %x\n",databyte);
#endif
	  	put_byte(databyte,currloc++);
		break;

	    case 1:
		/* a relocatable item */

		switch(getnbits(2))
		{
		    case 0:
			if (linkitem() == EOF)
			    return(0);
			break;
		    case 1:
			dataword = getaddr();
#ifdef DEBUG
			printf("Program relative word %x\n",dataword);
#endif
			dataword += codebase;
			put_word(dataword,currloc);
			currloc += 2;
			break;
		    case 2:
			dataword = getaddr();
#ifdef DEBUG
			printf("Data relative word %x\n",dataword);
#endif
			dataword += database;
			put_word(dataword,currloc);
			currloc += 2;
			break;
		    case 3:
			bad_common();
			break;
		}
		break;

	    default:
		printf("Premature EOF\n");
		return(1);

	}
    }

}


linkitem()
{
    int itemno;

    switch(itemno = getnbits(4))
    {
    case 1:
	getbfield(&b_field);
	bad_common();
	break;

    case 0:
	getbfield(&b_field);
	if (needed == FALSE && is_undefined(b_field.b_symbol))
 	    needed = TRUE;
	break;

    case 2:
	/* Module name: The first thing we see. */
	getbfield(&b_field);
	if (verbose == TRUE)
	    printf("Module %s base: %xH\n",b_field.b_symbol,base);
        codebase = database = currloc = base;
        codesize = datasize = 0;
	break;

    case 3:
	getbfield(&b_field);
	printf("Warning: Library search requests not implemented.\n");
	break;

    case 4:
	getbfield(&b_field);
	printf("Warning: Extension link items not implemented.\n");
	break;

    case 5:
	getafield(&a_field);
	getbfield(&b_field);
	bad_common();
	break;
	
    case 6:	/* Define an external chain */
	getafield(&a_field);
	getbfield(&b_field);
	switch (a_field.addrtype)
	{
	case ABSOLUTE:
	    add_chain(a_field.address,b_field.b_symbol);
	    break;
	case PROGREL:
	    add_chain(a_field.address + codebase, b_field.b_symbol);
	    break;
	case DATAREL:
	    add_chain(a_field.address + database, b_field.b_symbol);
	    break;
	case COMMONREL:
	    bad_common();
	    break;
	}
	break;

    case 7:		/* Define a symbol */
	getafield(&a_field);
	getbfield(&b_field);
	switch (a_field.addrtype)
	{
	case ABSOLUTE:
	    def_symbol(a_field.address,b_field.b_symbol);
	    break;
	case PROGREL:
	    def_symbol(a_field.address + codebase, b_field.b_symbol);
	    break;
	case DATAREL:
	    def_symbol(a_field.address + database, b_field.b_symbol);
	    break;
	case COMMONREL:
	    bad_common();
	    break;
	}

	break;

    case 9:
 	/* an 'External + offset' */
	getafield(&a_field);
	if (a_field.addrtype != ABSOLUTE)
	{
	    printf("Oops, a type 9 without absolute address\n");
	    abort();
	}
	add_offset(currloc, a_field.address);
 	break;

    case 12:
	getafield(&a_field);
        switch (a_field.addrtype)
	{
	case ABSOLUTE:
	    do_chain(a_field.address, currloc);
	    break;
	case PROGREL:
	    do_chain(a_field.address + codebase, currloc);
	    break;
	case DATAREL:
	    do_chain(a_field.address + database, currloc);
	    break;
	case COMMONREL:
	    bad_common();
	    break;
	}
	break;

    case 8:
	getafield(&a_field);
	printf("Warning: External - offset not implemented.\n");
	abort();
        break;

    case 10:
	/* We will see item 10 (the data size) after the symbol names and 
        module name, but before anything else. If needed is TRUE, do nothing.
	Otherwise, flush everything till end-of-module, and reset needed */

	getafield(&a_field);
	if (needed == FALSE)
	{
	    mod_flush();
	    needed = lsflag;
	}
	else
	{
	    if (a_field.addrtype != 0)
	    {
	        printf("Address type of data size is not absolute!\n");
	    }
	    if (verbose == TRUE)
		printf("Size of data area: %d bytes.\n", a_field.address);
	    datasize = a_field.address;
	}
	break;

    case 11:
	getafield(&a_field);
#ifdef DEBUG
	printf("Set location counter. Address type %x address %x\n",
		a_field.addrtype,a_field.address);
#endif
	switch(a_field.addrtype)
	{
	case 0:
	    currloc = a_field.address;
	    break;
	case 1:
	    currloc = codebase + a_field.address;
	    break;
	case 2:
	    currloc = database + a_field.address;
	    break;
	case 3:
	    bad_common();
	    break;
	}
	break;

    case 13:
	getafield(&a_field);
	if (needed == FALSE)
	{
	    mod_flush();
	    needed = lsflag;
	}
	else
	{
	    if (verbose == TRUE)
	        printf("Size of code area: %d bytes.\n", a_field.address);
	    codesize = a_field.address;
	    database = base + codesize;
#ifdef DEBUG
            printf("Base: %x Codebase: %x Database: %x\n",base,codebase,database);
#endif
	    if (a_field.addrtype != PROGREL)
	    {
	        printf("Oops, a type 13 without absolute address: %x\n",a_field.addrtype);
	    }
 	}
	break;

    case 14:
	getafield(&a_field);
#ifdef DEBUG
	printf("End of module.  Start address type %x address %x\n",
		a_field.addrtype,a_field.address);
#endif
	if (a_field.addrtype != ABSOLUTE || a_field.address != 0)
 	{
	    if (startaddr != 0)
	    {
		printf("Error: Multiply defined start address.\n");
		num_errors++;
	    }
	    else
	    {
	        switch(a_field.addrtype)
	        {
	        case 0:
	            startaddr = a_field.address;
	            break;
	        case 1:
	            startaddr = codebase + a_field.address;
	            break;
	        case 2:
	            startaddr = database + a_field.address;
	            break;
	        case 3:
	            bad_common();
	            break;
	        }
	    }
	}
	base = base + codesize + datasize;
	bytesync();
	needed = lsflag;
	break;

    case 15:

  	if (verbose == TRUE)
	    printf("End of file.\n");
        return(EOF);
	break;
    }

    return(0);
}


/* This takes the name xxx, and searches "xxxlib.rel" on
the current drive, or drive LIBDRIVE if it's not on the
current drive. If exists is TRUE, it is an error if the file
cannot be found. */

libsearch(name, exists)
char *name;
int exists;
{
    char fname[24];
    FILE *fp;

    strcpy(fname,name);
    strcat(fname,"LIB");
    strcat(fname,".REL");

    fp = fopen(fname,"rb");

    if (fp != NULL) /* we found it */
    {
	fclose(fp);
	lsflag = FALSE;
	load(fname);
    }
    else
    {
	strcpy(fname,libdrive);
	strcat(fname,"LIB");
	strcat(fname,name);
	strcat(fname,".REL");

	fp = fopen(fname,"rb");
	if (fp == NULL && exists == FALSE)
            return;

	fclose(fp);
	lsflag = FALSE;
	load(fname);
    }
   
}



abort()
{
    printf("Fatal error.\n");
    close_vmem();
    unlink(objfile);
    exit(1);
}


bad_common()
{
    printf("Can't handle COMMON regions.  ");
    abort();
}


char *alloc(nbytes)
unsigned nbytes;
{
    static char *retval; /* Static for speed */

    retval = sbrk(nbytes);
    if ((int)retval == -1)
 	bad_alloc();

    return(retval);
}



bad_alloc()
{
    printf("Memory allocation failure.  ");
    abort();
}



/* This reads in the file until it finds an end of module item */

mod_flush()
{

    if (verbose == TRUE)
	printf("Flushing...\n");
    while(1)
    {
	switch(getbit())
	{
	    case 0:
		getbchar();
		break;

	    case 1:
		/* a relocatable item */
		switch(getnbits(2))
		{
	        case 0:
		    switch(getnbits(4))
		    {
		    case 0:
		    case 1:
		    case 2:
    		    case 3:
		    case 4:
			getbfield(&b_field);
			break;

		    case 5:
		    case 6:
		    case 7:
			getafield(&a_field);
			getbfield(&b_field);
			break;
	
		    case 8:
		    case 9:
		    case 10:
		    case 11:
		    case 12:
		    case 13:
			getafield(&a_field);
			break;
		
		    case 14:
			getafield(&a_field);
#ifdef DEBUG
			printf("End of module.  Address type %x address %x\n",
				a_field.addrtype,a_field.address);
#endif
			bytesync();
			return(TRUE);   /* Done flushing */
		
		    case 15:
			printf("Oops.. Hit end of file before end of module in search!\n");
			abort();
			break;
		    }
		    break;

		case 1:
		case 2:
		case 3:
		    getaddr();
		    break;
 	        default:
		    printf("Premature EOF while flushing module!\n");
		    abort();
		    break;
		}
		break;

	    default:
		printf("Premature EOF while flushing module!\n");
		abort();
		break;
	}
    }
}

