
/* CC65 main program */

#include <stdio.h>
#include "cclex.h"
#include "cc65.h"

#define pathopen pathopen

#ifndef M6502

int atari;
int debug;
int stats;
int htabstats;
int maxloc = 0;
int tmpx, tmpy, tmpz;
extern int outcnt;
#endif

#ifdef atarist
extern long _stksize = 32768;
#endif

struct hashent * decl();
struct hashent * declare();

/* char	openerr[] = "Couldn't open: "; */

#ifdef pathopen

/* util function for opening files */
FILE * pathopen(name, ext, force, mode, truename)
char * name;		/* filename */
char * ext;		/* default extension */
int force;		/* force extension */
char * mode;		/* open mode */
char * truename;	/* resultant pathname */
{
FILE * f;
char * extp;

  strcpy(truename, name);
  if (!(extp = (char *)strchr(truename, '.')))
    strcpy(truename + strlen(truename), ext);
  else if (force)
    strcpy(extp, ext);
  if (!(f = (FILE *)fopen(truename, mode)))
    {
      PError("Can't open ", truename);
      PError(" mode ", mode);
      fatal(" Giving up");
    }
  return(f);
}

#else

#ifdef M6502

char * frob_name(name, ext, buf)
char * name;
char * ext;
char * buf;
{
  char tmp[40];
  char * p;

  if (!strchr(name, ':'))	/* no device? */
    {
      strcpy(tmp, "D:");
      strcat(tmp, name);
    }
  else
    strcpy(tmp, name);
  if (ext)
    {
      if (p = strchr(tmp, '.'))
	*p = '\0';
      strcat(tmp, ext);
    }
  strcpy(buf, tmp);
  return(buf);
}

#else
char * frob_name(name, ext, buf)
char * name;
char * ext;
char * buf;
{
char *p;

 if(!name)
   return (char *)0;

  p = (char *)strchr(name, '.');


  if (ext)
    {
      if (p)
	p[0] = 0;
    }

  strcpy(buf, name);
  if (ext)
    strcat(buf, ext);
  return(buf);
}

#endif
#endif

static char argbuf[128];
static char * argp;

#ifdef M6502
char in_name[20];
char out_name[20];
#else
char in_name[128];
char out_name[128];
#endif

#undef NOARGC

main(argc, argv)
int argc;
char *argv[];
{
  int i;
#ifdef pathopen
  char fout[128];
#else
  char *fout;
#endif

  /* set up global heap */
#ifdef M6502

#define LOMEM 0x02E7
  extern int _start;		/* program start address */

  gblspace = *((char ** )LOMEM); /* start heap here */
  gblend = &_start;		/* end it here */
#else
  /* cross-compiler */
  gblspace = (char *)malloc(GSPACE);
  gblend = gblspace + GSPACE;
#endif

#ifdef M6502
  /* later, put all this stuff in a loop */
  if (argc < 2)			/* under some kind of system that
				   doesn't do args? */
    argc = readargs("CC65>", argbuf, argv + 1) + 1;
#endif

  argp = NULL;
  fin = NULL;
#ifndef M6502
  atari = 0;
  debug = 0;
  stats = 0;
  htabstats = 0;
#endif

  for (i = 1; i < argc; i++)
    {
      if (*(argp = argv[i]) == '-')
	{
	  switch (toupper(argp[1]))
	    {
#ifndef M6502
	    case 'A':		/* hot-wire M6502 def */
	      atari = 1;
	      break;
	    case 'D':		/* print debug msgs */
	      debug = 1;
	      break;
	    case 'S':		/* print storage statistics */
	      stats = 1;
	      break;
	    case 'H':		/* print hashtab stats */
	      htabstats = 1;
	      break;
	    case 'C':		/* include source as comments */
	      source = 1;
	      break;
#endif
	    case 'O':		/* optimize */
	      optimize = 1;
	      break;
	    case 'V':		/* verbosity */
	      ++verbose;
	      break;
	    case 'I':		/* where to include <> things */
	      incl_dir = argp + 2;
	      break;
	    case 'N':
		print_copyleft();
		exit(0);
	    case '?':
	    case '-':
		print_usage();
	    default:
	      printf("Invalid option %s\n", argp);
	    }
	}
      else
	{
#ifdef pathopen
	  inp = pathopen(argp, ".c", 0, "r", (fin = argp));
#else
	if(argc>1 && argp)
	 {
	   fin = frob_name(argp, (strchr(argp, '.') ? NULL : ".c"), in_name);
	   inp = fopen(fin, "r");
         }
        else
         fin=0;
#endif
	}
    }
  if (!fin)
    {
      inp = stdin;
      fin = "stdin.c";
    }
#ifdef pathopen
  output = pathopen(fin, ".m65", 1, "w", fout);
#else
  fout = frob_name(fin, ".m65", out_name);
  output = fopen(fout, "w");
#endif
/****
#ifndef M6502
  if (!incl_dir)
    incl_dir = (char * )getenv("CC65INCLUDE");
#endif
****/
  if (verbose)
      printf("CC65 v 1.1p2 [*nix version]\n");
  compile();
  clout();
}


/*
	compile()
	Compiler begins execution here
	inp is input fd,
	output is output fd
	(called multiple times from main)
*/
compile()
{
char dummy;

  litptr =			/* clear literal pool */
    oursp =			/* stack ptr (relative) */
    errcnt =			/* no errors */
    eof =			/* not eof yet */
    ncmp =			/* no open compound states */
    macdef =			/* no macros defined yet */
    ifile =			/* index into include file array */
    lovptr =			/* index into local variable array */
    glblbl =			/* initial global label number */
    ln =			/* initial line number */
    0;				/*  ...all set to zero.... */

  filetab[0].f_iocb = inp;

  gsptr = gblspace;
  lsptr = locspace;
  wqptr = wq;			/* clear while queue */

  outqi = outq;
  outqsz = OUTQSZ;
  glvptr = NULL;

  i_ifdef = -1;
  litlab =			/* hotwire literal pool label */
    nxtlab = 1;

/*
#ifdef M6502
    ret_addr = &dummy - 2;
#endif
*/
  bzero(machtab, 256);
  bzero(macltab, 256);
  bzero(htab, 256);

  do_kill();			/* empty input line */

#ifndef M6502
  if (atari)
    {
      strcpy(line, "M6502");
      addmac();
      do_kill();
    }
#endif

  /*
     process ALL input
  */
  parse();

#ifndef M6502
  /*
     Dump global symbols
  */
  dumpglbs();

  /*
     Dump struct/unions
  */
  dumpstruct();
#endif

  /*
     Dump literal pool.
  */
  dumplits();

  /*
     Dump external names.
  */
  dumpnams();

#ifndef M6502
  if (stats)
    {
struct hashent * p;
int cnt;

      for (cnt = 0, p = glvptr; p != NULL; p = p->h_link)
	++cnt;
      printf("\n%d globals, space used: %d bytes\n", cnt, gsptr - gblspace);
      printf("Max local space used: %d bytes\n", maxloc);
      printf("Literal space used: %d bytes\n", litspace);
      printf("Object file: %d bytes\n", outcnt);
      printf("hash: %d calls, %d bytes\n", tmpx, tmpy);
    }
  if (htabstats)
    {
int i;
struct hashent * p;
char * q;

      printf("\n\nSymbol Hash Table Summary\n");
      for (i = 0; i < 128; ++i)
	{
	  printf("%3d : ", i);
	  if (htab[i])
	    {
	      for (p = htab[i]; p != NULL; p = p->h_ptr)
		{
		  printf("%s ", p->h_name);
		}
	      printf("\n");
	    }
	  else
	    {
	      printf("empty\n");
	    }
	}
      printf("\n\nMacro Hash Table Summary\n");
      for (i = 0; i < 128; ++i)
	{
	  printf("%3d : ", i);
	  if (machtab[i])
	    {
	      for (q = machtab[i]; q != NULL; q = *((char ** )q))
		{
		  printf("%s ", q + sizeof(char *));
		}
	      printf("\n");
	    }
	  else
	    {
	      printf("empty\n");
	    }
	}
    }
#endif
  ersum();
}

/*
	parse()
	Process all input text

	At this level, only static
	declarations,
	defines, includes, and function
	definitions are legal....
*/
parse()
{
int comma;
struct hashent * psym;
struct hashent * sadr;
int sc;
char tarray[MAXTYPELEN];
int tmpsc;
int type;

  gettok();			/* "prime" the pump */
  gettok();
  while (curtok != CEOF)
    {
/*
#ifdef M6502
	abtchk();
#endif
*/
      sc = getsclass(0, SC_EXTERN | SC_STATIC);
      type = gettype(T_INT, &sadr);

      if (curtok == SEMI)
	{
	  gettok();
	  continue;
	}
      comma = 0;
      while (1)
	{
	  absdecl = 0;
	  if ((psym = declare(tarray, type, sadr)) == NULL)
	    {
	      gettok();
	      break;
	    }

	  if ((tarray[0] != T_FUNC) &&
	      ((sc & SC_DEFAULT) || !(sc & SC_EXTERN)))
	    {
	      tmpsc = sc | SC_DEFINED | SC_STORAGE;
	    }
	  else
	    {
	      tmpsc = sc;
	    }
	  addglb(psym, tarray, tmpsc);

	  if (tmpsc & SC_STORAGE)
	    {
/*		outcdf(psym->h_gdata);	*/
	      outgblc(psym->h_gdata);	/* -- jrd */

	      if (curtok == ASGN)
		{
		  gettok();
		  parseinit(psym->h_gtptr);
		}
	      else
		{
		  outsp(SizeOf(psym->h_gtptr));
		}
	    }

	  if (curtok != COMMA)
	    break;
	  gettok();
	  comma = 1;
	}

      if (curtok == CEOF)
	{
	  break;
	}
      else if (curtok == SEMI)
	{
	  gettok();
	}
      else
	{
	  /*
             Possible function.
          */
	  if (comma)
	    {
	      Syntax();
	    }
	  if (tarray[0] != T_FUNC)
	    {
	      Illegal("function");
	    }
	  if (psym != NULL)
	    {
	      newfunc(psym);
	    }
#ifndef M6502
	  /*
             print local symbols.
          */
	  dumploc(psym);
#endif
	  eraseloc();
	}
    }

}

/*
	declare( ptyp, type )
	Construct a type array.
*/
struct hashent * declare(ptyp, type, sadr)
char * ptyp;
int type;
struct hashent * sadr;
{
struct hashent * psym;

  psym = decl(&ptyp);
  if (type & T_STRUCT)
    {
/* jrd hacked this part */
#ifdef M6502
      encode(ptyp, sadr); /* the old way */
#else
      encode(ptyp, (int )sadr - (int )gblspace); /* encode offset in glb mem */
#endif

      *ptyp |= type;
      ptyp += 3;
    }
  else
    {
      *ptyp++ = type;
    }
  *ptyp = '\0';
  return (psym);
}

/*
	decl()
	Process declarators.
*/
struct hashent * decl(ptyp)
char ** ptyp;
{
struct expent lval;
struct hashent * psym;
int sz;

  if (curtok == STAR)
    {
      gettok();
      psym = decl(ptyp);
      *(*ptyp)++ = T_PTR;
      return(psym);
    }
  else if (curtok == LPAREN)
    {
      gettok();
      psym = decl(ptyp);
      needbrack(RPAREN);
    }
  else
    {
      if (absdecl)
	{
	  psym = NULL;
	}
      else if (curtok == IDENT)
	{
	  gettok();
	  psym = (struct hashent * )curval;
	}
      else
	{
	  Syntax();
	  return(NULL);
	}
    }

  while (curtok == LBRACK || curtok == LPAREN)
    {
      if (curtok == LPAREN)
	{
	  gettok();
	  funargs();
	  *(*ptyp)++ = T_FUNC;
	}
      else
	{
	  gettok();
	  sz = 0;
	  if (curtok != RBRACK)
	    {
	      constexp(&lval);
	      sz = lval.e_const;
	    }
	  needbrack(RBRACK);
	  *(*ptyp)++ = T_ARRAY;
	  encode(*ptyp, sz);
	  *ptyp += 3;
	}
    }
  return(psym);
}

/*
	getsclass( lv, dflt )
	Process "<storage-class>"
*/
int getsclass(lv, dflt)
int lv;
int dflt;
{
#ifdef old_cruft
  if (curtok == EXTERN)
    {
      gettok();
      return(SC_EXTERN | SC_STATIC);
    }
  else if (curtok == STATIC)
    {
      gettok();
      return(SC_STATIC);
    }
#ifndef M6502 /* save a little space */
  else if (curtok == AUTO)
    {
      gettok();
      if (lv == 0)
	{
	  Warning("auto not allowed here");
	  return (SC_STATIC);
	}
      return(SC_STACK);
    }
#endif
  else
    {
      if (curtok == REGISTER)
	{
	  gettok();
	}
      return(dflt | SC_DEFAULT);
    }
#else

  switch (curtok)
    {
    case EXTERN:
      {
	gettok();
	return(SC_EXTERN | SC_STATIC);
	break;
      }
    case STATIC:
      {
	gettok();
	return(SC_STATIC);
	break;
      }
#ifndef M6502 /* save a little space */
    case AUTO:
      {
	gettok();
	if (lv == 0)
	  {
	    Warning("auto not allowed here");
	    return(SC_STATIC);
	  }
	return(SC_STACK);
      }
#endif
    case REGISTER:
      {
	gettok();
	/* and fall thru... */
      }
    default:
      return(dflt | SC_DEFAULT);
    }
#endif
}

/*
  gettype( dflt ) Process "<type>"
*/
int gettype(dflt, sptr)
int dflt;
struct hashent ** sptr;
{
  int sindex;
  int strtype;
  int sz;
  int type;

  switch (curtok)
    {
    case CHAR:
      gettok();
      return(T_CHAR);
    case LONG:
      Warning("long == short");
      /* fall through */
    case SHORT:
      if (nxttok == INT)
	gettok();
      /* fall through */
    case INT:
      gettok();
      return(T_INT);
    case UNSIGNED:
      gettok();
      if (curtok == CHAR)
	{
	  gettok();
	  return(T_UCHAR);
	}
      if (curtok == INT)
	gettok();
      return(T_UINT);
    case STRUCT:
    case UNION:
      strtype = curtok == STRUCT ? T_STRUCT : T_UNION;
      gettok();
      if (curtok == IDENT)
	{
	  *sptr = (struct hashent * )curval;
	  gettok();
	}
      else
	{
	  *sptr = (struct hashent * )Gmalloc(sizeof(struct hashent));
	  bzero(*sptr, sizeof(struct hashent));
	}
      sz = declstruct(*sptr, strtype);
      addstag(*sptr, sz);
      return(strtype);
#ifndef M6502
      /* leave enums out of native compiler, to save space */
    case ENUM:
      gettok();
      needtok(IDENT, "tag");
      declenum();
      return(T_INT);
#endif
    default:
      if (dflt < 0)
	{
	  Missing("type");
	}
      return(dflt);
    }
}

#ifndef M6502
/*
	declenum()
	Process body of enum.
*/
declenum()
{
int enumval;
struct expent lval;
struct hashent * psym;

  if (curtok != LCURLY)
    {
      return;
    }
  gettok();
  enumval = 0;
  while (curtok != RCURLY)
    {
      if (curtok != IDENT)
	{
	  Missing("identifier");
	  continue;
	}
      psym = (struct hashent * )curval;
      gettok();
      if (curtok == ASGN)
	{
	  gettok();
	  constexp(&lval);
	  enumval = lval.e_const;
	}
      addglb(psym, type_int, SC_ENUM);
      psym->h_gdata = enumval++;
      if (curtok != COMMA)
	break;
      gettok();
    }
  needbrack(RCURLY);
}

#endif

/*
	declstruct()
	Process body of struct/union declaration.
*/
int declstruct(last, strtype)
struct hashent * last;
int strtype;
{
int offset;
struct hashent * psym;
struct hashent * sadr;
int sz;
char tarray[MAXTYPELEN];
int type;

  if (curtok != LCURLY)
    {
      return(0);
    }
  gettok();
  sz = 0;
  while (curtok != RCURLY)
    {
      type = gettype(-1, &sadr);
      while (1)
	{
	  absdecl = 0;
	  psym = declare(tarray, type, sadr);
	  last->h_link = psym;
	  last = psym;

	  offset = strtype == T_STRUCT ? sz : 0;
	  addsfld(psym, tarray, offset);
	  offset = SizeOf(tarray);
	  if (strtype == T_STRUCT)
	    {
	      sz += offset;
	    }
	  else
	    {
	      if (offset > sz)
		sz = offset;
	    }

	  if (curtok != COMMA)
	    break;
	  gettok();
	}
      ns();
    }
  gettok();
  return(sz);
}

#ifndef M6502
/*
	ptype( psym, tarray )
	Output translation of type array.
*/
ptype(psym, tarray)
struct hashent * psym;
char * tarray;
{
  char * p;

  printf("%s: ", psym ? psym->h_name : "<>");
  for (p = tarray; *p != '\0'; ++p)
    {
      if (*p & T_STRUCT)
	{
	  /* jrd here ...
	     printf("struct/union of %s\n",
	     ((struct hashent * )decode(p))->h_name);	*/
	  printf("struct/union of %s\n",
		 ((struct hashent * )(((int) decode(p) + (int) gblspace)))->h_name);

	  p += 2;
	}
      else
	{
	  if (*p & T_UNSIGNED)
	    {
	      printf("unsigned ");
	    }
	  switch (*p)
	    {
	    case T_CHAR:
	    case T_UCHAR:
	      printf("char\n");
	      break;
	    case T_INT:
	    case T_UINT:
	      printf("int\n");
	      break;
	    case T_SHORT:
	      printf("short\n");
	      break;
	    case T_LONG:
	      printf("long\n");
	      break;
	    case T_FLOAT:
	      printf("float\n");
	      break;
	    case T_DOUBLE:
	      printf("double\n");
	      break;
	    case T_PTR:
	      printf("ptr to ");
	      break;
	    case T_ARRAY:
	      printf("array[%d] of ", decode(p + 1));
	      p += 3;
	      break;
	    case T_FUNC:
	      printf("function returning ");
	      break;
	    default:
	      printf("unknown type: %X\n", *p);
	    }
	}
    }
}

#endif

/*
   Dump the literal pool
*/
dumplits()
{
int k;

  /* if nothing there, exit...*/
  if (litptr == 0)
    return;
  /* print literal label */
/*	outcdf(LITLAB);		*/
  outcdf(litlab);
  /* data for next n bytes */
/*	outdat(litptr);	*/
  /* init an index... */
/*	k = 0;
/*	while (k < litptr)	/* 	to loop with */
/*	    outbyte(litq[k++]);				*/
  outbv(litq, litptr);

  litspace += litptr; /* account for space */
  litlab = getlabel();		/* get next lit pool label */
  litptr = 0;
}

/*
	clout()
	Flush the output buffer and close the output file.
*/
clout()
{
#ifdef old_cruft
int len;

  if ((len = outqi - outq) != 0)
    {
      ciov(output, 11, outq, len, -1, -1);
    }
#else
/*
    char *	p;

    for (p = outq; p != outqi; ++p) cout(*p);
*/
  flushout();
#endif
/*    fclose(output); */
  cclose(output);
}

#ifdef old_cruft
/*
	abtchk()
	check if user pressed break
*/
abtchk()
{
#ifdef M6502
char i;
  if (peek(17))
    return;
  poke(17, 255);
  i = 8;
  while (i > 1)
    cclose(--i);
  usr(dpeek(10)); /* jmp (DOS) */
#endif
}

#endif

#ifdef old_cruft
/*
	fast()
	Turn off screen, turn on CRITIC for speed.
*/
fast()
{
  poke(SDMCTL, 0);
  poke(CRITIC, 0xff);
  critic = 0xff;
}

/*
	slow()
	Return screen and CRITIC to normal.
*/
slow()
{
  poke(SDMCTL, 0x22);
  poke(CRITIC, 0);
  critic = 0;
}

#endif

print_usage()
{
  printf("\nCC65 v1.1p2 - (Ported to *nix by Intruder  1993)\n\n");
  printf("	-a	Hotwire def. M6502\n");
  printf("	-d	Debugging on\n");
  printf("	-s	Print storage info\n");
  printf("	-h	Print hashtab stats\n");
  printf("	-c	Include source as comment\n");
  printf("	-o	Optimize code\n");
  printf("	-v	Verbose mode\n");
  printf("	-i <fn>	Specify include directory\n");
  printf("	-?	This help message\n\n");
  printf("	-n	print copyright notice\n");
exit(0);
}
