/* << ACE >>

   -- Amiga BASIC Compiler --

   ** Parser: library and machine code function handling **
   ** Copyright (C) 1998 David Benn
   ** 
   ** This program is free software; you can redistribute it and/or
   ** modify it under the terms of the GNU General Public License
   ** as published by the Free Software Foundation; either version 2
   ** of the License, or (at your option) any later version.
   **
   ** This program is distributed in the hope that it will be useful,
   ** but WITHOUT ANY WARRANTY; without even the implied warranty of
   ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   ** GNU General Public License for more details.
   **
   ** You should have received a copy of the GNU General Public License
   ** along with this program; if not, write to the Free Software
   ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

   Author: David J Benn
     Date: 26th October-30th November, 1st-13th December 1991,
	   14th,20th-27th January 1992, 
           2nd-17th, 21st-29th February 1992, 
	   1st,13th,14th,22nd,23rd March 1992,
	   21st,22nd April 1992,
	   2nd,3rd,11th,15th,16th May 1992,
	   7th,8th,9th,11th,13th,14th,28th,29th,30th June 1992,
	   2nd-8th,14th-19th,26th-29th July 1992,
	   1st-3rd,7th,8th,9th August 1992,
	   6th,7th,22nd December 1992,
	   28th February 1993,
	   12th April 1993,
	   16th,17th December 1993,
	   2nd,3rd,13th January 1994,
	   12th June 1994,
	   5th,20th,28th August 1994,
	   1st,10th September 1994,
	   8th,9th March 1995
*/

#include "acedef.h"

/* locals */
static 	char 	*reg[] = { "  ","d0","d1","d2","d3","d4","d5","d6","d7",
			   "a0","a1","a2","a3","a4","a5" };

/* externals */
extern	int	sym;
extern	int	lastsym;
extern	int	typ;
extern	int	lev;
extern	char   	id[MAXIDSIZE]; 
extern	char   	ut_id[MAXIDSIZE];
extern	char 	tempshortname[80];
extern	char 	templongname[80];
extern	char	stringval[MAXSTRLEN];
extern	SYM	*curr_item;
extern	ACELIBS acelib[NUMACELIBS];
extern	ACELIBS otherlib[NUMLIBS];
extern	char 	librarybase[MAXIDSIZE+6];
extern	char 	libraryname[MAXIDSIZE+20];
extern	char	bmapname[MAXIDSIZE+20];
extern	BOOL 	restore_a4;
extern	BOOL 	restore_a5;
extern	BOOL	early_exit;

/* ------------------------- */
/* Library Function routines */
/* ------------------------- */

BYTE check_for_ace_lib(libname)
char *libname;
{
int c=0;
/* 
** If libname is a library used by ACE 
** open & close it in the normal way.
*/ 

 while(acelib[c].name[0] != '\0' && 
       strcmp(acelib[c].name,"SENTINEL") !=0) 
 {
  if (strcmp(acelib[c].name,libname)==0) 
     { enter_XREF(acelib[c].base); return(c); }
  c++;
 } 
 return(NEGATIVE);
}

void enter_new_library(libname)
char *libname;
{
int  cc=0;
/*
** Enter a library into the list 
** for future reference by function
** declarations which don't specify
** the library to be used.
*/

  /* 
  ** Find libname, first free or maximum entry.
  ** If it's already in the list, libname will
  ** be ignored.  
  */
  while (otherlib[cc].name[0] != '\0' && 
     	 strcmp(otherlib[cc].name,libname) != 0 &&
	 strcmp(otherlib[cc].name,"SENTINEL") != 0) cc++;

  /* maximum? */
  if (strcmp(otherlib[cc].name,"SENTINEL") == 0) 
     _error(76);  /* can't add another library */   
  else
  if (otherlib[cc].name[0] == '\0')
  {
	/* add a new entry */
	strcpy(otherlib[cc].name,libname);
	make_library_base(libname);
	strcpy(otherlib[cc].base,librarybase);
  }
}

void make_library_base(libname)
char *libname;	
{
 strcpy(librarybase,"_\0");
 strcat(librarybase,libname);
 strcat(librarybase,"Base");
 enter_BSS(librarybase,"ds.l 1");
}

void make_library_name(libname)
char *libname;
{
 strcpy(libraryname,libname);
 strcat(libraryname,".library");
}

void make_bmap_name(libname)
char *libname;
{
 strcpy(bmapname,"ACEbmaps:");
 strcat(bmapname,libname);
 strcat(bmapname,".bmap");
}

void get_libname(libname,ut_libname)
char *libname,*ut_libname;
{
char *tmp,*ut_tmp;

   /* get raw library name (uppercase and untouched) */
   if (sym == ident) 
   {
      strcpy(ut_libname,ut_id);
      strcpy(libname,id);
   }
   else
   {
      strcpy(ut_libname,stringval);
      strcpy(libname,strupr(stringval));
   }

   /* chop off extension if ".bmap" or ".library" */ 
   tmp=libname;
   ut_tmp=ut_libname;
   while (*tmp && *tmp != '.') { tmp++; ut_tmp++; }
   if (*tmp == '.' && 
       (strcmp(tmp,".BMAP") == 0 || strcmp(tmp,".LIBRARY") == 0))
   {
	*tmp = '\0';
	*ut_tmp = '\0';
   }
}
 
void library()
{
char libname[MAXIDSIZE],ut_libname[MAXIDSIZE];
char lab[80],lablabel[80];

 /* open or close a shared library */

 insymbol();
 if (sym == closesym) closelibrary();
 else
 {
  if (sym != ident && sym != stringconst) 
     _error(7);  /* identifier expected */
  else
  {
   get_libname(libname,ut_libname);  /* without extension! */

   if (check_for_ace_lib(libname) == NEGATIVE)
   {
     make_library_name(ut_libname); /* exec names are case sensitive */
     make_string_const(libraryname);
     gen("move.l","(sp)+","a1");   /* address of library name in a1 */
     gen("jsr","_open_library","  ");
     make_library_base(libname);
     gen("move.l","d0",librarybase);
     gen("cmpi.l","#0","d0");
     make_label(lab,lablabel);
     gen("bne.s",lab,"  ");
     gen("jmp","_EXIT_PROG","  "); /* quit program if can't open library */
     gen(lablabel,"  ","  ");
     enter_XREF("_open_library");

     /* enter new library info' into "other libraries" list */
     enter_new_library(libname);
   }
  }
  insymbol();
 }
}       

void closelibrary()
{
char libname[MAXIDSIZE],ut_libname[MAXIDSIZE];
int  cc;
 /* close one or more shared libraries */ 

 insymbol();
 if (sym != ident && sym != stringconst) 
 {
   /* 
   ** Close all open shared libraries not 
   ** normally closed by ACE at the end of
   ** the program run.
   */
   cc=0;
   while (otherlib[cc].name[0] != '\0' &&
	  strcmp(otherlib[cc].name,"SENTINEL") != 0)
   {
    	gen("move.l",otherlib[cc].base,"a1");  /* library base in a1 */
    	gen("jsr","_close_library","  ");
	gen("move.l","#0",otherlib[cc].base);
        enter_XREF("_close_library");
	cc++;
   }  	
 }   
 else
 {
  /* close a single shared library */
  get_libname(libname,ut_libname);  /* without extension! */

  if (check_for_ace_lib(libname) == NEGATIVE)
  {
    make_library_base(libname);
    gen("move.l",librarybase,"a1");  /* library base in a1 */
    gen("jsr","_close_library","  ");
    gen("move.l","#0",librarybase);
    enter_XREF("_close_library");
  }
  insymbol();
 }
}
  
void remove_qualifier(funcname)
char *funcname;
{
/* remove any trailing data type qualifier (%&#!$) @=& [=! */
int cc=0;

 /* find end of string */
 while (funcname[cc] != '\0') cc++;
 /* is the last character a qualifier? */
 if ((funcname[cc-1] == '%')||(funcname[cc-1] == '@')||(funcname[cc-1] == '#')
    ||(funcname[cc-1] == '[')||(funcname[cc-1] == '$')) funcname[cc-1]='\0';
}

BOOL search_func(bmap,func,declared_func)
char *bmap;
char *func;
SYM  *declared_func;
{
/* 
** Search for function in .bmap file, recording register usage
** and library base offset in symbol table entry for function. 
*/

unsigned char		*buf;
struct FileHandle	*f;
unsigned char		ch,name[MAXIDSIZE];
LONG	 		bmap_size,count,cc,rc;
SHORT			offset=0;
BOOL     		found=FALSE;

unsigned char *tmp;

 if ((bmap_size=fsize(bmap)) == -1L)  /* if not -1 -> file exists and
					 we get size of file in bytes. */
    { _error(50); return(FALSE); }
 else
 {
  /* read whole bmap file into a buffer */
  buf = (unsigned char *)alloc(bmap_size,MEMF_ANY);
  f = (struct FileHandle *)Open(bmap,MODE_OLDFILE);
  Read(f,buf,bmap_size);
  Close(f);

  count=0;  /* start of buffer */

  while (!found && count < bmap_size)
  {
   /* build function name */
   cc=0;
   while (count < bmap_size && cc < MAXIDSIZE)
   {
    ch=name[cc++]=buf[count++];
    if (ch == '\0') break;  /* exit loop when EOS reached */
   }

   name[cc-1]='\0';  /* make sure we have an EOS symbol (may exit early) */

   if (strcmp(name,func) != 0) 
   {
    /* skip 2-byte offset */
    count += 2;
    /* skip to next name */
    while (count < bmap_size && (ch=buf[count++]) != '\0');
   }
   else found=TRUE;  /* that's it! -> get the info' */
  }

  if (!found) return(FALSE);

  /* get library base offset (2 bytes) */
  if (count < bmap_size)
  {
   ch=buf[count++];
   offset += ch*256;
  }
  else 
      return(FALSE);

  if (count < bmap_size)
  {
   ch=buf[count++];
   offset += ch;
  }
  else 
      return(FALSE);

  declared_func->address = (SHORT)offset;  /* record library base offset */

  /* get n bytes of register data */
  declared_func->reg = (UBYTE *)sym_alloc(15,MEMF_ANY);
  if (declared_func->reg == NULL)
  {
	puts("Can't allocate memory for function register info!");
	early_exit=TRUE;
	kill_all_lists();
       	cleanup();
  }
  
  rc=0;
  while (count < bmap_size && 
        (ch=buf[count++]) != '\0') declared_func->reg[rc++]=ch;

  declared_func->no_of_params=rc;  /* record no. of parameters */

  if (ch != '\0') return(FALSE);  /* last character should be NULL */   

  /* we found it -> return */
  return(TRUE);
 }
}

BOOL found_func(libname,ut_funcname,declared_func)
char *libname;
char *ut_funcname;
SYM  *declared_func;
{
     /* 
     ** Search for function in bmap file and record register usage
     ** and library base offset in symbol table entry for function.
     */

     make_bmap_name(libname);

     if (search_func(bmapname,ut_funcname,declared_func))
     {
      /* store library name */
      declared_func->libname = (char *)sym_alloc(MAXIDSIZE+8,MEMF_ANY);
      if (declared_func->libname == NULL)
      {
	puts("Can't allocate memory for library name in symbol table!");
	early_exit=TRUE;
	kill_all_lists();
       	cleanup();
      }
      else
      {
	/* found the function */  
        strcpy(declared_func->libname,libname);
	return(TRUE);
      }
     }  
     else
	 /* didn't find the function */
    	 return(FALSE); 
}

void declare()
{
/* - declare a library function -- NOT optional. 
   - declare an external function -- NOT optional.
   - declare a forward reference to a SUB. 
   - declare an instance of a structure. 
*/

char funcname[MAXIDSIZE],ut_funcname[MAXIDSIZE];
char libname[MAXIDSIZE],ut_libname[MAXIDSIZE];
char extfuncname[MAXIDSIZE+1];
BYTE libnum;
int  oldlevel,cc;
int  functype=undefined;
SYM  *declared_func;
BOOL found;

 /* all functions and structures must be declared at level ZERO */

 oldlevel=lev;
 lev=ZERO;

 insymbol();

 if (sym == subsym) { lev=oldlevel; forward_ref(); return; }

 if (sym == structsym) { lev=oldlevel; declare_structure(); return; }

 if (sym != functionsym) _error(47);
 else
 {
  insymbol();

  /* type identifiers */
  if (sym == shortintsym || sym == longintsym || sym == addresssym ||
      sym == singlesym || sym == stringsym)
  {
   switch(sym)
   {
    case shortintsym : functype = shorttype;  break;
    case longintsym  : functype = longtype;   break;
    case addresssym  : functype = longtype;   break;
    case singlesym   : functype = singletype; break;
    case stringsym   : functype = stringtype; break;
   }
   insymbol();
  }

  if (sym != ident) _error(7);
  else
  {
   /* get the function's name */
   strcpy(funcname,id);
   strcpy(ut_funcname,ut_id);  /* case sensitive version for search! */
   remove_qualifier(funcname);
   remove_qualifier(ut_funcname);
   if (functype == undefined) functype = typ;

   /* get the LIBRARY symbol and/or parameter list */ 
   insymbol();

   /* parse parameter list? */
   if (sym == lparen)
   {    
    do
    {   
        insymbol();

	if (sym == rparen && lastsym == lparen) break;  /* FuncName() */

	/* type indentifier? */
        if (sym == shortintsym || sym == longintsym || sym == addresssym ||
      	    sym == singlesym || sym == stringsym) 
	insymbol();

	/* parameter */
	if (sym != ident) _error(7);
	else
	    insymbol();
    }
    while (sym == comma);

    if (sym != rparen) _error(9);
    else
        insymbol();
   }

   /* EXTERNAL or LIBRARY function? */
   if (sym == externalsym) 
   {
	if (ut_funcname[0] != '_')
	{
	   	strcpy(extfuncname,"_\0");
		strcat(extfuncname,ut_funcname);
	}
	else
		strcpy(extfuncname,ut_funcname);

	enter(extfuncname,functype,extfunc,0);
	enter_XREF(extfuncname);	

	insymbol();
   }
   else
   if (sym != librarysym) _error(48);
   else
   {
    enter(funcname,functype,function,0); /* enter into symbol table */ 
    declared_func=curr_item;

    insymbol();

    if (sym != ident && sym != stringconst)
    {
	/* 
	** Search all bmap files found in acelib and otherlib arrays 
	** for function since no library was specified.
  	*/

	found=FALSE;

	/* acelib array */
	cc=0;
	while (!found && acelib[cc].name[0] != '\0' &&
	       strcmp(acelib[cc].name,"SENTINEL") != 0)
	{
	  found = found_func(acelib[cc].name,ut_funcname,declared_func);
	  cc++;
	}

	if (found) { lev=oldlevel; return; }

	/* otherlib array */
	cc=0;
	while (!found && otherlib[cc].name[0] != '\0' &&
	       strcmp(otherlib[cc].name,"SENTINEL") != 0)
	{
	  found = found_func(otherlib[cc].name,ut_funcname,declared_func);
	  cc++;
	}	

	if (!found) _error(49);
    }
    else
    {
     	/* 
	** Search specified library (bmap file) for the declared function.
	*/
     	get_libname(libname,ut_libname);  /* without extension! */

	if (!found_func(libname,ut_funcname,declared_func)) 
	  _error(49); 

        insymbol();
    }
   }  
  }    
 }
 lev=oldlevel;
}

void load_func_params(func_item)
SYM *func_item;
{
/* 
** Parse a shared library function's actual parameter-list 
** and call the function.
*/
SHORT i,n;
char  ptemp[14][80];

 if (sym != lparen) { _error(14); return; }
 else
 {
  /* get parameters */
  i=0;
  do
  {
   insymbol();

   /*
   ** Make ALL parameters longints! 
   */
   if (expr() == shorttype) make_long();

   /* 
   ** Store parameter information temporarily 
   ** since expression evaluation may corrupt 
   ** data in registers later if loaded now.
   */
   make_temp_long();
   strcpy(ptemp[i],templongname);  /* later -> move.l srctemp,addrbuf */

   /* store it */
   gen("move.l","(sp)+",templongname);
   
   i++;
  }
  while ((i < func_item->no_of_params) && (sym == comma));

  if ((i < func_item->no_of_params) || (sym == comma)) 
     _error(39); /* parameter count mismatch - too few or too many. */
  else
  {
   /* load parameters into regs */
   for (n=0;n<func_item->no_of_params;n++)
   {
    /* does reg (a4 or a5 ONLY) need to be preserved? */
    if (func_item->reg[n] == 13)   /* a4 */ 
    { 
     restore_a4=TRUE; 
     gen("move.l","a4","_a4_temp");
     enter_BSS("_a4_temp:","ds.l 1");
    }
    else
    if (func_item->reg[n] == 14)   /* a5 */
    { 
     restore_a5=TRUE; 
     gen("move.l","a5","_a5_temp");
     enter_BSS("_a5_temp:","ds.l 1");
    }
 
    /*
    ** Store value in register.
    */ 
    gen("move.l",ptemp[n],reg[func_item->reg[n]]);
   }
  }

  if (sym != rparen) _error(9);
 }
}

/* ----------------- */
/* Machine Code CALL */
/* ----------------- */

void load_mc_params(sub_ptr)
SYM *sub_ptr;
{
SHORT i,n;
int   ptype;
char  p_temp[MAXPARAMS][80];
int   p_type[MAXPARAMS];

  /* 
  ** Store parameters in stack space of MC subroutine 
  ** which is about to be CALLed ala C parameter passing 
  ** convention.
  */

  i=0;
  do
  {
   insymbol();
   ptype=expr();

   /* store parameters temporarily */  
   if (ptype == shorttype)
   {
    /* save parameter type */
    p_type[i]=shorttype; /* not DATA type but STORE type (2 or 4 bytes) */

    /* create a temporary store */
    make_temp_short();
    strcpy(p_temp[i],tempshortname);  /* later -> move.w srctemp,-(sp) */

    /* store it */
    gen("move.w","(sp)+",tempshortname);
   }
   else
   /* long,single,string or array */   
   {
    /* save parameter type */
    p_type[i]=longtype;  /* storage requirement is 4 bytes */

    /* create a temporary store */
    make_temp_long();
    strcpy(p_temp[i],templongname);

    /* store it */
    gen("move.l","(sp)+",templongname);
   }
   
   i++;
  }
  while ((i < MAXPARAMS) && (sym == comma));

  /* push parameters onto stack ala C (see AmigaBASIC manual 6-11) */
  for (n=i-1;n>=0;n--)
  {
   if (p_type[n] == shorttype) 
   { 
    gen("move.w",p_temp[n],"-(sp)"); /* short */
    sub_ptr->p_type[n] = shorttype;
   }
   else
   {
    gen("move.l",p_temp[n],"-(sp)"); /* long,string,single,array */
    sub_ptr->p_type[n] = longtype;
   }
  }

  sub_ptr->no_of_params = i; /* record number of parameters pushed! */

  if (sym != rparen) _error(9);
}
