/********************************************************/
/*							*/
/*		Q/C Compiler Version 3.2		*/
/*			(Part 2)			*/
/*							*/
/*     Copyright (c) 1984 Quality Computer Systems	*/
/*							*/
/*			12/29/83			*/
/********************************************************/

#include "stdio.h"
#include "cstddef.h"
#include "cglbdecl.c"

/*
 * This section processes function definitions
 */

static          argcnt;
static struct st *argchain;	/* Linked list of arguments */

/* Label numbers for entry call routine, routine address, and
local space. */

static          entlab;

/* Process a function definition */
dofunc(funcname, type, sc, argc)
    register char  *funcname;
    struct typeinfo *type;
{
    register char  *p;
    char           *index();
    struct st      *addglb();
    struct st	   *sp;

    argcnt = argc;
    prarg2();			/* Process the argument declarations. */

    if (verbose)
	qprintf("=== %s()\n", funcname);
    dumplits();			/* dump literals for last function */
    infunc = TRUE;
    addglb(funcname, type, sc, ID_VAR, DECL_GLB);
    if (libflag)
    {				/* is this a library generation */
	closeout();		/* close last module file */

	/* Mark all current globals as external for next file */
	for (sp = symtab; sp < symtab + nsym; ++sp)
	{
	    if (!sp->st_type)
		continue;	/* Skip empty entries */
	    if (sp->st_sc == SC_GLOBAL && sp->st_decl == DECL_GLB)
		sp->st_sc = SC_EXTERN;
	}

	if (p = index(outfil, ':'))	/* build new output filename */
	    ++p;
	else
	    p = outfil;		/* no drive specified */
	getfilename(funcname, p, defext);
	while (p = index(p, '_'))	/* no underscore in filename */
	    *p = '1';
	getoutfil();
    }
    codeseg();
    prologue(funcname);		/* beginning code for function */
    needpunc('{');		/* function must begin with { */
    compound();			/* evaluate all statments */
    epilogue(funcname, sc);	/* ending code for function */
    infunc = FALSE;
}

/* Process arg declarations for a function */
prarg2()
{
    int             sc;
    struct typeinfo *type;

    while (isdecl(&sc, &type))
    {				/* look for arg declarations */
	if (sc == SC_NONE)
	    sc = SC_AUTO;
	if (sc == SC_STATIC || sc == SC_EXTERN)
	{
	    scerr();		/* invalid storage class */
	    sc = SC_AUTO;
	}
	declarg(sc, type);
    }
}

/* Initalize local symbol table, and read in argument list. */
/* Return how many arguments were found. */
/* The st_info fields of each argument in the symbol table contain
   pointers to the previous argument, forming a linked list whose
   head is stored in argchain. */
int
prarg1()
{
    struct st      *sym, *nextarg, *addvar(), *findloc();
    char            name[NAMESIZE];
    int             argcnt;

    locspace = S_INT;		/* amount of local stack space */
    nregvar = 0;		/* # of register variables used */
    argcnt = 0;
    nextarg = NULL;

    while (!matchc(')') && !endst())
    {
	if (symname(name))
	{
	    /* See if a lower-level var exists */
	    if (sym = fanyvar(name, ID_VAR))
	    {
		if (sym->st_decl > DECL_LOC + 1)
		    error("Panic!: declaration level failure.");
		else if (sym->st_decl == DECL_LOC + 1)
		    multidef(name);
		else
		    sym->st_decl |= HIDMASK;   /* Hide it */
	    }

	    nextarg = addvar(DECL_LOC + 1, name, (struct typeinfo *) NULL,
				 SC_AUTO, ID_VAR, (int) nextarg);
	    ++argcnt;
	}
	if (!nextc(')') && !matchc(','))
	    error("Expected comma");
    }
    argchain = nextarg;
    return (argcnt);
}

/* Add arguments to local symbol table */
declarg(sc, type)
    register        sc;
    struct typeinfo *type;
{
    char            name[NAMESIZE];
    register struct st *sym;
    register struct typeinfo *t;
    struct typeinfo *declvar(), *ptrto(), *ptrtype;
    struct st      *findloc();
    int             dummy;

    do
    {
	t = declvar(name, type, sc, &dummy, FALSE, &dummy);
	if (t->t_code == T_ARRAY)
	{			/* chg array-of-x to ptr-to-x */
	    reftype(ptrtype = ptrto(t->t_base.p_type));
	    unreftype(t);
	    t = ptrtype;
	} else if (t->t_code > T_SIMPLE)
	{
	    error("Argument can't be that type");
	    continue;
	}
	if ((sym = findloc(name)) == NULL)
	    error("Name not in argument list");
	else if (sym->st_type != NULL)
	    multidef(name);
	else
	{			/* fill in symbol table info */
	    sym->st_type = t;
	    sym->st_sc = sc;
	}
    } while (matchc(','));
    ns();
}


/* Do function begin code */
prologue(funcname)
    char           *funcname;
{
    register        offset, regno;
    register struct st *sym, *newsym;

    retvalue = retlab = 0;	/* no return statement found yet */

    codeseg();
    if (strcmp(funcname, "main") == 0)
    {
	mainflag = TRUE;
	shell(argcnt);		/* parse command line */
    }
    linelabel(entlab = getlabel());
    if (trace.is_on)
	gentrace('>', funcname);

    /* March down the linked list of arguments inthe symbol table, replacing
     * the st_info field with the correct offset. */

    offset = ARGOFFSET + S_INT;
    sym = argchain;
    while (sym != NULL)
    {
	newsym = (struct st *) sym->st_info;

	if (sym->st_type == NULL)	/* undeclared args */
	    reftype(sym->st_type = inttype);	/* default to int */

	sym->st_info = offset;
	if (sym->st_sc == SC_REG && (regno = needreg(sym->st_type)))
	{
	    /* copy arg into preg, update st entry. */
	    sym->st_sc = SC_AUTO;
	    argtoreg(sym, regno);
	} else
	{
	    sym->st_sc = SC_AUTO;
	}
	offset += S_INT;
	/* offset from local stk frame pointer */
	sym = newsym;
    }

}


/* Do function end code */
epilogue(funcname, fsc)
    char           *funcname;
    int             fsc;
{
    if (retlab)			/* was an early return found? */
	linelabel(retlab);	/* if so, print return label */
    if (trace.is_on)
	gentrace('<', funcname);

    /* Restore the registers, if used */
    if (nregvar > 1)
	cpop(RREG2);
    if (nregvar)
	cpop(RREG1);

    if (retvalue)		/* is there a return value? */
	dotest();		/* if so, test for zero */
    locspace -= S_INT;
    if (locspace == 0)
    {				/* generate call to exit routine */
	if (argcnt != 0)
	    restsfp();		/* restore stk frame */
	cret();
    } else if (locspace >= -126)
    {
	ot("LD\tA,");
	outdec(-locspace);
	nl();
	ot("JP");
	libname("nexss");		/* clean up stack (short version) */ 
	nl();
    } else
    {
	callib("nexs");		/* clean up stack */
	defword();		/* specify how much stk space to clear */
	outdec(-locspace);
	nl();
    }

    /* This is the actual entry location */

    glblabel(funcname, fsc);	/* print function name */

    if (locspace == 0)
    {				/* generate call to appropriate */
	if (argcnt != 0)
	    callib("nen");	/* just set stk frame */
	/* else nothing to do */
    } else if (locspace >= -126)
    {
	ot("LD\tA,");
	outdec(locspace);
	nl();
	callib("nenss");		/* set up stack (short version) */ 
	nl();
    } else
    {
	callib("nens");		/* alloc stk space for locals */
	defword();		/* specify how much stk space needed */
	outdec(locspace);
	nl();
    }

    /* Save the registers, if used */
    if (nregvar)
	cpush(RREG1);
    if (nregvar > 1)
	cpush(RREG2);

    jump(entlab);

    freesym(DECL_LOC);
}


/* end of CC2.C */
