/*
 *	TXX 2.0 -- An interpreter for T3X output (Tcode).
 *	Copyright (C) 1997,1998,1999 Nils M Holm
 *	See the file txtrn.t for details.
 *	See the file LICENSE for conditions of use.
 */

#ifdef plan9
#include <u.h>
#include <libc.h>
void exit(r) {
	if (!r) exits(0);
	exits("2");
}
#endif
#ifdef msdos
 #ifdef __TURBOC__
  #include <alloc.h>
  #include <io.h>
 #endif
 #define open	_open
 #define close	_close
 #define read	_read
#endif
#include <stdio.h>

#include "txx_ext.h"

#define ICLAB	129
#define IDLAB	130
#define IDECL	131
#define IDATA	132
#define ICREF	133
#define IDREF	134
#define ISTR	135
#define IPSTR	136
#define IINIT	137
#define IHDR	10
#define IEND	11
#define INEG	12
#define ILNOT	13
#define IBNOT	14
#define IPOP	15
#define ICLEAN	144
#define INOP	17
#define IINDB	18
#define IIND	19
#define IDREFB	20
#define IDEREF	21
#define IINCG	150
#define IINCL	151
#define IMUL	24
#define IDIV	25
#define IMOD	26
#define IADD	27
#define ISUB	28
#define IBAND	29
#define IBOR	30
#define IBXOR	31
#define IBSHL	32
#define IBSHR	33
#define IEQU	34
#define INEQU	35
#define ILESS	36
#define IGRTR	37
#define ILTEQ	38
#define IGTEQ	39
#define ILDG	168
#define ILDGV	169
#define ILDL	170
#define ILDLV	171
#define ILDLAB	172
#define INUM	173
#define ISAVG	174
#define ISAVL	175
#define ISTORE	48
#define ISTORB	49
#define ISTACK	178
#define ICALL	179
#define ICALR	52
#define IEXEC	181
#define IBRF	182
#define IBRT	183
#define INBRF	184
#define INBRT	185
#define IJUMP	186
#define IUNEXT	187
#define IDNEXT	188
#define IHALT	61
#define IPUB	190
#define IEXT	191
#define IUMUL	64
#define IUDIV	65
#define IULESS	66
#define IUGRTR	67
#define IULTEQ	68
#define IUGTEQ	69
#define IDUP	70
#define ISWAP	71
#define ILINE	200
#define IGSYM	201
#define ILSYM	202
#define IENDOFSET	75


void	ifail();

void	ihdr(), iend(), ineg(), ilnot(), ibnot(), ipop(),
	iclean(), inop(), iindb(), iind(), idrefb(), ideref(), iincg(),
	iincl(), imul(), idiv(), imod(), iadd(), isub(), iband(),
	ibor(), ibxor(), ibshl(), ibshr(), iequ(), inequ(), iless(),
	igrtr(), ilteq(), igteq(), ildg(), ildgv(), ildl(), ildlv(),
	ildlab(), inum(), isavg(), isavl(), istore(), istorb(),
	istack(), icall(), icalr(), iexec(), ibrf(), ibrt(), inbrf(),
	inbrt(), ijump(), iunext(), idnext(), ihalt(), iumul(),
	iudiv(), iuless(), iugrtr(), iulteq(), iugteq(), idup(),
	iswap(), iline();

void	(*Builtins[])() = {
	ifail, ifail, ifail, ifail, ifail, ifail, ifail, ifail, ifail,
	ifail, ihdr, iend, ineg, ilnot, ibnot, ipop,
	iclean, inop, iindb, iind, idrefb, ideref, iincg,
	iincl, imul, idiv, imod, iadd, isub, iband,
	ibor, ibxor, ibshl, ibshr, iequ, inequ, iless,
	igrtr, ilteq, igteq, ildg, ildgv, ildl, ildlv,
	ildlab, inum, isavg, isavl, istore, istorb,
	istack, icall, icalr, iexec, ibrf, ibrt, inbrf,
	inbrt, ijump, iunext, idnext, ihalt, ifail, ifail,
	iumul, iudiv, iuless, iugrtr, iulteq, iugteq,
	idup, iswap, iline, ifail, ifail
};

#ifdef msdos
#define TEXTSIZE	64000U
#define DATASIZE	TEXTSIZE
#else
#ifdef SMALL
#define TEXTSIZE	20480
#define DATASIZE	28672
#else
#define TEXTSIZE	65536L
#define DATASIZE	TEXTSIZE
#endif
#endif
#ifdef SMALL
#define NLABELS		1024
#define NMARKS		512
#else
#define NLABELS		2048
#define NMARKS		2048
#endif

#define uchar	unsigned char
#define uint	unsigned int
#define ushort	unsigned short


#define CELL	short
#define UCELL	ushort


struct mark {
	int	l, t, d;
};

uchar		*Text, *Data;
CELL		*Wdata, *Stack;
UCELL		*Uwdata, *Ustack;
UCELL		Etext, Edata;
int		Tags[NLABELS];
struct mark	Marks[NMARKS];
uint		Mt;
uint		Dptr, Tptr;
CELL		A1, A2, Acc;
UCELL		U1;
UCELL		Ip, Sp, Fp;
int		Execmax;
int		end_of_set;


#define E_BASIC		0x0001
#define E_VIO		0x0002
#define E_GRAPHICS	0x0004


#ifdef __TURBOC__
void fail(char *m, char *n);
#endif
void fail(m, n)
char	*m, *n;
{
	if (n)
		fprintf(stderr, "TXX core fault: %s: %s\n", m, n);
	else
		fprintf(stderr, "TXX core fault: %s\n", m);
	fprintf(stderr, "IP=%u, SP=%u, FP=%u, RR=%d\n"
			"A1=%d (%u), A2=%d\n"
			"Slot#=%d, Etext=%u, Edata=%u\n",
		Ip, Sp<<1, Fp<<1, Acc, A1, U1, A2, Execmax, Etext, Edata);
	exit(2);
}


void init() {
	int	i;

	Text = (uchar *) malloc(TEXTSIZE);
	Data = (uchar *) malloc(DATASIZE);
	if (!Text || !Data) fail("insufficient memory", 0);
	Wdata = (CELL *) Data;
	Stack = Wdata;
	Uwdata = (UCELL *) Data;
	Ustack = Uwdata;
	Sp = Fp = DATASIZE/2;
	Etext = 0;
	Edata = 0;
	for (i=0; i<NLABELS; i++) Tags[i] = -1;
	Mt = 0;
	Tptr = 0;
	Dptr = 0;
	Execmax = sizeof(Primitives) / sizeof(Primitives[0]);
	end_of_set = IENDOFSET;
	libinit();
}


void addlabel(op, id) {
	uint	a, i, o;
	uint	here;

	if (id >= NLABELS) fail("label too big", 0);
	if (Tags[id] != -1) fail("duplicate label", 0);
	Tags[id] = op == ICLAB? Tptr: Dptr;
	here = op == IDLAB? Dptr: Tptr;
	for (i=0; i<Mt; i++) if (Marks[i].l == id) break;
	if (i >= Mt) return;
	a = Marks[i].t;
	while (a) {
		o = a;
		a = (Text[a+1] << 8) | Text[a];
		Text[o] = here & 0xff;
		Text[o+1] = here >> 8;
	}
	a = Marks[i].d;
	while (a) {
		o = a;
		a = (Data[a+1] << 8) | Data[a];
		Data[o] = here & 0xff;
		Data[o+1] = here >> 8;
	}
}


int addmark(id, dref) {
	int	i, old;

	for (i=0; i<Mt; i++) {
		if (Marks[i].l == id) {
			if (dref) {
				old = Marks[i].d;
				Marks[i].d = Dptr;
			}
			else {
				old = Marks[i].t;
				Marks[i].t = Tptr;
			}
			return(old);
		}
	}
	if (Mt >= NMARKS) fail("too many marks", 0);
	Marks[Mt].l = id;
	if (dref) {
		Marks[Mt].d = Dptr;
		Marks[Mt].t = 0;
	}
	else {
		Marks[Mt].t = Tptr;
		Marks[Mt].d = 0;
	}
	Mt++;
	return(0);
}


int findlab(id, dref) {
	if (Tags[id] != -1) return(Tags[id]);
	return(addmark(id, dref));
}


config(i) {
	int	ev;
	char	*req = "extension not present";

	ev = (Text[i+1] << 8) | Text[i];
#ifndef BASIC_EXT
	if (ev & E_BASIC) fail(req, "BASIC");
#endif
#ifndef VIO_EXT
	if (ev & E_VIO) fail(req, "VIO");
#endif
#ifndef GRAPHICS_EXT
	if (ev & E_GRAPHICS) fail(req, "GRAPHICS");
#endif
	if (ev & ~(E_BASIC|E_VIO|E_GRAPHICS))
		fail(req, "UNKNOWN EXTENSION");
	return(i+2);
}


void resolve() {
	UCELL	i, j, op, arg, arg2, a2f;

	if (Text[0] != IINIT)
		fail("bad input program (magic match failed)", 0);
	for (i=0; i<Etext; ) {
		op = Text[i++];
		if (op & 128) {
			arg = (Text[i+1] << 8) | Text[i];
			i += 2;
		}
		a2f = 0;
		if (op==IINCG || op==IINCL) {
			arg2 = (Text[i+1] << 8) | Text[i];
			i += 2;
			a2f = 1;
		}
		switch (op) {
		case ICLAB:
		case IDLAB:
			addlabel(op, arg);
			op = 0;
			break;

		case IDECL:
			Dptr += arg? (arg<<1): 2;
			if (Dptr >= DATASIZE)
				fail("data area too big", 0);
			op = 0;
			break;

		case IDATA:
			Wdata[Dptr>>1] = arg;
			Dptr += 2;
			if (Dptr >= DATASIZE)
				fail("data area too big", 0);
			op = 0;
			break;

		case ICREF:
		case IDREF:
			Wdata[Dptr>>1] = findlab(arg, 1);
			Dptr += 2;
			if (Dptr >= DATASIZE)
				fail("data area too big", 0);
			op = 0;
			break;

		case IPSTR:
			memcpy(&Data[Dptr], &Text[i], arg);
			Dptr += arg;
			do { Data[Dptr++] = 0; } while (Dptr & 1);
			i += arg;
			op = 0;
			break;

		case ISTR:
			for (j=0; j<arg; j++) {
				Data[Dptr++] = Text[i++];
				Data[Dptr++] = 0;
			}
			do { Data[Dptr++] = 0; } while (Dptr & 1);
			op = 0;
			break;

		case IINCG: case ILDG: case ILDGV: case ISAVG:
		case ILDLAB: case ICALL: case IBRF: case IBRT:
		case INBRF: case INBRT: case IJUMP: case IUNEXT:
		case IDNEXT:
			Text[Tptr++] = op;
			arg = findlab(arg, 0);
			break;

		case IPUB:
			i += arg;
			op = 0;
			break;

		case ILSYM:
		case IGSYM:
			i += (arg+2);
			op = 0;
			break;

		case IEXT:
			Text[i+arg] = 0;
			fail("external reference", (char *) &Text[i]);
			break;

		case IINIT:
			if (arg == 1)
				;
			else if (arg == 2)
				i = config(i);
			else
				fail("unsupported tcode version", NULL);
			op = 0;
			break;

		default:
			Text[Tptr++] = op;
		}
		if (op & 128) {
			Text[Tptr++] = arg & 0xff;
			Text[Tptr++] = arg >> 8;
		}
		if (a2f) {
			Text[Tptr++] = arg2 & 0xff;
			Text[Tptr++] = arg2 >> 8;
		}
	}
	Etext = Tptr;
	Edata = Dptr;
}


void load(name)
char	*name;
{
	int	fd, i;
	uint	r;

	if ((fd = open(name, 0)) < 0) fail("cannot open program", name);
	r = read(fd, Text, TEXTSIZE);
	if (r < 1) fail("I/O error reading program", 0);
	if (r >= TEXTSIZE) fail("program too big", 0);
	close(fd);
	Etext = r;
	if (Text[0] == '#' && Text[1] == '!') {
		for (i=0; Text[i] != '\n' && i<Etext; i++)
			;
		Text = &Text[i+1];
		Etext -= (i+1);
	}
	resolve();
}


void primitive(id) {
	if (id < 0 || id >= Execmax-1 || !Primitives[id])
		fail("undefined primitive", 0);
	(*Primitives[id])();
}


void divz() { fail("divide by zero", 0); }

void ifail() { fail("illegal opcode", 0); }
void ihdr() { Stack[--Sp] = Fp; Fp = Sp; }
void iend() { Fp = Stack[Sp++]; Ip = Stack[Sp++]; }
void ineg() { Stack[Sp] = -Stack[Sp]; }
void ilnot() { Stack[Sp] = Stack[Sp]? 0: -1; }
void ibnot() { Stack[Sp] = ~Stack[Sp]; }
void ipop() { Acc = Stack[Sp++]; }
void iclean() { Sp += A1; Stack[--Sp] = Acc; }
void inop() { }
void iindb() { Stack[Sp] = Data[Ustack[Sp]]; }
void iind() { Stack[Sp] = Wdata[Ustack[Sp]>>1]; }
void idrefb() { Stack[Sp+1] += Stack[Sp]; Sp++; }
void ideref() { Stack[Sp+1] += (Stack[Sp]<<1); Sp++; }
void iincg() { A2 = (Text[Ip+1] << 8) | Text[Ip]; Ip += 2;
	Wdata[U1>>1] += A2; }
void iincl() { A2 = (Text[Ip+1] << 8) | Text[Ip]; Ip += 2;
	Stack[Fp-A1] += A2; }
void imul() { Stack[Sp+1] *= Stack[Sp]; Sp++; }
void idiv() { if (!Stack[Sp]) divz();
	Stack[Sp+1] /= Stack[Sp]; Sp++; }
void imod() { if (!Stack[Sp]) divz();
	Ustack[Sp+1] %= Ustack[Sp]; Sp++; }
void iadd() { Stack[Sp+1] += Stack[Sp]; Sp++; }
void isub() { Stack[Sp+1] -= Stack[Sp]; Sp++; }
void iband() { Stack[Sp+1] &= Stack[Sp]; Sp++; }
void ibor() { Stack[Sp+1] |= Stack[Sp]; Sp++; }
void ibxor() { Stack[Sp+1] ^= Stack[Sp]; Sp++; }
void ibshl() { Stack[Sp+1] <<= Stack[Sp]; Sp++; }
void ibshr() { Ustack[Sp+1] >>= Ustack[Sp]; Sp++; }
void iequ() { Stack[Sp+1] = Stack[Sp+1] == Stack[Sp]? -1: 0; Sp++; }
void inequ() { Stack[Sp+1] = Stack[Sp+1] != Stack[Sp]? -1: 0; Sp++; }
void iless() { Stack[Sp+1] = Stack[Sp+1] < Stack[Sp]? -1: 0; Sp++; }
void igrtr() { Stack[Sp+1] = Stack[Sp+1] > Stack[Sp]? -1: 0; Sp++; }
void ilteq() { Stack[Sp+1] = Stack[Sp+1] <= Stack[Sp]? -1: 0; Sp++; }
void igteq() { Stack[Sp+1] = Stack[Sp+1] >= Stack[Sp]? -1: 0; Sp++; }
void ildg() { Stack[--Sp] = Wdata[U1>>1]; }
void ildgv() { Stack[--Sp] = U1; }
void ildl() { Stack[--Sp] = Stack[Fp-A1]; }
void ildlv() { Stack[--Sp] = (Fp-A1)<<1; }
void ildlab() { Stack[--Sp] = U1; }
void inum() { Stack[--Sp] = A1; }
void isavg() { Wdata[U1>>1] = Stack[Sp++]; }
void isavl() { Stack[Fp-A1] = Stack[Sp++]; }
void istore() { Wdata[Ustack[Sp+1]>>1] = Stack[Sp]; Sp+=2; }
void istorb() { Data[Ustack[Sp+1]] = Ustack[Sp]; Sp+=2; }
void istack() { Sp -= A1; }
void icall() { Stack[--Sp] = Ip; Ip = U1; }
void icalr() { UCELL t; t = Stack[Sp]; Stack[Sp] = Ip; Ip = t; }
void iexec() { primitive(A1); }
void ibrf() { if (!Stack[Sp++]) Ip = U1; }
void ibrt() { if (Stack[Sp++]) Ip = U1; }
void inbrf() { if (!Stack[Sp]) Ip = U1; }
void inbrt() { if (Stack[Sp]) Ip = U1; }
void ijump() { Ip = U1; }
void iunext() { if (Stack[Sp+1] >= Stack[Sp]) Ip = U1; Sp+=2; }
void idnext() { if (Stack[Sp+1] <= Stack[Sp]) Ip = U1; Sp+=2; }
void ihalt() { exit(0); }
void iumul() { Ustack[Sp+1] *= Ustack[Sp]; Sp++; }
void iudiv() { if (!Stack[Sp]) divz();
	Ustack[Sp+1] /= Ustack[Sp]; Sp++; }
void iuless() { Ustack[Sp+1] = Ustack[Sp+1] < Ustack[Sp]? -1: 0; Sp++; }
void iugrtr() { Ustack[Sp+1] = Ustack[Sp+1] > Ustack[Sp]? -1: 0; Sp++; }
void iulteq() { Ustack[Sp+1] = Ustack[Sp+1] <= Ustack[Sp]? -1: 0; Sp++; }
void iugteq() { Ustack[Sp+1] = Ustack[Sp+1] >= Ustack[Sp]? -1: 0; Sp++; }
void idup() { Stack[Sp-1] = Stack[Sp]; Sp--; }
void iswap() { UCELL t;
	t = Stack[Sp+1]; Stack[Sp+1] = Stack[Sp]; Stack[Sp] = t; }
void iline() {}


run() {
	uint	op;

	for (Ip = 0;;) {
		op = Text[Ip++];
		if (op & 128) {
			A1 = (Text[Ip+1] << 8) | Text[Ip];
			Ip += 2;
		}
		op &= ~128;
		if (op >= end_of_set) ifail();
		U1 = A1;
		(*Builtins[op])();
	}
}


void version() {
	printf("TXX 2.0 -- Copyright (C) 1997,1998,1999 Nils M Holm\n");
	printf("Tcode runtime engine V1.2, Extensions = ( ");
#ifdef BASIC_EXT
	printf("BASIC ");
#endif
#ifdef VIO_EXT
	printf("VIO ");
#endif
#ifdef GRAPHICS_EXT
	printf("GRAPHICS ");
#endif
	printf(")\n");
	exit(0);
}


main(argc, argv)
int	argc;
char	**argv;
{
	init();
	if (argc < 2) version();
	if (argc > 2) fail("too many programs", 0);
	load(argv[1]);
	run();
	return(0);
}

