/*******************************************************
 *
 *  a56 - a DSP56001 assembler
 *
 *  Written by Quinn C. Jensen
 *  July 1990
 *  jensenq@npd.novell.com (or jensenq@qcj.icon.com)
 *
 *******************************************************\

/*
 * Copyright (C) 1990-1992 Quinn C. Jensen
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 *
 */
static char *Copyright = "Copyright (C) 1990-1992 Quinn C. Jensen";

/*
 *  main.c - The "main" code for the assembler.
 *
 */

#include "a56.h"

#define MAX 1024

int pass;
int error;
extern unsigned long pc;
extern int seg;
BOOL binary_listing = FALSE;
BOOL list_includes = FALSE;
FILE *obj = NULL;
BOOL list_on = TRUE;
char *alloc();

main(argc,argv)
int argc;
char *argv[];
{
    int i;
    extern char *optarg;
    extern int optind;
    int c;
    char *output_file = "a56.out";
    char *input_file;

    while((c = getopt(argc, argv, "blo:")) != EOF) switch (c) {
	case 'b':
	    binary_listing++;
	    break;
	case 'l':
	    list_includes++;
	    break;
	case 'o':
	    output_file = optarg;
	    break;
	case '?':
	default:
	    fatal("usage: a56  [-b]  [-l]  [-o output-file]  input-file\n");
    }
    input_file = argv[optind++];
    obj = open_write(output_file);

    pc = 0;
    seg = 0;
    pass = 1;
    reset_psects();
    include(input_file);

    pc = 0;
    seg = 0;
    pass = 2;
    reset_psects();
    include(input_file);

    dump_symtab();
    fclose(obj);
    printf("errors=%d\n", error);
    return(error ? 1 : 0);
}

struct inc inc[MAX_NEST];
int inc_p = 0;

include(file)
char *file;
{
    FILE *fp = open_read(file);
    extern FILE *yyin;

    inc_p++;
    if(inc_p >= MAX_NEST)
	fatal("%s: include nesting too deep\n", file);

    inc[inc_p].file = file;
    inc[inc_p].fp = fp;
    inc[inc_p].line = 0;

    list_on = TRUE;
    if(inc_p > 1 && NOT list_includes)
	list_on = FALSE;

    yyin = inc[inc_p].fp;
    if(inc_p == 1)
	yyparse();
}

yywrap()
{
    fclose(inc[inc_p].fp);
    inc_p--;
    list_on = TRUE;
    if(inc_p > 1)
	list_on = FALSE;
    if(inc_p) {
	yyin = inc[inc_p].fp;
	return(0);
    } else {
	return(1);
    }
}

struct n
sym_ref(sym)	/* return symbol value or UNDEF if not defined yet */
char *sym;
{
    struct sym *sp, *find_sym();
    struct n result;

    result.type = UNDEF;

    sp = find_sym(sym);
    if(NOT sp) {
	if(pass == 2) {
	    yyerror("%s: undefined symbol", sym);
	}           
	return result;
    }

    return sp->n;
}

#define HASHSIZE 128

#define HASH(sym) (((sym)[0] ^ sym[1]) % HASHSIZE)

struct sym *symtab[HASHSIZE];

sym_def(sym, type, i, f)
char *sym;
int type;
int i;
double f;
{
    struct sym *sp, **stop, *find_sym();

    if(pass == 1) {
	if(find_sym(sym)) {
	    pass = 2;		/* what a kludge */
	    yyerror("%s: multiply defined symbol", sym);
	    pass = 1;
	    return;
	}
	stop = &symtab[HASH(sym)];
	sp = NEW(struct sym);
	sp->next = *stop;
	*stop = sp;
	sp->name = strsave(sym);
	sp->n.type = type;
	if(type == INT)
	    sp->n.val.i = i & 0xFFFFFF;
	else
	    sp->n.val.f = f;
    } else {
	sp = find_sym(sym);
	if(NOT sp)
	    fatal("internal error 304\n");
	if(sp->n.type != type ||
	    type == INT && sp->n.val.i != (i & 0xFFFFFF) ||
	    type == FLT && sp->n.val.f != f)
	    yyerror("%s: assembler phase error", sym);
    }	
}

struct sym *find_sym(sym)
char *sym;
{
    struct sym *sp, **stop;

    stop = &symtab[HASH(sym)];
    for(sp = *stop; sp; sp = sp->next)
	if(strcmp(sp->name, sym) == 0)
	    return(sp);
	
    return(NULL);
}

dump_symtab()
{
    struct sym *sp, **stop;
    int i;

    printf("\nSymbol Table\n");

    for(i = 0, stop = symtab; i < HASHSIZE; i++, stop++) {
	for(sp = *stop; sp; sp = sp->next) {
	    if(sp->n.type == INT)
		printf("%16s %06X\n", sp->name, sp->n.val.i);
	    else
		printf("%16s %20.10f\n", sp->name, sp->n.val.f);
	}
    }   
}

char *printcode(word)
int word;
{
    static char list[MAX], *lp;
    int i;

    word &= 0xFFFFFF;

    if(binary_listing) {
	sprintf(list, "%06X<", word);
	for(i = 0, lp = &list[7]; i < 24; i++, lp++) {
	    *lp = word & 1 << 23 - i ? '1' : '0';
	    if(i && i % 4 == 3)
		*++lp = i % 8 == 7 ? ' ' : ',';
	}
	lp[-1] = '>';
	lp[0] = '\0';
    } else {
	sprintf(list, "%06X", word);
    }
    return(list);
}

char *spacespace[2] = {
/*P:XXXX_XXXXXX_*/
 "              ",
/*P:XXXX_XXXXXX(XXXX_XXXX_XXXX_XXXX_XXXX_XXXX)_*/
 "                                             "};
char *spaces(n)
int n;
{
    return(&spacespace[binary_listing ? 1 : 0][n]);
}

gencode(seg, pc, word)
int seg, pc, word;
{
    extern char segs[];

    fprintf(obj, "%c %04X %06X\n", segs[seg], pc, word & 0xFFFFFF);
}

char fixbuf[1024];

char *fixstring(s)
char *s;
{
    char *bp = fixbuf;
    int ival;

    while(*s) {
	switch (*s) {
	    case '\'':
	    case '\"':
		s++;
		break;
	    case '\\':
		switch (*++s) {
		    case 'b': *bp++ = '\b'; break;
		    case 'r': *bp++ = '\r'; break;
		    case 'f': *bp++ = '\f'; break;
		    case 'n': *bp++ = '\n'; break;
		    case 't': *bp++ = '\t'; break;
		    case '\\': *bp++ = '\\'; break;
		    case '0':
			ival = 0;
			while(*s >= '0' && *s <= '9') {
			    ival <<= 3;
			    ival += *s++ - '0';
			}
			*bp++ = ival;
			break;
		}
		break;
	    default:
		*bp++ = *s++;
		break;
	}
    }
    *bp = '\0';
    return(fixbuf);
}

#define ONE 0x4000000

makefrac(s)
char *s;
{
    int frac = 0, div = 1;
    int scale = 1;

    while(*s) {
	switch(*s) {
	    case '-':
		scale = -1;
		break;
	    case '.':
		div = 10;
		break;
	    default:
		frac += (*s - '0') * scale * ONE / div;
		div *= 10;
		break;
	}
	s++;
    }

    return(frac + scale * 4 >> 3 & 0xFFFFFF);
}

/***************** psect stuff ************************/

struct psect *ptop = NULL, *cur_psect = NULL;

reset_psects()
{
    struct psect *pp;

    for(pp = ptop; pp; pp = pp->next)
	pp->pc = pp->bottom;

    set_psect(NULL);
}

struct psect *find_psect(name)
char *name;
{
    struct psect *pp;

    for(pp = ptop; pp; pp = pp->next)
	if(strcmp(pp->name, name) == 0)
	    return(pp);

    return(NULL);
}

set_psect(pp)
struct psect *pp;
{
    cur_psect = pp;
}

check_psect(seg, pc)
int seg;
unsigned int pc;
{
    if(cur_psect) {
	if(seg == cur_psect->seg && pc >= cur_psect->bottom && 
	    pc <= cur_psect->top) {
	    cur_psect->pc = pc;
	    return(TRUE);
	} else {
	    return(FALSE);
	}
    } else {
	return(TRUE);
    }
}

struct psect *new_psect(name, seg, bottom, top)
char *name;
int seg;
unsigned int bottom, top;
{
    struct psect *pp = find_psect(name);

    if(NOT pp) {
	pp = (struct psect *)alloc(sizeof *pp);
	pp->next = ptop;
	ptop = pp;
	pp->name = strsave(name);
	pp->seg = seg;
	pp->pc = bottom;
    }
    pp->bottom = bottom;
    pp->top = top;

    return(pp);
}
