/*
 *	netcread.c - TCE reading stuff
 *	Copyright (C) 2000-2004 Fred Barnes <frmb@kent.ac.uk>
 *
 *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*{{{  includes*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

#include "main.h"
#include "support.h"
#include "structs.h"
#define WANT_TCOFF_TAGNAMES
#include "tcoff.h"
#undef WANT_TCOFF_TAGNAMES
#include "transputer.h"
#include "trancomm.h"


/*}}}*/


/*{{{  private variables/forward-decls */
static char *netcfile_filename;
static char *netcfile_bytes;
static int netcfile_fd;
static size_t netcfile_length, netcfile_ptr;


static void netcread_nextblocks (etc_chain **head, etc_chain **tail);
static etc_chain *new_etc_chain (void);
static char *netcread_nextline (void);
/*}}}*/


/*{{{  static etc_chain *new_etc_chain (void)*/
/*
 *	allocates a new etc_chain structure
 */
static etc_chain *new_etc_chain (void)
{
	etc_chain *tmp;

	tmp = (etc_chain *)smalloc (sizeof (etc_chain));
	tmp->next = NULL;
	tmp->fn = 0;
	tmp->opd = 0;
	tmp->o_bytes = NULL;
	tmp->o_len = 0;
	return tmp;
}
/*}}}*/
/*{{{  etc_chain *read_netc_chain (void)*/
/*
 *	builds the ETC chain
 */
etc_chain *read_netc_chain (void)
{
	etc_chain *head, *tail;
	etc_chain *xhead, *xtail;
	int running;

	head = tail = NULL;
	running = 1;
	while (running) {
		netcread_nextblocks (&xhead, &xtail);
		if (!xhead && !xtail) {
			continue;
		} else if (!xhead && (xtail == (etc_chain *)-1)) {
			running = 0;
		} else {
			if (!head) {
				head = xhead;
				tail = xtail;
			} else {
				tail->next = xhead;
				tail = xtail;
			}
		}
	}
	return head;
}
/*}}}*/
/*{{{  static void add_to_chain (etc_chain **hptr, etc_chain **tptr, etc_chain *item)*/
/*
 *	adds an ETC entry to the specified ETC chain
 */
static void add_to_chain (etc_chain **hptr, etc_chain **tptr, etc_chain *item)
{
	if (!*hptr) {
		*hptr = *tptr = item;
	} else {
		(*tptr)->next = item;
		*tptr = item;
	}
	return;
}
/*}}}*/
/*{{{  static void netcread_nextblocks (etc_chain **head, etc_chain **tail)*/
/*
 *	Returns the next bit of the TCE file
 */
static void netcread_nextblocks (etc_chain **head, etc_chain **tail)
{
	etc_chain *hblk, *tblk, *tmp;
	char *line = NULL;
	char *origline;
	int goodline;
	char **bits;

	/* get next line from input */
	for (goodline = 0; !goodline; ) {
		line = netcread_nextline ();
		if (!line) {
			goodline = 1;
			break;		/* for() */
		}
		/* skip leading whitespace */
		for (; (*line == ' ') || (*line == '\t'); line++);
		if (*line == ';') {
			/* rest of the line is junk */
			continue;
		} else if (*line == '\0') {
			/* blank line */
			continue;
		}
		goodline = 1;
	}

	if (!line) {
		/* end of file */
		*head = NULL;
		*tail = (etc_chain *)-1;
		return;
	}

#if 0
fprintf (stderr, "netcread_nextblocks(): line is: %s\n", line);
#endif

	hblk = tblk = NULL;
	/* duplicate line before we pull it apart */
	origline = line;
	line = string_dup (origline);
	bits = split_stringsc (line, 0);

	if (!bits[0]) {
		fprintf (stderr, "netcread_nextblocks(): bad line: %s\n", origline);
		*head = hblk;
		*tail = tblk;
		return;
	}
	switch (bits[0][0]) {
	case '.':
		if (!strcmp (bits[0], ".setlabel")) {
			/*{{{  set label*/
			int lab;

			if (!bits[1] || (sscanf (bits[1], "%d", &lab) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC6;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = lab;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".byte")) {
			/*{{{  data bytes*/
			int i, blen;

			for (i=1, blen=0; bits[i]; i++) {
				char *ch;

				for (ch = bits[i]; (*ch != '\0'); ch++) {
					if ((ch[0] == '0') && (ch[1] == 'x')) {
						ch++;
						continue;
					}
					if (ch[0] == ',') {
						continue;
					}
					/* expect 2 hex characters */
					ch++;
					blen++;
				}
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCS8;
			tmp->o_len = blen;
			tmp->o_bytes = smalloc (blen);

			for (i=1, blen=0; bits[i]; i++) {
				char *ch;

				for (ch = bits[i]; (*ch != '\0'); ch++) {
					char rch = 0;

					if ((ch[0] == '0') && (ch[1] == 'x')) {
						ch++;
						continue;
					}
					if (ch[0] == ',') {
						continue;
					}
					/* expect 2 hex characters */
					if ((ch[0] >= '0') && (ch[0] <= '9')) {
						rch = ch[0] - '0';
					} else if ((ch[0] >= 'a') && (ch[0] <= 'f')) {
						rch = (ch[0] - 'a') + 10;
					} else if ((ch[0] >= 'A') && (ch[0] <= 'F')) {
						rch = (ch[0] - 'A') + 10;
					}
					rch <<= 4;
					ch++;
					if ((ch[0] >= '0') && (ch[0] <= '9')) {
						rch |= ch[0] - '0';
					} else if ((ch[0] >= 'a') && (ch[0] <= 'f')) {
						rch |= (ch[0] - 'a') + 10;
					} else if ((ch[0] >= 'A') && (ch[0] <= 'F')) {
						rch |= (ch[0] - 'A') + 10;
					}
					tmp->o_bytes[blen] = rch;
					blen++;
				}
			}

			if (!hblk) {
				hblk = tblk = tmp;
			} else {
				tblk->next = tmp;
				tblk = tmp;
			}
			/*}}}*/
		} else if (!strcmp (bits[0], ".jentry")) {
			/*{{{  jump entry (program entry-point)*/
			if (!bits[1]) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCS3;
			tmp->o_len = strlen (bits[1]);
			tmp->o_bytes = string_dup (bits[1]);
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".descriptor")) {
			/*{{{  descriptor line*/
			int v;

			if (!bits[1]) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL6;
			tmp->o_len = strlen (bits[1]) + sizeof (int) + 10;
			tmp->o_bytes = (char *)smalloc (tmp->o_len);
			*(int *)(tmp->o_bytes) = DESCRIPTOR_TAG;
			memcpy (tmp->o_bytes + sizeof (int) + 6, bits[1] + 1, strlen (bits[1]) - 2);
			for (v=0; v<6; v++) {
				tmp->o_bytes[sizeof (int) + v] = 0x00;
			}
			memcpy (tmp->o_bytes + (tmp->o_len - 6), "\x0aSEQ\x0a:", 6);
			add_to_chain (&hblk, &tblk, tmp);
#if 0
fprintf (stderr, "netcread_nextblocks(): DESCRIPTOR [%s]\n", bits[1]);
#endif
			/*}}}*/
		} else if (!strcmp (bits[0], ".setws")) {
			/*{{{  set workspace requirements*/
			int wsb, wsadj;

#if 0
fprintf (stderr, "netcread_nextblocks(): bits[0] = [%s], bits[1] = [%s], bits[2] = [%s]\n", bits[0], bits[1] ?: "(null)", bits[2] ?: "(null)");
#endif
			if (!bits[1] || !bits[2] || (sscanf (bits[1], "%d", &wsb) != 1) || (sscanf (bits[2], "%d", &wsadj) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC11;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = wsb;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_OPR + wsadj;
			tmp->opd = I_NWSADJ;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".setvs")) {
			/*{{{  set vectorspace requirements*/
			int vsb;

			if (!bits[1] || (sscanf (bits[1], "%d", &vsb) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC12;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = vsb;

			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".setms")) {
			/*{{{  set mobilespace requirements*/
			int msb;

			if (!bits[1] || (sscanf (bits[1], "%d", &msb) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL4;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = msb;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".mobileinit")) {
			/*{{{  mobilespace initialisation sequence*/
			int mspoffset, count;

			if (!bits[1] || !bits[2] || (sscanf (bits[1], "%d", &mspoffset) != 1) || (sscanf (bits[2], "%d", &count) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL5;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = mspoffset >> WSH;			/* in words */
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = count;
			add_to_chain (&hblk, &tblk, tmp);

			/* should have "count" .mobileinitpairs following this */
			/*}}}*/
		} else if (!strcmp (bits[0], ".mobileinitpair")) {
			/*{{{  mobilespace pair initialisation*/
			int slotoffs, dataoffs;

			if (!bits[1] || !bits[2] || (sscanf (bits[1], "%d", &slotoffs) != 1) || (sscanf (bits[2], "%d", &dataoffs) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = slotoffs >> WSH;			/* in words */
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = dataoffs >> WSH;			/* in words */
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".setnamedlabel")) {
			/*{{{  set named label*/
			if (!bits[1] || (bits[1][0] != '\"')) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCS2;
			tmp->o_len = strlen (bits[1]) - 2;
			tmp->o_bytes = string_ndup (bits[1] + 1, tmp->o_len);
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".tsdepth")) {
			/*{{{  set transputer stack depth*/
			int tsd;

			if (!bits[1] || (sscanf (bits[1], "%d", &tsd) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC1;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = tsd;

			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".loadlabaddr")) {
			/*{{{  load address of label*/
			int lab;

			if (!bits[1] || (sscanf (bits[1], "%d", &lab) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = lab;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = -1;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".reschedule")) {
			/*{{{  reschedule instruction*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = RESCHEDULE;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".boolinvert")) {
			/*{{{  invert boolean*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = BOOLINVERT;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".notprocess")) {
			/*{{{  load not-process*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = NOTPROCESS;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], ".sourcefile")) {
			/*{{{  source filename*/
			if (!bits[1]) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCS5;
			tmp->o_len = strlen (bits[1]);
			tmp->o_bytes = string_dup (bits[1]);
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".sourceline")) {
			/*{{{  source line-number*/
			int lineno;

			if (!bits[1] || (sscanf (bits[1], "%d", &lineno) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0 + LINENUM;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = lineno;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".align")) {
			/*{{{  alignment (usually in constants)*/
			int align;

			if (!bits[1] || (sscanf (bits[1], "%d", &align) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0 + ALIGN;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = align;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".funcresults")) {
			/*{{{  function results (called after a function to indicate stuff left on the stack)*/
			int nresults;

			if (!bits[1] || (sscanf (bits[1], "%d", &nresults) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0 + FUNCRESULTS;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = nresults;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else if (!strcmp (bits[0], ".funcreturn")) {
			/*{{{  function return (called before the real ret)*/
			int nresults;

			if (!bits[1] || (sscanf (bits[1], "%d", &nresults) != 1)) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETC0 + FUNCRETURN;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = nresults;
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'a':
#if 0
fprintf (stderr, "case 'a': [%s]\n", bits[0]);
#endif
		if (!strcmp (bits[0], "add")) {
			/*{{{  add with overflow check*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_ADD;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "adc")) {
			/*{{{  add constant (with overflow checking)*/
			int cnst;

			if (!bits[1] || (sscanf (bits[1], "%d", &cnst) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_ADC;
			tmp->opd = cnst;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "ajw")) {
			/*{{{  adjust workspace*/
			int adj;

			if (!bits[1] || (sscanf (bits[1], "%d", &adj) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_AJW;
			tmp->opd = adj >> WSH;

			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'c':
		if (!strcmp (bits[0], "call")) {
			/*{{{  call routine*/
			int adj;
			char *fname;

			if (!bits[1] || !bits[2] || (sscanf (bits[2], "%d", &adj) != 1)) {
				goto bad_input_line;
			}
			fname = bits[1];

			tmp = new_etc_chain ();
			tmp->fn = I_OPR + (adj >> WSH);
			tmp->opd = I_NCALL;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			if ((*fname == 'L') && (fname[1] >= '0') && (fname[1] <= '9')) {
				/* numeric label */
				if (sscanf (fname + 1, "%d", &(tmp->opd)) != 1) {
					goto bad_input_line;
				}
			} else {
				tmp->opd = -1;
				tmp->o_len = strlen (fname);
				tmp->o_bytes = string_dup (fname);
			}
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "cj")) {
			/*{{{  conditional jump*/
			if (!bits[1]) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_CJ;
			if (sscanf (bits[1], "%d", &tmp->opd) != 1) {
				goto bad_input_line;
			}
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'd':
		if (!strcmp (bits[0], "diff")) {
			/*{{{  subtract without overflow checking*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_DIFF;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "div")) {
			/*{{{  divide with overflow checking*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_DIV;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'e':
		if (!strcmp (bits[0], "endp")) {
			/*{{{  end process*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_ENDP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'g':
		if (!strcmp (bits[0], "gt")) {
			/*{{{  greater-than*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_GT;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "getpri")) {
			/*{{{  get priority*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_GETPRI;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'j':
		if (!strcmp (bits[0], "j")) {
			/*{{{  outright jump*/
			if (!bits[1]) {
				goto bad_input_line;
			}

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = ETCL0;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_J;
			if (sscanf (bits[1], "%d", &tmp->opd) != 1) {
				goto bad_input_line;
			}
			add_to_chain (&hblk, &tblk, tmp);

			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'l':
		if (!strcmp (bits[0], "ldl")) {
			/*{{{  load local*/
			int wsoff;

			if (!bits[1] || (sscanf (bits[1], "%d", &wsoff) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_LDL;
			tmp->opd = wsoff >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "ldc")) {
			/*{{{  load constant*/
			int cnst;

			if (!bits[1] || (sscanf (bits[1], "%d", &cnst) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_LDC;
			tmp->opd = cnst;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "ldnl")) {
			/*{{{  load non-local*/
			int wsoff;

			if (!bits[1] || (sscanf (bits[1], "%d", &wsoff) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_LDNL;
			tmp->opd = wsoff >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "ldlp")) {
			/*{{{  load local pointer*/
			int wsoff;

			if (!bits[1] || (sscanf (bits[1], "%d", &wsoff) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_LDLP;
			tmp->opd = wsoff >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "ldnlp")) {
			/*{{{  load non-local pointer*/
			int wsoff;

			if (!bits[1] || (sscanf (bits[1], "%d", &wsoff) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_LDNLP;
			tmp->opd = wsoff >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "lt")) {
			/*{{{  less-than*/
			/* don't have this, of course, so reverse and greater-than */
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_REV;
			add_to_chain (&hblk, &tblk, tmp);

			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_GT;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "lb")) {
			/*{{{  load byte*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_LB;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "lw")) {
			/*{{{  load word*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NLW;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'm':
		if (!strcmp (bits[0], "malloc")) {
			/*{{{  allocate dynamic memory*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_MALLOC;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "mrelease")) {
			/*{{{  free dynamic memory*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_MRELEASE;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "mul")) {
			/*{{{  multiply with overflow checking*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_MUL;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'n':
		if (!strcmp (bits[0], "neg")) {
			/*{{{  invert integer*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NNEG;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "not")) {
			/*{{{  bitwise not*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NOT;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "null")) {
			/*{{{  load constant null*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NULL;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'o':
		if (!strcmp (bits[0], "out")) {
			/*{{{  channel output*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_OUT;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'p':
		if (!strcmp (bits[0], "prod")) {
			/*{{{  multiply without overflow checking*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_PROD;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "pop")) {
			/*{{{  pop register from stack*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_POP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 'r':
		if (!strcmp (bits[0], "ret")) {
			/*{{{  return*/
			int adj;

			if (!bits[1] || (sscanf (bits[1], "%d", &adj) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_OPR + (adj >> WSH);
			tmp->opd = I_NRET;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "rev")) {
			/*{{{  reverse top two stack entries*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_REV;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "runp")) {
			/*{{{  run process*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_RUNP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 's':
		if (!strcmp (bits[0], "seterr")) {
			/*{{{  set error flag (halt)*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_SETERR;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "stl")) {
			/*{{{  store local*/
			int wsoff;

			if (!bits[1] || (sscanf (bits[1], "%d", &wsoff) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_STL;
			tmp->opd = wsoff >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "stnl")) {
			/*{{{  store non-local*/
			int off;

			if (!bits[1] || (sscanf (bits[1], "%d", &off) != 1)) {
				goto bad_input_line;
			}
			tmp = new_etc_chain ();
			tmp->fn = I_STNL;
			tmp->opd = off >> WSH;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "startp")) {
			/*{{{  start process*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NSTARTP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "stopp")) {
			/*{{{  stop process*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_STOPP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "sub")) {
			/*{{{  subtract with overflow checking*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_SUB;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "sb")) {
			/*{{{  store byte*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_SB;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "sw")) {
			/*{{{  store word*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_NSW;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "sum")) {
			/*{{{  add without overflow check*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_SUM;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else if (!strcmp (bits[0], "setpri")) {
			/*{{{  set priority*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_SETPRI;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	case 't':
		if (!strcmp (bits[0], "trap")) {
			/*{{{  trap (debugging)*/
			tmp = new_etc_chain ();
			tmp->fn = I_OPR;
			tmp->opd = I_TRAP;
			add_to_chain (&hblk, &tblk, tmp);
			/*}}}*/
		} else {
			goto bad_input_line;
		}
		break;
	default:
		goto bad_input_line;
	}

	*head = hblk;
	*tail = tblk;

	sfree (line);
	return;

bad_input_line:
	fprintf (stderr, "netcread_nextblocks(): badly formed directive: %s\n", origline);
	*head = hblk;
	*tail = tblk;
	sfree (line);
	return;
}
/*}}}*/
/*{{{  static char *netcread_nextline (void)*/
/*
 *	returns a pointer to the next line of input (textual)
 */
static char *netcread_nextline (void)
{
	char *ptr, *ch;

	if (netcfile_ptr >= netcfile_length) {
		return NULL;		/* end of file */
	}
	ptr = netcfile_bytes + netcfile_ptr;
	for (ch=ptr; (netcfile_ptr < netcfile_length) && (*ch != '\r') && (*ch != '\n'); netcfile_ptr++, ch++);
	while ((netcfile_ptr < netcfile_length) && ((*ch == '\r') || (*ch == '\n'))) {
		*ch = '\0';
		ch++;
		netcfile_ptr++;
	}

	return ptr;
}
/*}}}*/
/*{{{  int open_netc_file (char *filename)*/
/*
 *	opens a TCE file
 */
int open_netc_file (char *filename)
{
	struct stat stbuf;
	int err;
	int i, j;

	netcfile_filename = filename;
#ifdef HOST_OS_IS_CYGWIN
	netcfile_fd = open (netcfile_filename, O_RDONLY | O_BINARY);
#else
	netcfile_fd = open (netcfile_filename, O_RDONLY);
#endif
	if (netcfile_fd < 0) {
		fprintf (stderr, "%s: unable to open %s: %s\n", progname, netcfile_filename, strerror (errno));
		return -1;
	}
	err = fstat (netcfile_fd, &stbuf);
	if (err < 0) {
		fprintf (stderr, "%s: unable to stat %s: %s\n", progname, netcfile_filename, strerror (errno));
		close (netcfile_fd);
		return -1;
	}
	netcfile_length = stbuf.st_size;
	netcfile_ptr = 0;
	netcfile_bytes = (char *)smalloc (netcfile_length);
	i = 0;
	while (i < netcfile_length) {
		j = read (netcfile_fd, (netcfile_bytes + i), (netcfile_length - i));
		if (j == 0) {
			fprintf (stderr, "%s: unexpected EOF in %s\n", progname, netcfile_filename);
			close (netcfile_fd);
			return -1;
		} else if (j == -1) {
			fprintf (stderr, "%s: read error while reading from %s: %s\n", progname, netcfile_filename, strerror (errno));
			close (netcfile_fd);
			return -1;
		}
		i += j;
	}
	if (i != netcfile_length) {
		fprintf (stderr, "%s: read too much from %s\n", progname, netcfile_filename);
		close (netcfile_fd);
		return -1;
	}
	return 0;
}
/*}}}*/
/*{{{  void close_netc_file (void)*/
/*
 *	closes the netc file
 */
void close_netc_file (void)
{
	free (netcfile_bytes);
	close (netcfile_fd);
	return;
}
/*}}}*/


