/*
 * gencode.c  -  Code generator
 *
 * Copyright (C) 1997-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: gencode.c,v 1.24 2007/01/06 18:31:27 gkminix Exp $
 */

#define NEED_BINARY 1
#include "mknbi.h"
#include "mgl.h"
#include "gencode.h"
#include "headers/runtime.h"



/*
 *****************************************************************************
 *
 * Global variables
 */
addr_t dataptr = 0;			/* Current data pointer */
addr_t constptr = 0;			/* Current constant pointer */
addr_t codeptr = 0;			/* Current code pointer */
addr_t startadr = 0;			/* Start address */
addr_t stacksize = 0;			/* Runtime stack size */



/*
 *****************************************************************************
 *
 * The following structure is used to hold debugging information
 */
struct debuginfo {
	addr_t addr;
	int    lineno;
};



/*
 *****************************************************************************
 *
 * Structures to hold fixup information
 */
struct fixupinfo {
	fixuptype  type;
	addr_t     addr;
	slabel_t   label;
};



/*
 *****************************************************************************
 *
 * List of symbols which have to be defined by the runtime module. The runtime
 * module can define more symbols than these, but these _have_ to be defined
 * at least. Some of these symbols are not visible to the user, and their names
 * get replaced by an internal symbol name which is not accessible through the
 * lexer.
 */

/* Runtime module symbol names */
static const struct {
	char    *name;			/* symbol name as given in runtime */
	int      id;			/* internal symbol ID number */
	int      visible;		/* flag if symbol is user visible */
} rtsymnames[] = {
	{ "strcmp",		RTCMD_STRCMP,		TRUE	},
	{ "strsub",		RTCMD_STRSUB,		TRUE	},
	{ "strcatcc",		RTCMD_STRCATCC,		FALSE	},
	{ "strcatcs",		RTCMD_STRCATCS,		FALSE	},
	{ "strcatsc",		RTCMD_STRCATSC,		FALSE	},
	{ "strcat",		RTCMD_STRCAT,		TRUE	},
	{ "strset",		RTCMD_STRSET,		TRUE	},
	{ "memcmp",		RTCMD_MEMCMP,		FALSE	},
	{ "putbootp",		RTCMD_PUTBOOTP,		FALSE	},
	{ "getbootp",		RTCMD_GETBOOTP,		FALSE	},
	{ "printint",		RTCMD_PRINTINT,		TRUE	},
	{ "printstring",	RTCMD_PRINTSTR,		TRUE	},
	{ "printchar",		RTCMD_PRINTCHR,		TRUE	},
	{ "getint",		RTCMD_GETINT,		TRUE	},
	{ "getstring",		RTCMD_GETSTR,		TRUE	},
	{ "getchar",		RTCMD_GETCHR,		TRUE	},
	{ "select",		RTCMD_SELECT,		FALSE	},
	{ "str2ip",		RTCMD_STR2IP,		FALSE	},
	{ "ip2str",		RTCMD_IP2STR,		FALSE	},
	{ "load",		RTCMD_LOAD,		TRUE	},
	{ "gotoxy",		RTCMD_GOTOXY,		TRUE	},
	{ "handletry",		RTCMD_HANDLETRY,	FALSE	},
	{ "endexcept",		RTCMD_ENDEXCEPT,	FALSE	},
	{ "raise",		RTCMD_RAISE,		FALSE	},
	{ "chkstk",		RTCMD_CHKSTK,		FALSE	},
	{ "malloc",		RTCMD_MALLOC,		FALSE	},
	{ "free",		RTCMD_FREE,		FALSE	},
	{ "exception",		RTVAR_EXCEPTION,	FALSE	},
	{ "tftpaddr",		RTVAR_TFTP,		TRUE	},
	{ "gatewayaddr",	RTVAR_GATEWAY,		TRUE	},
	{ NULL,			0,			FALSE	}
};


/* List of symbols corresponding to symbol names */
struct sym *rtsymbols[RTSYM_NUM];



/*
 *****************************************************************************
 *
 * Local variables
 */
static __u8 codebuf[CODESIZE];			/* Buffer for code */
static __u8 constbuf[DATASIZE];			/* Buffer for constant data */
static addr_t codestart;			/* Start of user code */
static addr_t conststart;			/* Start of user constant */
static addr_t *relocbuf;			/* Buffer for reloc entries */
static unsigned int relocnum;			/* Current reloc entry */
static unsigned int relocentries;		/* Size of reloc buffer */
static struct debuginfo *debugbuf;		/* Buffer for debugging info */
static unsigned int debugnum;			/* Number of debug entries */
static unsigned int debugentries;		/* Size of debug buffer */
static addr_t *labelbuf;			/* Buffer for labels */
static unsigned int labelnum;			/* Current label number */
static unsigned int labelentries;		/* Size of label buffer */
static struct fixupinfo *fixupbuf;		/* Buffer for fixup entries */
static unsigned int fixupnum;			/* Last fixup entry */
static unsigned int fixupentries;		/* Size of fixup buffer */




/*
 ************************************************************************
 *
 *               Miscellaneous code generation routines
 *
 ************************************************************************
 */

/*
 * Map a compare command code into a suitable code for the runtime
 * module.
 */
__attribute__((const)) int mapcmp __F((cmd), int cmd)
{
  int ret = RTCMP_NONE;

  switch (cmd) {
	case CMD_EQ:
		ret = RTCMP_EQ;
		break;
	case CMD_GT:
		ret = RTCMP_GT;
		break;
	case CMD_GE:
		ret = RTCMP_GE;
		break;
	case CMD_LT:
		ret = RTCMP_LT;
		break;
	case CMD_LE:
		ret = RTCMP_LE;
		break;
	case CMD_NE:
		ret = RTCMP_NE;
		break;
	default:
#ifdef PARANOID
		interror(118, "invalid comparison command");
#endif
		break;
  }
  return(ret);
}




/*
 ************************************************************************
 *
 *               Label handling
 *
 ************************************************************************
 */

/*
 * Define a new label, or redefine an existing label.
 */
slabel_t setlabel __F((labelno, addr), slabel_t labelno AND addr_t addr)
{
  addr_t *tmpbuf;

  /* Create or increase label buffer as necessary */
  if (labelnum >= labelentries) {
	labelentries += LABELSIZE;
	tmpbuf = (addr_t *)nbmalloc(sizeof(addr_t) * labelentries);
	if (labelbuf != NULL) {
		memcpy(tmpbuf, labelbuf, sizeof(addr_t) * labelnum);
		free(labelbuf);
	} else {
		/*
		 * Label 0 doesn't exist, it is needed for special
		 * purposes. Therefore, start out with number 1.
		 */
		labelnum = 1;
	}
	labelbuf = tmpbuf;
  }

  /* If labelno is set, we have to redefine the given label */
  if (labelno != LABEL_NULL) {
	if (addr != LADDR_UNDEF && addr != LADDR_NONE &&
	    labelno < (slabel_t)labelnum &&
	    labelbuf[(unsigned int)labelno] == LADDR_UNDEF) {
		labelbuf[(unsigned int)labelno] =
					(addr == LADDR_CODEPTR ? codeptr : addr);
		return(labelno);
	}
	return(LABEL_NULL);
  }

  /* Otherwise create a new label */
#ifdef PARANOID
  if (addr == LADDR_NONE)
	interror(55, "invalid label address");
#endif
  labelbuf[(unsigned int)labelnum] = (addr == LADDR_CODEPTR ? codeptr : addr);
  return((unsigned int)(labelnum++));
}



/*
 * Determine address of label
 */
addr_t getlabel __F((labelno), slabel_t labelno)
{
  if (labelno == LABEL_NULL || labelno >= labelnum)
	return(LADDR_NONE);
  return(labelbuf[labelno]);
}



/*
 * Set a fixup position
 */
static void setfixup __F((addr, labelno, ftype),
				addr_t addr AND
				slabel_t labelno AND
				fixuptype ftype)
{
  struct fixupinfo *tmpbuf;

  if (fixupnum >= fixupentries) {
	fixupentries += FIXUPSIZE;
	tmpbuf = (struct fixupinfo *)nbmalloc(sizeof(struct fixupinfo) *
								fixupentries);
	if (fixupbuf != NULL) {
		memcpy(tmpbuf, fixupbuf, sizeof(struct fixupinfo) * fixupnum);
		free(fixupbuf);
	}
	fixupbuf = tmpbuf;
  }

  tmpbuf = &fixupbuf[fixupnum++];
  tmpbuf->addr = addr;
  tmpbuf->label = labelno;
  tmpbuf->type = ftype;
}



/*
 * Resolve one fixup entry
 */
static int onefixup __F((fixupno), unsigned int fixupno)
{
  struct fixupinfo *fp;
  addr_t addr, offset;

  /* Just for safety */
  if (fixupno >= fixupnum)
	return(FALSE);

  /* Check for valid fixup entry */
  fp = &fixupbuf[fixupno];
  if (fp->label == LABEL_NULL ||
      fp->label >= labelnum ||
      fp->addr >= codeptr ||
      islabspecial(labelbuf[fp->label]))
	return(FALSE);

  /* Write new address into code segment */
  offset = codebuf[fp->addr] & 0x00ff;
  if (fp->type != FIXUP_REL8)
	offset += ((codebuf[fp->addr + 1]) << 8) & 0xff00;
  if (fp->type == FIXUP_ABS)
	addr = labelbuf[fp->label] + offset;
  else
	addr = labelbuf[fp->label] - (fp->addr + offset);
#ifdef PARANOID
  if (fp->type == FIXUP_REL8 && (addr <= -128 || addr >= 128))
	interror(48, "relative address out of range");
#endif
  codebuf[fp->addr] = (__u8)(addr & 0x00ff);
  if (fp->type != FIXUP_REL8)
	codebuf[fp->addr + 1] = (__u8)((addr >> 8) & 0x00ff);
  return(TRUE);
}



/*
 * Resolve as many fixup entries as possible
 */
void dofixup __F_NOARGS
{
  unsigned int i, j;

  if (fixupbuf == NULL || fixupnum == 0)
	return;

  for (i = 0, j = 0; i < fixupnum; i++)
	if (!onefixup(i)) {
		if (i > j)
			fixupbuf[j] = fixupbuf[i];
		j++;
	}
  fixupnum = j;
}




/*
 ************************************************************************
 *
 *               Basic code generation routines
 *
 ************************************************************************
 */

/*
 * Put one byte into the code segment
 */
void putcode __F((c), byte_t c)
{
  if (codeptr >= CODESIZE) {
	prnerr("code segment too large");
	nbexit(EXIT_MGL_CODESIZE);
  }
  codebuf[codeptr++] = (__u8)(c & 0xff);
}



/*
 * Put one byte into the data segment
 */
void putconst __F((c), byte_t c)
{
  if (constptr >= CONSTSIZE) {
	prnerr("constant data segment too large");
	nbexit(EXIT_MGL_CONSTSIZE);
  }
  constbuf[constptr++] = (__u8)(c & 0xff);
}



/*
 * Put a complex constant or string into a memory block
 */
addr_t putmemblock __F((memblock, size), const byte_t *memblock AND size_t size)
{
  size_t k;
  addr_t i, j, end;

  /* Let's see if we have the constant in the constant buffer already */
  end = constptr - (addr_t)size;
  for (i = conststart; i < end; i++) {
	for (k = 0, j = i; k < size && j < constptr; k++, j++)
		if ((__u8)(memblock[k] & 0xff) != constbuf[j])
			break;
	if (j <= constptr && k == size &&
	    (__u8)(memblock[k - 1] & 0xff) == constbuf[j - 1])
		return(i);
  }

  /* No, it's not in yet, create a new constant in the buffer */
  i = constptr;
  for (k = 0; k < size; k++)
	putconst(memblock[k]);
  return(i);
}



/*
 * Put one integer value into the code segment
 */
void putint __F((val, usereloc), long val AND int usereloc)
{
  if (usereloc)
	setreloc();
  putcode((byte_t)(val & 0x00ff));
  putcode((byte_t)((val >> 8) & 0x00ff));
}



/*
 * Put a label address into the code segment
 */
void putaddr __F((labelno, offset, ftype),
				slabel_t labelno AND
				addr_t offset AND
				fixuptype ftype)
{
  addr_t addr;

  /* First check some error conditions */
#ifdef PARANOID
  if (labelno == LABEL_NULL || labelno >= labelnum)
	interror(46, "invalid label number");
#endif

  addr = labelbuf[(unsigned int)labelno];
#ifdef PARANOID
  if (addr == LADDR_NONE || addr == LADDR_CODEPTR)
	interror(47, "invalid label address");
#endif

  /* Put the address into the code segment */
  if (addr == LADDR_UNDEF) {
	/*
	 * If the label is undefined, we have to create a new fixup. For this,
	 * we put the offset into where the address should go.
	 */
	setfixup(codeptr, labelno, ftype);
	if (ftype == FIXUP_REL8)
		putcode((byte_t)(offset & 0x00ff));
	else
		putint((long)(offset & 0xffff), FALSE);
  } else {
	/*
	 * The label has been defined already, so we can compute the real
	 * address, and put it into the code segment.
	 */
	if (ftype == FIXUP_ABS)
		addr += offset;
	else
		addr = addr - (codeptr + offset);
#ifdef PARANOID
	if (ftype == FIXUP_REL8 && (addr <= -128 || addr >= 128))
		interror(49, "relative address out of range");
#endif
	if (ftype == FIXUP_REL8)
		putcode((byte_t)(addr & 0x00ff));
	else
		putint((long)(addr & 0xffff), FALSE);
  }
}



/*
 * Set a new relocation entry at present code position
 */
void setreloc __F_NOARGS
{
  addr_t *tmpbuf;

  if (relocnum >= relocentries) {
	relocentries += RELOCSIZE;
	tmpbuf = (addr_t *)nbmalloc(sizeof(addr_t) * relocentries);
	if (relocbuf != NULL) {
		memcpy(tmpbuf, relocbuf, sizeof(addr_t) * relocnum);
		free(relocbuf);
	}
	relocbuf = tmpbuf;
  }
  relocbuf[relocnum++] = codeptr;
}



/*
 * Save debug info for current statement
 */
void setdebug __F_NOARGS
{
  struct debuginfo *tmpbuf;

  if (!debug)
	return;

  if (debugnum >= debugentries) {
	debugentries += DEBUGSIZE;
	tmpbuf = (struct debuginfo *)nbmalloc(sizeof(struct debuginfo) *
								debugentries);
	if (debugbuf != NULL) {
		memcpy(tmpbuf, debugbuf, sizeof(struct debuginfo) * debugnum);
		free(debugbuf);
	}
	debugbuf = tmpbuf;
  }

  if (debugnum == 0 ||
      debugbuf[debugnum - 1].lineno != lineno ||
      debugbuf[debugnum - 1].addr != codeptr) {
	debugbuf[debugnum].addr = codeptr;
	debugbuf[debugnum].lineno = lineno;
	debugnum++;
  }
}




/*
 ************************************************************************
 *
 * Code generator initialization and file output
 *
 ************************************************************************
 */

/*
 * Get argument specification for a procedure or function from runtime
 * module.
 */
static struct vardef *getargs __F((ofs, num, rtbuf),
				unsigned int ofs AND
				unsigned int num AND
				__u8 *rtbuf)
{
  struct vardef *args = NULL;
  __u8 *bp = &rtbuf[ofs];
  unsigned int i, argtype;

  if (num > 0) {
	args = (struct vardef *)nbmalloc(sizeof(struct vardef) * num);
	for (i = 0; i < num; i++) {
		/* Determine argument type */
		argtype = (unsigned int)(*bp++ & 0xff);
		switch (argtype) {
			case VALTYPE_ANY:
				args[i].t = &any_type;
				break;
			case VALTYPE_NUM:
				args[i].t = &int_type;
				break;
			case VALTYPE_BOOL:
				args[i].t = &bool_type;
				break;
			case VALTYPE_CHAR:
				args[i].t = &char_type;
				break;
			case VALTYPE_STRING:
				args[i].t = &string_type;
				break;
			case VALTYPE_IPADDR:
				args[i].t = &ipaddr_type;
				break;
			case VALTYPE_POINTER:
				args[i].t = &pointer_type;
				break;
			default:
				args[i].t = &none_type;
				break;
		}

		/* Determine argument class */
		argtype = (unsigned int)(*bp++ & 0xff);
		switch (argtype) {
			case VALCLASS_CONST:
				args[i].class = CLASS_CONST;
				break;
			case VALCLASS_REF:
				args[i].class = CLASS_REF;
				break;
			default:
				args[i].class = CLASS_LOCAL;
				break;
		}
	}
  }
  return(args);
}



/*
 * Get a string constant out of the runtime module
 */
static byte_t *getstrconst __F((rtbuf), const __u8 *rtbuf)
{
  byte_t *retbuf, *cp;
  const __u8 *src;
  size_t len;

  len = 0;
  for (src = rtbuf; *src != 0; src++)
	len++;
  if (len > MAX_STR_LEN)
	len = MAX_STR_LEN;

  retbuf = (byte_t *)nbmalloc((len + 1) * sizeof(byte_t));
  retbuf[0] = (byte_t)len;
  cp = &(retbuf[1]);
  src = rtbuf;
  while (len > 0) {
	*cp++ = (byte_t)(*src++);
	len--;
  }
  return(retbuf);
}



/*
 * Get an IP address constant out of the runtime module
 */
static byte_t *getipconst __F((rtbuf), const __u8 *rtbuf)
{
  byte_t *retbuf;
  int i;

  retbuf = (byte_t *)nbmalloc(IPADDR_SIZE * sizeof(byte_t));
  for (i = 0; i < 4; i++)
	retbuf[i] = (byte_t)(rtbuf[i]);
  return(retbuf);
}



/*
 * Initialize the code generator
 */
void codeinit __F((rtfile), const char *rtfile)
{
  __u8 *rtbuf;
  size_t rtsize;
  struct rtdata *datap;
  unsigned int rtstksize;
  unsigned int rttextofs, rtdataofs, rtexpofs;
  unsigned int rttextlen, rtdatalen, rtbsslen;
  int symnum, errnum, version;
  char *namebuf;

  /* Clear code and data buffers */
  memzero(codebuf, sizeof(codebuf));
  memzero(constbuf, sizeof(constbuf));

  /* Initialize relocation buffer */
  relocbuf = NULL;
  relocnum = 0;
  relocentries = 0;

  /* Initialize debugging buffer */
  debugbuf = NULL;
  debugnum = 0;
  debugentries = 0;

  /* Initialize label buffer */
  labelbuf = NULL;
  labelnum = 0;
  labelentries = 0;

  /* Initialize fixup buffer */
  fixupbuf = NULL;
  fixupnum = 0;
  fixupentries = 0;

  /* Clear symbol index buffer */
  for (symnum = 0; symnum < RTSYM_NUM; symnum++)
	rtsymbols[symnum] = NULL;

  /* Get runtime module */
  if (rtfile == NULL) {
	rtsize = runtime_data_size;
	rtbuf = runtime_data;
  } else {
	int fd;

	rtsize = (size_t)filesize(rtfile);
	if (rtsize == (size_t)(-1))
		nbexit(-1);
	rtbuf = (__u8 *)nbmalloc(rtsize + 1);
	if ((fd = open(rtfile, O_RDONLY|O_BINARY)) < 0) {
		prnerr("unable to open file %s", rtfile);
		nbexit(EXIT_OPEN);
	}
	rtsize = nbread(rtbuf, rtsize, fd);
	close(fd);
  }

  /* Check that the runtime module is valid and read it's header */
  datap = NULL;
  rttextofs = 0;
  rtdataofs = 0;
  rtexpofs = 0;		/* these are just for avoiding compiler warnings */
  rttextlen = 0;
  rtdatalen = 0;
  rtbsslen = 0;
  rtstksize = 0;
  if (rtsize > (sizeof(struct rtdata) + RT_DATAOFS + 1)) {
	unsigned int nameofs;

	datap = (struct rtdata *)&(rtbuf[RT_DATAOFS]);
	rttextofs = 0;
	rtdataofs = (get_word(datap->dataofs) + 15) & 0xfff0;
	rtexpofs  = get_word(datap->debug);
	rttextlen = get_word(datap->textend);
	rtdatalen = get_word(datap->dataend);
	rtbsslen  = get_word(datap->bssend) - rtdatalen;
	rtstksize = get_word(datap->stksize);
	if (rtdataofs < (rttextofs + rttextlen) ||
	    rtdataofs < (rtexpofs + sizeof(struct rtexport)) ||
	    rtsize < (rtdataofs + rtdatalen) ||
	    rtstksize < RTSTK_MIN ||
	    rtstksize > RTSTK_MAX)
		datap = NULL;
	else {
		nameofs = get_word(datap->id);
		namebuf = bytestr(&rtbuf[nameofs], 0);
		if (strcmp(namebuf, RT_IDSTRING))
			datap = NULL;
		free(namebuf);
	}
  }
  if (datap == NULL) {
	if (rtfile != NULL)
		prnerr("invalid runtime module in file %s", rtfile);
	else
		prnerr("invalid runtime module");
	nbexit(EXIT_MGL_RUNTIME);
  }

  /* Copy the code segment from runtime module into buffers */
  assert((rttextofs + rttextlen) <= rtsize);
  memcpy(codebuf, &rtbuf[rttextofs], (size_t)rttextlen);
  codeptr = codestart = (addr_t)rttextlen;

  /* Copy the data segment from runtime module into buffers */
  assert((rtdataofs + rtdatalen) <= rtsize);
  memcpy(constbuf, &rtbuf[rtdataofs], rtdatalen);
  dataptr = constptr = conststart = (addr_t)rtdatalen + (addr_t)rtbsslen;

  /* Set default stack size */
  stacksize = (addr_t)rtstksize;

  /* Scan through the exports table and setup all symbols */
  version = 0;
  while (rtexpofs > 0 && rtexpofs < rtdataofs) {
	unsigned int exptype, expval, expsize;
	struct rtexport *expp;
	struct sym *sp = NULL;
	struct typesdef *tp;

	/* Determine type of current export record */
	expp = (struct rtexport *)&(rtbuf[rtexpofs]);
	exptype = (unsigned int)(expp->exptype & 0xff);
	if (exptype == RTEXP_END) {
		rtexpofs = 0;
		break;
	}

	/* Determine value and type of the symbol */
	expval = get_word(expp->value);
	expsize = get_word(expp->size);
	switch ((int)(expp->valtype & 0xff)) {
		case VALTYPE_ANY:
			tp = &any_type;
			break;
		case VALTYPE_NUM:
			tp = &int_type;
			break;
		case VALTYPE_BOOL:
			tp = &bool_type;
			break;
		case VALTYPE_CHAR:
			tp = &char_type;
			break;
		case VALTYPE_STRING:
			tp = &string_type;
			break;
		case VALTYPE_IPADDR:
			tp = &ipaddr_type;
			break;
		case VALTYPE_POINTER:
			tp = &pointer_type;
			break;
		default:
			tp = NULL;
			break;
	}

	/* Extract the symbol name */
	rtexpofs += sizeof(struct rtexport);
	namebuf = bytestr(&rtbuf[rtexpofs], 0);
	while (rtbuf[rtexpofs] != 0)
		rtexpofs++;
	rtexpofs++;

	/* Check if this is an internal symbol */
	for (symnum = 0; rtsymnames[symnum].name != NULL; symnum++)
		if (!strcmp(rtsymnames[symnum].name, namebuf))
			break;
	if (rtsymnames[symnum].name == NULL)
		symnum = -1;

	/* Determine the name under which to store the symbol */
	if (symnum >= 0 && !rtsymnames[symnum].visible) {
		free(namebuf);
		namebuf = (char *)nbmalloc(8);
		sprintf(namebuf, "$I%05u", rtsymnames[symnum].id);
	}

	/* The version info record has to come first */
	if (version == 0 && exptype != RTEXP_VERSION) {
		prnerr("missing version info in runtime module");
		nbexit(EXIT_MGL_RUNTIME);
	}

	/* Create or change the symbol */
	switch (exptype) {
		case RTEXP_VERSION:
			if (tp->type != EXPR_NUM || expval == 0 ||
			    expsize != 0 || version != 0) {
				prnerr("invalid exports table in "
				       "runtime module");
				nbexit(EXIT_MGL_RUNTIME);
			}
			version = (int)expval;
			break;

		case RTEXP_PROC:
			sp = addsym(namebuf, SYMBOL_RTREF);
			sp->type = funcsym;
			sp->loc.label = setlabel(LABEL_NULL, (addr_t)expval);
			sp->def.f.retaddr = 0;
			sp->def.f.varsize = 0;
			sp->def.f.opcode = CMD_RTFUNC;
			sp->def.f.argnum = expsize;
			sp->def.f.ret = tp;
			sp->def.f.args = getargs(rtexpofs, expsize, rtbuf);
			rtexpofs += expsize * 2;
			break;

		case RTEXP_VAR:
		case RTEXP_VARCONST:
			if (tp->type == EXPR_STRING && expsize > 0)
				tp = newstrtype(expsize);
			sp = addsym(namebuf, SYMBOL_RTREF);
			sp->type = varsym;
			sp->loc.addr = (addr_t)expval;
			sp->def.v.class = (exptype == RTEXP_VAR ?
							CLASS_STATIC :
							CLASS_STATICRO);
			sp->def.v.t = tp;
			break;

		case RTEXP_CONST:
			sp = addsym(namebuf, SYMBOL_RTREF);
			sp->type = constsym;
			sp->def.c.t = tp;
			switch (tp->type) {
				case EXPR_NUM:
					sp->def.c.val.i = (int)expval;
					break;
				case EXPR_CHAR:
					sp->def.c.val.c = (byte_t)(expval & 0xff);
					break;
				case EXPR_STRING:
					sp->def.c.val.s =
						getstrconst(&rtbuf[rtexpofs]);
					rtexpofs += sp->def.c.val.s[0] + 1;
					break;
				case EXPR_IPADDR:
					sp->def.c.val.a =
						getipconst(&rtbuf[rtexpofs]);
					rtexpofs += 4;
				default:
					break;
			}
			break;

		default:
			break;
	}
	free(namebuf);

	/* Setup the symbol within the runtime symbol table */
	if (symnum >= 0)
		rtsymbols[rtsymnames[symnum].id] = sp;
  }

  /* Delete runtime module, it's no longer needed */
  if (rtbuf != runtime_data)
	free(rtbuf);

  /* Check if exports table has been terminated correctly */
  if (rtexpofs > 0) {
	prnerr("unexpected end of exports table in runtime module");
	nbexit(EXIT_MGL_RUNTIME);
  }

  /* Check interface version of runtime module exports table */
  if (version == 0 || version != RTIF_VERSION) {
	prnerr("invalid version of exports table in runtime module");
	nbexit(EXIT_MGL_RUNTIME);
  }

  /*
   * Check that all those runtime symbols have been defined, which are
   * required by the compiler. Of course, the runtime module can define
   * even more symbols, which can then be used by the user program.
   */
  errnum = 0;
  for (symnum = 0; rtsymnames[symnum].name != NULL; symnum++)
	if (rtsymbols[symnum] == NULL) {
		prnerr("unknown symbol '%s'", rtsymnames[symnum].name);
		errnum++;
	}
  if (errnum > 0) {
	prnerr("could not find %d symbols in runtime module", errnum);
	nbexit(EXIT_MGL_RUNTIME);
  }
}



/*
 * Write the compiled code into an output file
 */
void codewrite __F((outfile), int outfile)
{
  struct rtdata *datap = (struct rtdata *)&(codebuf[RT_DATAOFS]);
  addr_t codesize;
  addr_t relocval;
  addr_t debugptr;
  unsigned int rtdatalen, j;
  unsigned long loadsize;

  /* Update all fixup information */
  if (fixupbuf != NULL) {
	dofixup();
	if (fixupnum > 0)
		error("unresolved fixup entries", FALSE);
	free(fixupbuf);
  }

  /* Write the debugging info into the constant segment */
  debugptr = 0;
  if (debug && debugbuf != NULL) {
	debugptr = constptr;
	for (j = 0; j < debugnum; j++) {
		putconst((byte_t)(debugbuf[j].addr & 0x00ff));
		putconst((byte_t)((debugbuf[j].addr >> 8) & 0x00ff));
		putconst((byte_t)(debugbuf[j].lineno & 0x00ff));
		putconst((byte_t)((debugbuf[j].lineno >> 8) & 0x00ff));
	}
	putconst(0);	/* end marker */
	putconst(0);
	free(debugbuf);
  }

  /*
   * Even-align the end of the constant and variable data sections, and
   * scan through the relocation table and add the value at the relocation
   * to the size of the constant segment
   */
  constptr = (constptr + 1) & 0xfffe;
  dataptr = (dataptr - conststart + 1) & 0xfffe;
  if (relocbuf != NULL) {
	for (j = 0; j < relocnum; j++) {
		relocval = codebuf[relocbuf[j] + 0] & 0x00ff;
		relocval += ((codebuf[relocbuf[j] + 1]) << 8) & 0xff00;
		if (relocval >= conststart) {
			/*
			 * If the address at the relocation address is lower
			 * than conststart, we are dealing with an address
			 * for a runtime variable, which should not get
			 * relocated.
			 */
			relocval += constptr - conststart;
			codebuf[relocbuf[j] + 0] =
					(__u8)(relocval & 0x00ff);
			codebuf[relocbuf[j] + 1] =
					(__u8)((relocval >> 8) & 0x00ff);
		}
	}
	free(relocbuf);
  }

  /* Write the start address into the code module */
  if (startadr == 0) {
	prnerr("no start address defined");
	nbexit(EXIT_MGL_NOSTART);
  }
  assign(datap->start, htot(startadr & 0xffff));

  /* Write the stack size into the code module */
  assign(datap->stksize, htot(stacksize & 0xffff));

  /* Align the data segment to a segment boundary and set the segment sizes */
  codesize = (codeptr + 15) & 0xfff0;
  assign(datap->constend, htot(constptr & 0xffff));
  assign(datap->varend, htot((constptr + dataptr) & 0xffff));
  assign(datap->debug, htot(debugptr & 0xffff));
  assign(datap->dataofs, htot(codesize & 0xffff));

  /* Determine the total size of the output file */
  rtdatalen = get_word(datap->dataend);
  loadsize = (unsigned int)codesize +
		(unsigned int)rtdatalen +
		(unsigned int)constptr;
  assign(datap->loadsize.low, htot(low_word(loadsize & 0xffff)));
  assign(datap->loadsize.high, htot(high_word(loadsize & 0xffff)));

  /* Write all buffers into output file */
  (void)nbwrite(&codebuf[0], (unsigned int)codesize, outfile);
  (void)nbwrite(&constbuf[0], (unsigned int)rtdatalen, outfile);
  (void)nbwrite(&constbuf[conststart], (unsigned int)constptr, outfile);
  if (lseek(outfile, 0L, 0) < 0) {
	prnerr("unable to seek in temporary file");
	nbexit(EXIT_SEEK);
  }

  /* If requested, print code sizes to stdout. This is for testing only */
  if (printsizes)
	printf("%lu %lu\n", (unsigned long)codestart, (unsigned long)codeptr);
}

