//
//  EXPStackMachine.m
//  StackMachine
//
//  Created by Ashley on 23/04/2007.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#ifdef GNUSTEP
#include <math.h>
#endif
#import "EXPVirtualMachine.h"
//#import "EXPTableElement.h"
//#include "interp.h"
#import "EXPSpline.h"

//#define STEPWISE 0 
//#define LINEAR 1
//#define SPLINE 2

#define GLOBALADDR

NSDictionary *mnemonicTable;
NSMutableDictionary *opcodeTable;
NSArray *modeTable;
NSArray *interpNameTable;

@implementation EXPVirtualMachine

+ (id) interpNames
{
	if (interpNameTable==nil) {
		interpNameTable = [NSArray arrayWithObjects:@"stepwise", @"linear", @"spline", nil];
	}
	
	return interpNameTable;
}

- (id) initWithMemory:(unsigned int)newMemorySize //store:(unsigned int)newStoreSize
{
	self = [super init];
	
	if (self==nil) {
		return nil;
	}
	
	_filenames = [[NSMutableArray alloc] init];
	_filetypes = [[NSMutableArray alloc] init];
	nextFile = 0;
	files = (FILE **)calloc(MAXFILES, sizeof(FILE *));

	if (mnemonicTable==nil) {
		mnemonicTable = [[NSDictionary alloc] initWithObjectsAndKeys:
			[NSNumber numberWithInt:PSH], @"PSH", 
			[NSNumber numberWithInt:PUL], @"PUL", 
			[NSNumber numberWithInt:LDA], @"LDA", 
			[NSNumber numberWithInt:STA], @"STA", 
			[NSNumber numberWithInt:ADD], @"ADD", 
			[NSNumber numberWithInt:SUB], @"SUB", 
			[NSNumber numberWithInt:MLT], @"MLT", 
			[NSNumber numberWithInt:DVD], @"DVD", 
			[NSNumber numberWithInt:PWR], @"PWR", 
			[NSNumber numberWithInt:GTH], @"GTH", 
			[NSNumber numberWithInt:GTE], @"GTE", 
			[NSNumber numberWithInt:LTH], @"LTH", 
			[NSNumber numberWithInt:LTE], @"LTE", 
			[NSNumber numberWithInt:EQL], @"EQL", 
			[NSNumber numberWithInt:NEQ], @"NEQ", 
			[NSNumber numberWithInt:INC], @"INC", 
			[NSNumber numberWithInt:DEC], @"DEC", 
			[NSNumber numberWithInt:ADJ], @"ADJ", 
			[NSNumber numberWithInt:CLR], @"CLR", 
			[NSNumber numberWithInt:DBNC], @"DBNC", 
			[NSNumber numberWithInt:JMP], @"JMP",
			[NSNumber numberWithInt:JSR], @"JSR",
			[NSNumber numberWithInt:LEA], @"LEA",
			[NSNumber numberWithInt:LEAS], @"LEAS",
			[NSNumber numberWithInt:LEAL], @"LEAL",
			[NSNumber numberWithInt:LEAG], @"LEAG",
			[NSNumber numberWithInt:PEA], @"PEA",
			[NSNumber numberWithInt:INTERP], @"INTERP", 

			[NSNumber numberWithInt:ABSA], @"ABSA", 
			[NSNumber numberWithInt:NEGA], @"NEGA", 
			[NSNumber numberWithInt:SQRTA], @"SQRTA", 
			[NSNumber numberWithInt:INTA], @"INTA", 
			[NSNumber numberWithInt:EXPA], @"EXPA", 
			[NSNumber numberWithInt:LNA], @"LNA", 
			[NSNumber numberWithInt:LOG10A], @"LOG10A", 
			[NSNumber numberWithInt:PSHA], @"PSHA", 
			[NSNumber numberWithInt:PULA], @"PULA", 

/*			[NSNumber numberWithInt:DELAY], @"DELAY", 
			[NSNumber numberWithInt:DELAY1], @"DELAY1", 
			[NSNumber numberWithInt:DELAYG], @"DELAYG", 
			[NSNumber numberWithInt:DELAYG1], @"DELAYG1", 
			[NSNumber numberWithInt:TIME], @"TIME", 
			[NSNumber numberWithInt:TSTART], @"TSTART", 
			[NSNumber numberWithInt:TSTOP], @"TSTOP", */
			[NSNumber numberWithInt:TRAP], @"TRAP", 

			[NSNumber numberWithInt:SINA], @"SINA", 
			[NSNumber numberWithInt:COSA], @"COSA", 
			[NSNumber numberWithInt:TANA], @"TANA", 
			[NSNumber numberWithInt:ASINA], @"ASINA", 
			[NSNumber numberWithInt:ACOSA], @"ACOSA", 
			[NSNumber numberWithInt:ATANA], @"ATANA", 
			[NSNumber numberWithInt:SINHA], @"SINHA", 
			[NSNumber numberWithInt:COSHA], @"COSHA", 
			[NSNumber numberWithInt:TANHA], @"TANHA", 
			[NSNumber numberWithInt:ASINHA], @"ASINHA", 
			[NSNumber numberWithInt:ACOSHA], @"ACOSHA", 
			[NSNumber numberWithInt:ATANHA], @"ATANHA", 
			[NSNumber numberWithInt:ERFA], @"ERFA", 
			[NSNumber numberWithInt:FLTA], @"FLTA", 
			[NSNumber numberWithInt:STEP], @"STEP", 
			[NSNumber numberWithInt:RAMP], @"RAMP", 
			[NSNumber numberWithInt:ZIDZ], @"ZIDZ",
			[NSNumber numberWithInt:XIDZ], @"XIDZ",
			
			[NSNumber numberWithInt:PSTR], @"PSTR", 
			[NSNumber numberWithInt:FPSTR], @"FPSTR", 
			[NSNumber numberWithInt:PRTA], @"PRTA", 
			[NSNumber numberWithInt:PCHR], @"PCHR", 
			[NSNumber numberWithInt:FPRTA], @"FPRTA", 
			[NSNumber numberWithInt:FPCHR], @"FPCHR", 

			[NSNumber numberWithInt:MINN], @"MINN", 
			[NSNumber numberWithInt:MAXN], @"MAXN", 
			[NSNumber numberWithInt:MEANN], @"MEANN", 
			[NSNumber numberWithInt:MOD], @"MOD", 
			[NSNumber numberWithInt:RTABLE], @"RTABLE", 
			[NSNumber numberWithInt:TABLE], @"TABLE", 
			[NSNumber numberWithInt:RTAB], @"RTAB",
			[NSNumber numberWithInt:ATAB], @"ATAB",

			[NSNumber numberWithInt:SHL1], @"SHL1", 
			[NSNumber numberWithInt:SHL2], @"SHL2", 
			[NSNumber numberWithInt:SHL3], @"SHL3", 
			[NSNumber numberWithInt:SHL4], @"SHL4", 

			[NSNumber numberWithInt:ADDGP], @"ADDGP", 
			[NSNumber numberWithInt:ADDLP], @"ADDLP", 
			[NSNumber numberWithInt:LDGP], @"LDGP", 
			[NSNumber numberWithInt:LDLP], @"LDLP", 
			[NSNumber numberWithInt:LDSP], @"LDSP", 
			[NSNumber numberWithInt:SAVE], @"SAVE",
//			[NSNumber numberWithInt:SPADD], @"SPADD",

			[NSNumber numberWithInt:BRA], @"BRA", 
			[NSNumber numberWithInt:BEQ], @"BEQ", 
			[NSNumber numberWithInt:BNE], @"BNE", 
			[NSNumber numberWithInt:BGT], @"BGT", 
			[NSNumber numberWithInt:BGE], @"BGE", 
			[NSNumber numberWithInt:BLT], @"BLT", 
			[NSNumber numberWithInt:BLE], @"BLE", 
			[NSNumber numberWithInt:SWT], @"SWT", 
			[NSNumber numberWithInt:BSR], @"BSR", 
			[NSNumber numberWithInt:RTS], @"RTS", 

			[NSNumber numberWithInt:BRK], @"BRK", 
			[NSNumber numberWithInt:HLT], @"HLT", 
			nil
		];
		
		opcodeTable = [[NSMutableDictionary alloc] init];
/*		int i;
		for(i=0; i<[mnemonicTable count]; i++) {
			[opcodeTable setValue:forKey:];
		} */
		NSEnumerator *enumerator = [mnemonicTable keyEnumerator];
		id key;
 
		while ((key = [enumerator nextObject])) {
			id object = [mnemonicTable objectForKey:key];
			[opcodeTable setValue:key forKey:object];
		}
	}

	if (modeTable==nil) {
		modeTable = [[NSArray alloc] initWithObjects:@"GLOBAL", @"LOCAL", @"STACK", @"", @"PCREL", @"AUTODEC", @"AUTO", @"IMM", @"INDXG", 
												     @"INDXL", @"INDXGP", @"INDXLP", @"AUTOIND", @"AUTOIDC", @"", @"", nil];
	}

	memorySize = newMemorySize;
//	storeSize = newStoreSize;
	memory = (unsigned short int *)calloc(memorySize, sizeof(unsigned short int));
//	store = (double *)calloc(storeSize, sizeof(double));
	
	int i;
	for(i=0; i<memorySize; i++) {
		memory[i] = 0xffff;
	}
	
	exitCode = 0;
	echo = NO;
		
	ptr = 0;
	
	pc = 0;
	gp = 0;
	sp = 0;
	lp = 0;
	
	trapCalls = [[NSMutableArray alloc] init];
	
//	NSDictionary *mnemonics = nil;
//	_time = 0.0;
//	_startTime = 0.0;
//	_stopTime = 10.0;	
	return self;
}

- (unsigned short int *) memory
{
	return memory;
}

/*- (double *)store
{
	return store;
} */

- (void) setHistory:(id <history, NSObject>)history
{
	[history retain];
	[_history release];
	_history = history;
}

- (id) history
{
	return _history;
}

- (void) setNHistory:(int)nHistory
{
	_nHistory = nHistory;
}

- (int) nHistory
{
	return _nHistory;
}

- (void) setNTables:(int) newNTables
{
	nTables = newNTables;
}

- (int) nTables
{
	return nTables;
}

- (BOOL) echo
{
	return echo;
}

- (void) setEcho:(BOOL)newEcho
{
	echo = newEcho;
}

- (void) putOpcode:(unsigned short int)opcode mode:(unsigned short int)mode
{
	unsigned short int val = opcode | mode;
	if (echo) {
//		printf("%04x: %04x\t", ptr, val);
		printf("%04x:\t", ptr);
		NSNumber *opcodeNum = [[NSNumber alloc] initWithInt:opcode];
		NSString *opcodeName = [opcodeTable objectForKey:opcodeNum];
		if (opcodeName!=nil) {
			printf("%s", [opcodeName UTF8String]);
		}
		[opcodeNum release];
		
		int dataType = mode & 0x10;
		if (dataType==INTEGER) {
			printf(".I\t");
		} else {
			printf(".X\t");
		}

		int addrMode = mode & 0xf;
		NSString *modeName = [modeTable objectAtIndex:addrMode];
		if (modeName!=nil) {
			printf("%s\n", [modeName UTF8String]);
		}
	}
	memory[ptr++] = val;
	
}

- (void) putUInt:(unsigned int)val
{
	if (echo) {
		printf("%04x:\t%08x\n", ptr, val);
	}
	*(unsigned int *)(&memory[ptr]) = val;
	ptr += M;
}

- (void) putUShInt:(unsigned short int)val
{
	if (echo) {
		printf("%04x:\t%04x\n", ptr, val);
	}
	memory[ptr++] = val;
}

- (void) putUInt:(unsigned int)val at:(unsigned int)location
{
	memory[location] = val;
}

- (void) putDouble:(double)val
{
	if (echo) {
		printf("%04x:\t%g\n", ptr, val);
	}
	double *p = (double *)(&memory[ptr]);
	*p = val;
	ptr += N;
}

- (void) putString:(NSString *)string withNewLine:(BOOL)newLine
{
	int i=0;
//	for(i=0; i<[string length]; i++) {
	while (i<[string length]) {
		unichar ch = [string characterAtIndex:i++];
		if (ch=='\\') {
			ch = [string characterAtIndex:i++];
			if (ch=='n') {
				ch = '\n';
			} else if (ch=='t') {
				ch = '\t';
			} else {
				ch = '\0';
			}
		}
		[self putUShInt:(unsigned int)ch];
//		i++;
	}
	if (newLine) {
		[self putUShInt:(unsigned int)'\n'];
	}
	[self putUShInt:(unsigned int)0x0];
}

- (int) registerTrapCall:(SEL)sel forObject:(id) object
{
	NSString *selString = NSStringFromSelector(sel);
	NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:selString, @"selector", object, @"object", nil];
	int n = [trapCalls count];
	[trapCalls addObject:dict];
	[dict release];
	return n;
}

- (void) runFromPc
{
	BOOL running = YES;
//	double *p;
	unsigned int addr;
	unsigned int disp;
	unsigned short int instruction;
//	double delayTime;
//	double initialValue;
//	int markValue;
	double value;
	double x1, x2;
	char ch;
//	interp *interpolator;
//	int interpType;
//	gsl_interp_accel *accel;
//	gsl_spline *spline;
	double *xp;
	double *yp;
	int errorCode;
	int n;
	int i;
	unsigned int mode;
	unsigned int type1ins;
	int fileNumber;
	FILE *f;
	
//	id history = [self history];
//	double startTime = [self startTime];
//	double stopTime = [self stopTime];
	exitCode = 0;

	while (running) {
		lastpc = pc;
		instruction = memory[pc];
/*		if (pc==0x01a1) {
			printf("%08x: %04x %g %d\n", pc, instruction, acc.x, acc.i);
		} */
		pc += 1;
		
		if (instruction<ABSA) {
			int n, nn;
			if ((instruction & 0x0010)==INTEGER) {
				n = M;
				nn = MM;
			} else {
				n = N;
				nn = NN;
			}
		
			mode = instruction & 0x000f;
			switch (mode) {
				case GLOBAL:
					addr = *(unsigned int *)(&memory[pc]) + gp;
					pc += M;
					break;
				case LOCAL:
					addr = lp + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case STACK:
					addr = sp + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case PCREL:
					addr = pc + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case AUTO:
					addr = sp;
					sp += n;
					break;
				case AUTODEC:
					sp -= n;
					addr = sp;
					break;
				case IMM:
					addr = pc;
					pc += n;
					break;
				case INDXG:
					addr = (acc.i<<nn) + *(unsigned int *)(&memory[pc]) + gp;
/*					printf("INDXG %08x: %04x %8s %1s %08x\n", pc, instruction & 0xfff0, [[modeTable objectAtIndex:mode] UTF8String], 
													  (instruction & INTEGER)==0?"F":"I", 
													  (acc.i<<nn) + *(unsigned int *)(&memory[pc])); */
					pc += M;
					break;
				case INDXL:
					addr = (acc.i<<nn) + *(unsigned int *)(&memory[pc]) + lp;
					pc += M;
					break;
				case INDXGP:
					addr = (acc.i<<nn) + *(unsigned int *)(&memory[*(unsigned int *)(&memory[pc]) + gp]) + gp;
					pc += M;
					break;
				case INDXLP:
					addr = (acc.i<<nn) + *(unsigned int *)(&memory[*(unsigned int *)(&memory[pc]) + lp]) + gp;
					pc += M;
					break;
				case AUTOIND:
					addr = *(unsigned int *)(&memory[sp]);
					sp += M;
					break;
				case AUTOIDC:
					sp -= M;
					addr = *(unsigned int *)(&memory[sp]);
					break;
			}
			
			type1ins = instruction & 0xfff0;
			switch (type1ins) {
				case PSH + FLTPOINT:
					*(double *)(&memory[sp]) = *(double *)(&memory[addr]);
					sp += N;
					break;
				case PSH + INTEGER:
					*(unsigned int *)(&memory[sp]) = *(unsigned int *)(&memory[addr]);
					sp += M;
					break;
				case PUL + FLTPOINT:
					sp -= N;
					*(double *)(&memory[addr]) = *(double *)(&memory[sp]);
					break;
				case PUL + INTEGER:
					sp -= M;
					*(unsigned int *)(&memory[addr]) = *(unsigned int *)(&memory[sp]);
					break;				
				case LDA + FLTPOINT:
					acc.x = *(double *)(&memory[addr]);
//					printf("%08x %08x: %g\n", pc, addr, acc.x);
					break;
				case LDA + INTEGER:
					acc.i = *(unsigned int *)(&memory[addr]);
					break;
				case STA + FLTPOINT:
//					printf("STA.X %08x %g\n", addr, acc.x);
					*(double *)(&memory[addr]) = acc.x;
					break;
				case STA + INTEGER:
					*(unsigned int *)(&memory[addr]) = acc.i;
					break;
				case ADD + FLTPOINT:
					acc.x += *(double *)(&memory[addr]);
					break;
				case ADD + INTEGER:
					acc.i += *(unsigned int *)(&memory[addr]);
					break;
				case SUB + FLTPOINT:
					acc.x -= *(double *)(&memory[addr]);
					break;
				case SUB + INTEGER:
					acc.i -= *(unsigned int *)(&memory[addr]);
					break;
				case MLT + FLTPOINT:
					acc.x *= *(double *)(&memory[addr]);
					break;
				case MLT + INTEGER:
					acc.i *= *(unsigned int *)(&memory[addr]);
					break;
				case DVD + FLTPOINT:
					value = *(double *)(&memory[addr]);
					if (value!=0.0) {
						acc.x /= value;
					} else {
						exitCode = -1;
						running = NO; 
					}
					break;
				case DVD + INTEGER:
					acc.i /= *(unsigned int *)(&memory[addr]);
					break;
				case PWR + FLTPOINT:
					acc.x = pow(acc.x, *(double *)(&memory[addr]));
					break;
//	To do: Integer power operations.
				case GTH + FLTPOINT:
					acc.i = acc.x > *(double *)(&memory[addr]);
					break;
				case GTH + INTEGER:
					acc.i = acc.i > *(unsigned int *)(&memory[addr]);
					break;
				case GTE + FLTPOINT:
					acc.i = acc.x >= *(double *)(&memory[addr]);
					break;
				case GTE + INTEGER:
					acc.i = acc.i >= *(unsigned int *)(&memory[addr]);
					break;
				case LTH + FLTPOINT:
					acc.i = acc.x < *(double *)(&memory[addr]);
					break;
				case LTH + INTEGER:
					acc.i = acc.i < *(unsigned int *)(&memory[addr]);
					break;
				case LTE + FLTPOINT:
					acc.i = acc.x <= *(double *)(&memory[addr]);
					break;
				case LTE + INTEGER:
					acc.i = acc.i <= *(unsigned int *)(&memory[addr]);
					break;
				case EQL + FLTPOINT:
					acc.i = acc.x == *(double *)(&memory[addr]);
					break;
				case EQL + INTEGER:
					acc.i = acc.i == *(unsigned int *)(&memory[addr]);
					break;
				case NEQ + FLTPOINT:
					acc.i = acc.x != *(double *)(&memory[addr]);
					break;
				case NEQ + INTEGER:
					acc.i = acc.i != *(unsigned int *)(&memory[addr]);
					break;
				case INC + FLTPOINT:
					*(double *)(&memory[addr]) += 1.0;
					break;
				case INC + INTEGER:
//					*(unsigned int *)(&memory[addr]) += 1;
					*(int *)(&memory[addr]) += 1;
//					printf("%04x: INC.I %08x\n", lastpc, *(int *)(&memory[addr]));
					break;
				case DEC + FLTPOINT:
					*(double *)(&memory[addr]) -= 1.0;
					break;
				case DEC + INTEGER:
					*(int *)(&memory[addr]) -= 1;
					break;
				case NEG + FLTPOINT:
					*(double *)(&memory[addr]) = -*(double *)(&memory[addr]);
					break;
				case NEG + INTEGER:
					*(int *)(&memory[addr]) = -*(unsigned int *)(&memory[addr]);
					break;
				case CLR + FLTPOINT:
					*(double *)(&memory[addr]) = 0.0;
					break;
				case CLR + INTEGER:
					*(unsigned int *)(&memory[addr]) = 0;
					break;
				case JMP + INTEGER:
					pc = *(unsigned int *)(&memory[addr]);
					break;
				case JSR + INTEGER:
					rp -= M;
					*(unsigned int *)(&memory[rp]) = lp;
					rp -= M;
					*(unsigned int *)(&memory[rp]) = pc;
					lp = sp;
					pc = *(unsigned int *)(&memory[addr]);
					break;
				case LEA + INTEGER:
					acc.i = addr;
					break;
				case LEAS + INTEGER:
					sp = addr;
					break;
				case LEAL + INTEGER:
					lp = addr;
					break;
				case LEAG + INTEGER:
					gp = addr;
					break;
				case PEA + INTEGER:
				case PEA + FLTPOINT:
//					printf("%04x: PEA.I %04x:\n", lastpc, addr-gp);
					*(unsigned int *)(&memory[sp]) = addr;
					sp += M;
					break;
/*				case PRT + FLTPOINT:
					printf("%g", acc.x);
					break;
				case PRT + INTEGER:
					printf("%d", (int)acc.i);
					break; */
				case PCHR + INTEGER:
					ch = (unichar)memory[addr];
					putchar(ch);
					break;
				case FPCHR + INTEGER:
					fileNumber = *(int *)(&memory[pc]);
					pc += M;
					f = files[fileNumber];
					ch = (unichar)memory[addr];
					fputc(ch, f);
					break;
/*				case FPRT + FLTPOINT:
					printf("%g", acc.x);
					break; 
				case FPRT + INTEGER:
					printf("%d", (int)acc.i);
					break; + IMM:
					ch = (unichar)memory[pc++];
					putchar(ch);
					break; */
// TO DO: Convert these to +FLTPOINTtrap calls.
				case INTERP+FLTPOINT:
				case INTERP+INTEGER:
//					interpolator = (interp *)*(unsigned int *)(&memory[addr]);
//					acc.x = interpolate(interpolator, acc.x);
					{
						EXPSpline *interpolator = (EXPSpline *)*(unsigned int *)(&memory[addr]);
						value = acc.x;
//						if (_time>1905) {
//							printf("value = %g\n", value);
//						}
						acc.x = [interpolator evaluate:value primes:0];
//						printf("INTERP: %g %g\n", value, acc.x);
					}
					break;
				case TABLE+FLTPOINT:
				case TABLE+INTEGER:
					{
						n = *(unsigned int *)(&memory[pc]);
						pc += M;
						int method = memory[pc++];
						sp -= n*N;
						yp = (double *)(&memory[sp]);
						sp -= n*N;
						xp = (double *)(&memory[sp]);
						EXPSpline *interpolator = (EXPSpline *)*(unsigned int *)(&memory[addr]);
//						EXPSpline *interpolator = [[EXPSpline alloc] init];	// makeInterp(xp, yp, n, interpType)
						if (interpolator==nil) {
							printf("\nCould not initialise interpolator\n");
							exitCode = -1;
							running = NO; 
						}
						NSString *interpType = [[EXPVirtualMachine interpNames] objectAtIndex:method];
						BOOL success = [interpolator setPoints:interpType nPoints:n xPoints:xp yPoints:yp];
						if (!success) {
							printf("\nCould not initialise interpolator\n");
							exitCode = -1;
							running = NO; 
						}
//						*(unsigned int *)(&memory[addr]) = (unsigned int)interpolator;
					}
					break;

				case RTABLE+FLTPOINT:
				case RTABLE+INTEGER:
					{
						n = *(unsigned int *)(&memory[pc]);
						pc += M;
						int method = memory[pc++];
						sp -= N;
						double xMax = *(double *)(&memory[sp]);
						sp -= N;
						double xMin = *(double *)(&memory[sp]);
//						pc += N;
						sp -= n*N;
						yp = (double *)(&memory[sp]);
//						EXPSpline *interpolator = [[EXPSpline alloc] init];
						EXPSpline *interpolator = (EXPSpline *)*(unsigned int *)(&memory[addr]);
						if (interpolator==nil) {
							printf("\nCould not initialise interpolator\n");
							exitCode = -1;
							running = NO; 
						}
						NSString *interpType = [[EXPVirtualMachine interpNames] objectAtIndex:method];
						BOOL success = [interpolator setPoints:interpType nPoints:n xMin:xMin xMax:xMax yPoints:yp];
						if (!success) {
							printf("\nCould not initialise interpolator\n");
							exitCode = -1;
							running = NO; 
						}
//						*(unsigned int *)(&memory[addr]) = (unsigned int)interpolator;
					}
					break;

				case RTAB:
					{
						EXPSpline *interpolator = (EXPSpline *)*(unsigned int *)(&memory[addr]);
						[interpolator release];
						*(unsigned int *)(&memory[addr]) = 0x0;
					}
					break;

				case ATAB:
					{
						EXPSpline *interpolator = [[EXPSpline alloc] init];
						*(unsigned int *)(&memory[addr]) = (unsigned int)interpolator;
					}
					break;

				default:
					printf("\nIllegal opcode: %04x\n", instruction);
					running = NO; 
			}
			continue;
		}
		
		switch (instruction) {

			case PSTR + INTEGER:
				for(;;) {
					n = *(unichar *)(&memory[pc++]);
					if (n==0) break;
					printf("%c", n);
				}
				break;

			case FPSTR + INTEGER:
				fileNumber = *(int *)(&memory[pc]);
				pc += M;
				f = files[fileNumber];
				for(;;) {
					n = *(unichar *)(&memory[pc++]);
					if (n==0) break;
					fprintf(f, "%c", n);
				}
				break;

			case PRTA + FLTPOINT:
				printf("%g", acc.x);
				break;

			case PRTA + INTEGER:
				printf("%d", (int)acc.i);
				break;

/*			case PCHR + INTEGER + IMM:
				ch = (unichar)memory[pc++];
				putchar(ch);
				break; */

			case FPRTA + FLTPOINT:
				fileNumber = *(int *)(&memory[pc]);
				pc += M;
				f = files[fileNumber];
				fprintf(f, "%g", acc.x);
				break;

			case FPRTA + INTEGER:
				fileNumber = *(int *)(&memory[pc]);
				pc += M;
				f = files[fileNumber];
				fprintf(f, "%d", (int)acc.i);
				break;

/*			case FPCHR + INTEGER + IMM:
				ch = (unichar)memory[pc++];
				putchar(ch);
				break;  */

			case ABSA:
				acc.x = fabs(acc.x);
				break;
			case NEGA:
				acc.x = -acc.x;
				break;
			case SQRTA:
				if (acc.x>=0) {
					acc.x = sqrt(acc.x);
				} else {
//					errorCode = GSL_ERANGE;
					errorCode = -1;
					running = NO;
				}
				break;
			case INTA:
				acc.i = (int)acc.x;
				break;
			case EXPA:
				acc.x = exp(acc.x);
				break;
			case LNA:
				acc.x = log(acc.x);
				break;
			case LOG10A:
				acc.x = log10(acc.x);
				break;
			case PSHA:
				*(double *)(&memory[sp]) = acc.x;
				sp += N;
				break;
			case PULA:
				sp -= N;
				acc.x = *(double *)(&memory[sp]);
				break;
			case PSHA + INTEGER:
				*(unsigned int *)(&memory[sp]) = acc.i;
				sp += M;
				break;
			case PULA + INTEGER:
				sp -= M;
				acc.i = *(unsigned int *)(&memory[sp]);
				break;

			case SINA:
				acc.x = sin(acc.x);
				break;
			case COSA:
				acc.x = cos(acc.x);
				break;
			case TANA:
				acc.x = tan(acc.x);
				break;
			case ASINA:
				acc.x = asin(acc.x);
				break;
			case ACOSA:
				acc.x = acos(acc.x);
				break;
			case ATANA:
				acc.x = atan(acc.x);
				break;
			case SINHA:
				acc.x = sinh(acc.x);
				break;
			case COSHA:
				acc.x = cosh(acc.x);
				break;
			case TANHA:
				acc.x = tanh(acc.x);
				break;
			case ASINHA:
#ifdef GNUSTEP
				acc.x = log(val + sqrt(1.0 + acc.x*acc.x));
#else
				acc.x = asinh(acc.x);
#endif
				break;
			case ACOSHA:
#ifdef GNUSTEP
				acc.x = log(val + sqrt(acc.x*acc.x - 1.0));
#else
				acc.x = acosh(acc.x);
#endif
				break;
			case ATANHA:
#ifdef GNUSTEP
				acc.x = 0.5*log((1.0 + acc.x)/(1.0 - acc.x));
#else
				acc.x = atanh(acc.x);
#endif
				break;
			case ERFA:
#ifdef GNUSTEP
				acc.x = 0.0;
#else
				acc.x = erf(acc.x);
#endif
				break;
			case FLTA:
				acc.x = (double)acc.i;
				break;

/*			case STEP:
				n = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				x2 =  *(double *)(&memory[sp]);
				sp -= N;
				x1 =  *(double *)(&memory[sp]);
				if (_time>=x1) {
					acc.x = x2;
				} else {
					acc.x = 0.0;
				} 
				break; */

			case ZIDZ:
				n = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				x2 =  *(double *)(&memory[sp]);
				sp -= N;
				x1 =  *(double *)(&memory[sp]);
				if (x2!=0.0) {
					acc.x = x1/x2;
				} else {
					acc.x = 0.0;
				}
				break;

			case XIDZ:
				n = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				x2 =  *(double *)(&memory[sp]);
				sp -= N;
				x1 =  *(double *)(&memory[sp]);
				if (x2!=0.0) {
					acc.x = x1/x2;
				} else {
					acc.x = x1;
				}
				break;
				
			case BRA + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				pc = disp;
				break;
			case BEQ + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				if (acc.i==0) {
					pc = disp;
				}
				break;
			case BNE + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				if (acc.i!=0) {
					pc = disp;
				}
				break;
			case BGT + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				if ((int)acc.i>0) {
					pc = disp;
				}
				break;
			case BGE + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				if ((int)acc.i>=0) {
					pc = disp;
				}
				break;
			case BLT + LONG:
				disp = (int)*(unsigned int *)(&memory[pc]);
				pc += M;
				if ((int)acc.i<0) {
					pc = disp;
				}
				break;
			case BLE + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
				if ((int)acc.i<=0) {
					pc = disp;
				}
				break; 
			case BSR + LONG:
				disp = *(unsigned int *)(&memory[pc]);
				pc += M;
/*				rp -= M;
				*(unsigned int *)(&memory[rp]) = lp; */
				rp -= M; 
				*(unsigned int *)(&memory[rp]) = pc;
//				lp = sp;
				pc = disp;
				break;

			case SWT + SHORT:
			case SWT + LONG:
				addr = (acc.i << MM) + pc;
				pc = *(unsigned int *)(&memory[addr]);
				break;

			case BRA + SHORT:
				disp = memory[pc++];
				pc = disp;
				break;
			case BEQ + SHORT:
				disp = memory[pc++];
				if (acc.i==0) {
					pc = disp;
				}
				break;
			case BNE + SHORT:
				disp = memory[pc++];
				if (acc.i!=0) {
					pc = disp;
				}
				break;
			case BSR + SHORT:
				disp = memory[pc++];
				rp -= M;
				memory[rp] = lp;
				rp -= M;
				memory[rp] = pc;
				lp = sp;
				pc = disp;
				break;

			case DBNC + SHORT:
			case DBNC + LONG:
				switch (instruction & 0x000f) {
				case GLOBAL:
					addr = *(unsigned int *)(&memory[pc]) + gp;
					pc += M;
					break;
				case LOCAL:
					addr = lp + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case STACK:
					addr = sp + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case PCREL:
					addr = pc + *(unsigned int *)(&memory[pc]);
					pc += M;
					break;
				case AUTO:
					addr = sp;
					sp -= N;
					break;
				case IMM:
					addr = pc;
					pc += M;
					break;
				case INDXG:
					addr = (acc.i<<MM) + *(unsigned int *)(&memory[*(unsigned int *)(&memory[pc]) + gp]) + gp;
					pc += M;
					break;
				case INDXL:
					addr = (acc.i<<MM) + *(unsigned int *)(&memory[*(unsigned int *)(&memory[pc]) + lp]) + gp;
					pc += M;
					break;
				}
//				unsigned int *p = (unsigned int *)(&memory[addr]);
//				(*p)--;
				(*(unsigned int *)(&memory[addr]))--;
				if (instruction==(DBNC + SHORT)) {
					disp = memory[pc++];
				} else {
					disp = *(unsigned int *)(&memory[pc]);
					pc += M;
				}
				if ((*(int *)(&memory[addr]))>=0) {
					pc = disp;
				}
				break;

			case RTS:
				sp = lp;		// ????
				pc = *(unsigned int *)(&memory[rp]);
				rp += M;
				lp = *(unsigned int *)(&memory[rp]);
				rp += M;
				break;

			case TRAP:
				{
					unsigned int trapCode = *(unsigned int *)(&memory[pc]);
					pc += M;
					NSDictionary *dict = [trapCalls objectAtIndex:trapCode];
					SEL sel = NSSelectorFromString([dict objectForKey:@"selector"]);
					id object = [dict objectForKey:@"object"];
//					int ret;
					[object performSelector:sel withObject:object withObject:self /*returnValue:&ret*/];
//					running = (ret==0);
				}
				break;
			
/*			case TIME:
				acc.x = _time;
				break;

			case TSTART:
				acc.x = startTime;
				break;

			case TSTOP:
				acc.x = stopTime;
				break; */

/*			case DELAY:
				addr = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				markValue = (int)(*(double *)(&memory[sp]));
				sp -= N;
				initialValue = *(double *)(&memory[sp]);
				sp -= N;
				delayTime = *(double *)(&memory[sp]);
				if (delayTime<0) {
					acc.x = initialValue;
				} else if (delayTime>_time) {
					printf("Oops! Delay time %g is greater than time %g\n", delayTime, _time);
					exitCode = -1;
					running = NO; 
				} else {
					acc.x = [history pastValue:addr time:delayTime mark:markValue];
				}
//				printf("%04x %12.5f %12.5f %12.5f %4d\n", addr, acc.x, initialValue, delayTime, markValue);
				break;

			case DELAY1:
				addr = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				markValue = (int)(*(double *)(&memory[sp]));
				sp -= N;
				initialValue = *(double *)(&memory[sp]);
				sp -= N;
				delayTime = *(double *)(&memory[sp]);
				sp -= M;
				addr += *(unsigned int *)(&memory[sp]);
				if (delayTime<_startTime) {
					acc.x = initialValue;
				} else if (delayTime>_time) {
					printf("Oops! Delay time %g is greater than time %g\n", delayTime, _time);
					exitCode = -1;
					running = NO; 
				} else {
					acc.x = [history  pastValue:addr time:delayTime mark:markValue];
				}
//				printf("%04x %12.5f %12.5f %12.5f %4d\n", addr, acc.x, initialValue, delayTime, markValue);
				break; */

			case MINN:
				n = *(unsigned int *)(&memory[pc]);
				if (n<=0) {
					running = NO;
					break;
				}
				pc += M;
				sp -= N;
				acc.x = (int)(*(double *)(&memory[sp]));
				for(i=1; i<n; i++) {
					sp -= N;
					value = (int)(*(double *)(&memory[sp]));
					if (value < acc.x) {
						acc.x = value;
					}
				}
				break;

			case MAXN:
				n = *(unsigned int *)(&memory[pc]);
				if (n<=0) {
					running = NO;
					break;
				}
				pc += M;
				sp -= N;
				acc.x = (int)(*(double *)(&memory[sp]));
				for(i=1; i<n; i++) {
					sp -= N;
					value = (int)(*(double *)(&memory[sp]));
					if (value > acc.x) {
						acc.x = value;
					}
				}
				break;

//	Check reference for stable algorithm for computing mean.
			case MEANN:
				n = *(unsigned int *)(&memory[pc]);
				if (n<=0) {
					running = NO;
					break;
				}
				pc += M;
				acc.x = 0.0;
				for(i=0; i<n; i++) {
					sp -= N;
					acc.x += (int)(*(double *)(&memory[sp]));
				}
				acc.x = acc.x/(double)n;
				break;

			case MOD:
				n = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp -= N;
				x2 = *(double *)(&memory[sp]);
				sp -= N;
				x1 = *(double *)(&memory[sp]);
				acc.x = floor(x1/x2);
				break;

			case SHL1 + INTEGER:
				acc.i = acc.i<<1;
				break;

			case SHL2 + INTEGER:
				acc.i = acc.i<<2;
				break;

			case SHL3 + INTEGER:
				acc.i = acc.i<<3;
				break;

			case SHL4 + INTEGER:
				acc.i = acc.i<<4;
				break;

			case ADDGP:
				acc.i += gp;
				break;

			case ADDLP:
				acc.i += lp;
				break;

			case LDGP:
				addr = *(unsigned int *)(&memory[pc]);
				pc += M;
				gp = addr;
				break;

			case LDLP:
				addr = *(unsigned int *)(&memory[pc]);
				pc += M;
				lp = addr;
				break;

			case LDSP:
				addr = *(unsigned int *)(&memory[pc]);
				pc += M;
				sp = addr;
				break;

			case SAVE:
				rp -= M;
				*(unsigned int *)(&memory[rp]) = lp;
				lp = sp;
				break;

//			case SPADD:
//				break;

			case TRP: 
				break;

			case TRPN: 
				if (acc.i<0) {
					exitCode = -1;
					running = NO; 
				}
				break;

			case BRK: 
				printf("Break at location %08x\n", pc);
				[self dumpRegisters];
				break;

			case HLT: 
				exitCode = 0;
				running = NO; 
				break;

			default:
				printf("\nIllegal opcode: %04x at %04x\n", instruction, lastpc);
				running = NO; 

		}
		
//		printf("%g %g\n", _time, acc.x);
		if (!finite(acc.x)) {
			printf("\nResult of instruction %04x undefined at %04x. Acc.x = %g\n", instruction, lastpc, acc.x);
			exitCode = -1;
			running = NO; 
		} 
	}
}

- (int) exitCode
{
	return exitCode;
}

- (void) setFilenames:(id)filenames
{
	[filenames retain];
	[_filenames release];
	_filenames = filenames;
} 

- (id) filenames
{
	return _filenames;
}

- (void) setFiletypes:(id)filetypes
{
	[filetypes retain];
	[_filetypes release];
	_filetypes = filetypes;
} 

- (id) filetypes
{
	return _filetypes;
}

- (int) nFiles
{
	return  nextFile;
}

- (int) attachFile:(NSString *)filename type:(NSString *)filetype
{
	if (nextFile>MAXFILES) {
		return -1;
	}
	
	NSMutableArray *filenames = [self filenames];
	NSMutableArray *filetypes = [self filetypes];
	
	[filenames addObject:filename];
	[filetypes addObject:filetype];
	
	int file = nextFile;
	
	nextFile++;
	
	return file;
}

- (BOOL) openFiles
{
	BOOL success = YES;
	
	NSArray *filenames = [self filenames];
	NSArray *filetypes = [self filetypes];
	
	int i;
	for(i=0; i<[self nFiles]; i++) {
		NSString *filename = [filenames objectAtIndex:i];
		NSString *filetype = [filetypes objectAtIndex:i];
		FILE *f = fopen([filename UTF8String], [filetype UTF8String]);
		if (f==NULL) {
			success = NO;
		} 
	
		files[i] = f;
	}
	
	return success;
}

- (void) closeFiles
{
	int i;
	for(i=0; i<[self nFiles]; i++) {
		fclose(files[i]);
	}
}

- (unsigned int)ptr
{
	return ptr;
}

- (void) setPtr:(unsigned int)newPtr
{
	ptr = newPtr;
}

- (unsigned int)pc
{
	return pc;
}

- (void) setPc:(unsigned int)newPc
{
	pc = newPc;
}

- (unsigned int)sp
{
	return sp;
}

- (void) setSp:(unsigned int)newSp
{
	sp = newSp;
}

- (unsigned int)gp
{
	return gp;
}

- (void) setGp:(unsigned int)newGp
{
	gp = newGp;
}

- (void) setRp:(unsigned int)newRp
{
	rp = newRp;
}

- (unsigned int)rp
{
	return rp;
}

- (double)acc_fp
{
	return acc.x;
}

- (void) setAccFP:(double)newAcc
{
	acc.x = newAcc;
}

- (int) accInt
{
	return acc.i;
}

- (void) setAccInt:(int)newAcc
{
	acc.i = newAcc;
}

- (unsigned int) memorySize
{
	return memorySize;
}

- (void) dumpRegisters
{
	printf("PC = %08x\n", pc);
	printf("GP = %08x\n", gp);
	printf("LP = %08x\n", lp);
	printf("SP = %08x\n", sp);
	printf("A  = %08x %g %d\n", acc.i, acc.x, acc.i);
	printf("\nexitCode = %d\n\n", exitCode);
}

- (void) dump:(unsigned int)start to:(unsigned int)stop
{
	int i;
	for(i=start; i<stop; i++) {
		printf("%04x %04x\n", i, memory[i]);
	}
}

- (void) dumpDoubles:(unsigned int)start to:(unsigned int)stop
{
	int i;
	for(i=start; i<stop; i+=N) {
		printf("%04x %g\n", i, *(double *)(&memory[i]));
	}
}

- (void) dealloc
{
//	printf("deallocating store machine.\n");
/*	int i;
	for(i=0; i<nextFile; i++) {
		if (files[i]!=NULL) {
			fclose(files[i]);
		}
	} */
	free(files);
	[_filenames release];
	[_filetypes release];
	
	[trapCalls release];
	
	free(memory);
//	free(store);

/*	if (tables!=NULL) {
		int i;
		for(i=0; i<[self nTables]; i++) {
			free(tables[i]);
		}
		free(tables);
	} */

	[super dealloc];
}

@end