/* Version of "assmblgo" hastily patched to use "curses" for
greater terminal independence.
It still sends null characters to a terminal to try to gain timing
control.
*/
#include <curses.h>
#include <ctype.h>
/*       This program is a simulator for a somewhat simplified
      version of the DEC PDP-8 computer.
*/
#define false 0
#define true ~0
/* The conventional starting address (O200) for PDP8 programs */
#define start  128

/* The PDP-8 instruction repertoire ! */

#define and8   0
#define tad    1
#define isz    2
#define dca    3
#define jms    4
#define jmp    5
#define iot    6
#define opr    7

/* Size of memory for this simulated machine */

#define coresize  4095
#define corelimit 4095
#define elim     20   /* maximum number of "events" */
#define symlim    320  /* number of symbols in user symbol table */



#define mask7   7
#define mask77  077
#define mask177 0177
#define mask200 0200
#define mask400 0400
#define mask770 0770
#define mask777  0777
#define mask7000 07000
#define mask7600 07600
#define mask7700 07700
#define mask7777 07777


/* These just define positions for various components of display */

#define accrow   2            /* accumulator register display */
#define acccol   12

#define linkrow  2           /* link register display */
#define linkcol  24

#define pcrow    2            /* program counter display */
#define pccol    36

#define mem1row   12          /* Window into memory, instructions */
#define mem1col   10

#define mem2row   12          /* Window into Memory, data */
#define mem2col   29

#define mem0row   7           /* Header for Memory Displays */

#define waitrow  20           /* where "To Continue Press Return" msg goes */
#define waitcol  40

#define dirow    2            /* Decode of Instruction */
#define dicol    45

#define tirow    5            /* "Time" */
#define ticol    12

#define periphrow  16         /* Peripherals */

#define d00row   17          /* "DEVICE" flags etc */
#define d00col   0

#define d03row   18
#define d03col   0

#define d04row   19
#define d04col   0

#define d50row   20
#define d50col   0

#define d60row   21
#define d60col   0

#define d40row   18
#define d40col   40

#define d41row    19
#define d41col    40

#define dmarow  16
#define dmacol  40

/* some device id codes for i/o */
#define d00      0
#define d03      3
#define d04      4
#define d50      050
#define d60      060
#define d40      040
#define d41     041



/* Some timing constants used in calls to delayn */
#define regchng  10           /* Time to highlight a register, after a change */
#define memchng  10           /* Time to highlight a memory location */


/* symbol, packed array for a name */
/* symbol == packed array[1..6] of char;*/

/* Classification of symbols in sym table*/
/* symtype == (label8,mri8,iot8,opr8,indir8);   */
#define label8  0
#define mri8    1
#define opr8    2
#define iot8    3
#define indir8  4

/* record structure for entries in symbol table */
struct entry {
	char name[6];
	int stype;
	int value;
};

/* The files: */
FILE *dumpfile;  /* file for output */
int dumpopen;    /* flag to indicate if file opened */
FILE *kbd;       /* file for pseudo-keyboard input */
FILE *tty;       /* file for pseudo-teletype output */
char ttybuff[100]; /* internal copy of tty output line */
int ttyc = 0;

/* The symbol table passed on from "smap" */
struct entry symtab[symlim];
int numsym;


/* The variables representing registers of the simulated machine: */
int acc;     /* PDP-8 accumulator */
int pc;      /* PDP-8 program counter */
int opc;     /* copy of same, useful for catching certain bugs */
int link8;    /* PDP-8 link bit */
int mar;     /* Memory address register */
int mdr;     /* Memory data register */
int ir;      /* Instruction register */

/* Additional variables relating to register use, instruction    */
/* & address decoding:                                         */
int opcode;  /* Op code */
int page0;
int indirect;
int location;
int running; /* Our flag indicating that program was running */
int aborted; /* Flag to indicate if execution aborted by illegal mem-ref */
int specialbreak; /* special entry to break package */
int oprbits;
int device;
int inst;



int acclink; /* A temporary variable to make arithmetic etc */
/*  easier to work with in this PASCAL(C?) program */



/* The PDP-8"s memory */
int store[coresize];
int breakpts[coresize];


/* controls on execution & display */
int slowfactor; /* depends on terminal speed, determines number of nulls sent */

int singlestep;
int regdisplay;
int progdisplay;
int datadisplay;
int dcddisplay;
int perdisplay;
int reglog;
int fast;

int time8;
int idelay;
int icount;

/* Variables used in several of the breakpoint routines  */
char userinput[80]; /* user"s command */
int chcnt;


/* Flags etc for i/o simulations */
int ionf;
int d03f;
int d04f;
int d50f;
int d60f;
int d40f;
int d41f;

int d03b;       /* Buffers for simulated i/o devices */
int d04b;

int d40b;      /* "disc" block register */
int d41b;      /* disc memory address */

int d60b;


int etime[elim];  /* Event table for i/o simulations */
int etype[elim];

int times[2][7] = {
	{
		0, 1800, 900, 2000, 550, 180,  0           }
	,
	{
		0,  150, 100,  180,  45,  40,  0            }
	,
};
int rtimes[2][7] = {
	{
		0,  300,   0,    0,   0,   0,   0        }
	,
	{
		0,    0,   0,    0,   0,   0,   0        }
	,
};

/* The times arrays are used in association with postevent, they */
/* define the basic delay, & random component of delay. */
/* the 0/1 index says whether or not have set "fast" mode */
/* when very little delay for peripherals or in more normal mode */
/* the various entries are */
/* 0   the cpu (unused) */
/* 1   the keyboard */
/* 2   the teletype */
/* 3   the clock    */
/* 4   the a/d      */
/* 5   disc seek (d40)       */
/* 6   disc read/write (d41) currently treated specially, 128 times 3 mem cycles */

int numevents;

int interrupt;
int clockoff;
int intsused;
int dmacount;
int cyclecount;

int verbose; /* if true, produce long-winded intro */


onintr()
{
	specialbreak = 1;
	signal(2,onintr);
	signal(0,0);
}

isonum(ch)
char ch;
{
	return((ch >= '0') && (ch <= '7'));
}

isaonum(ch)
char ch;
{
	return(isalpha(ch) || isonum(ch));
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*       The following functions are just for displaying what is           */
/*  happening. They attempt to create a visual window into the simulated   */
/*  computer. They use various cursor addressing facilities of VC404       */
/*  terminals.                                                             */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

position(line,column)
int line,column;
{
	move(line,column);
}

slowdown()
{
	int i;
#define delaych 0177
	/*         The terminal takes a finite time to process some cntrl-characters. */
	/*        Provide a delay by sending the terminal a set of null characters.   */

	for(i=1;i<=slowfactor;i++) putchar(delaych);
}

clrendline()
{

	clrtoeol();
}


clearscreen()
{
	clear();
}


static int standouton = 0;

invertvideo()
{

	if(standouton) { standouton = 0; standend(); }
	else { standouton = 1; standout(); }
}



delayn(n)
int n;
{
	int i;
	/* simplest reliable way of getting controllable delays seems      */
	/* to be to send a stream of null characters to the terminal       */

	for(i=1;i<=n;i++) slowdown();
}


waitforit()
{       int ch;
	position(waitrow,waitcol);
	printw("To continue press");
	invertvideo();
	printw("RETURN");
	invertvideo();
	refresh();
	do
	{
	ch = getchar();
	switch(ch) {
		case 'q' : running = false; break;
		case 'b' : specialbreak = true;  /* and switch off singlestep */
		case 'c' : singlestep = false; break;

		}
	} while(ch !='\n');
	position(waitrow,waitcol);
	clrendline();
	refresh();
}



box(row,col,size)
int row,col,size;
{
	int i;

	/* Want to have a field on screen @row, col, of length size      */
	/* where will be having some datum displayed.                    */
	/* Draw a box around that field                                  */
	position(row-1,col-1);
	addch('+');
	for(i=1;i<=size;i++) addch('-');
	addch('+');
	position(row,col-1);
	addch('|');
	position(row,col+size);
	addch('|');
	position(row+1,col-1);
	addch('+');
	for(i=1;i<=size;i++) addch('-');
	addch('+');
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*               Utility functions not really part of the                  */
/*                      simulator                                          */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */




dumpcore(dumpfile,needregisters)
FILE *dumpfile;
int needregisters;
{
	int  i,j,skip,data,anywritten;

	/* This procedure produces a dump, in octal, of the              */
	/* contents of memory, also, of registers                        */
	/* Large areas of empty memory (i.e. zeros) are omitted from     */
	/* the generated listing.                                        */



	if(needregisters)  {
		fprintf(dumpfile,"\nacc : %4o",acc);
		fprintf(dumpfile,"    pc  : %4o",pc);
		fprintf(dumpfile,"    link: %1o\n",link8);
		fprintf(dumpfile,"Time: %8d*10^-7 seconds, Instruction Count: %8d.\n\n\n",time8,icount);
	}

	i = 0;
	anywritten = false;
	skip = false;


	fprintf(dumpfile,"\n\nAddress                        Contents\n\n");

	while (i< coresize)  {

		data = false;
		for(j=i;j<=(i+7);j++) data = data || (store[j] != 0);

		if(data)  {
			if(skip && anywritten)
				fprintf(dumpfile,"          .     .     .     .     .     .     .     .\n");
			fprintf(dumpfile,"%4o :",i);
			for(j=i;j<=(i+7);j++) fprintf(dumpfile,"  %4o",store[j]);
			fputc('\n',dumpfile);
			anywritten = true;
			skip = false;
		}
		else skip = true;

		i = i+8;

	}
}



/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */



highlight(atrow,atcol,value,howbig,howlong)
int atrow,atcol,value,howbig,howlong;
{
	/* draw attention to some particular field containing an octal number */
	if(regdisplay)  {
		/* at position */
		position(atrow,atcol);

		/* inverse video to highlight */
		invertvideo();

		/* write the value, in a field size defined by howbig */
		printw("%*o",howbig,value);
		/* pause a while */
		refresh();
		delayn(howlong);

		/* restore to normal, repeat sequence really */
		position(atrow,atcol);
		invertvideo();
		printw("%*o",howbig,value);
		refresh();
	}
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

lookup(want,st,ptr)
int want,st;
int *ptr;
{
	int i,found;

	/* look through symbol table for a symbol whose value field      */
	/* equals "want" & whose symtype equals "st", if find it       */
	/* return true & pass back index number in table where it is   */

	i = 0;
	found = false;
	while ((i < numsym))  {


		if((want==symtab[i].value) && (st==symtab[i].stype))  {
			found = true;
			*ptr = i;
			break;
		}
		i++;
	}
	return(found);

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

memshow(loc, instruction)
int loc,instruction;
{
	int i,l;
	for(i= -2;i<=2;i++) {
		l = loc+i;
		if(l<0)  l = l+coresize;
		l = l % coresize;
		if(instruction)  position(mem1row+i,mem1col);
		else position(mem2row+i,mem2col);
		if(l==loc)  invertvideo();
		printw("%4o",l);
		if(l==loc)  invertvideo();
		printw("  %4o",store[l]);
	}
	refresh();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

frommemory(instruction)
int instruction;
{
	int disp;
	/*       mdr =  store[mar];                                       */
	if((mar>=0) && (mar<=corelimit))  {
		disp = (instruction && progdisplay) || ((! instruction) && datadisplay);

		if(disp)  {
			memshow(mar,instruction);
			if(instruction)  highlight(mem1row,mem1col+6,store[mar],4,memchng);
			else highlight(mem2row,mem2col+6,store[mar],4,memchng);
		}
		mdr = store[mar];
		time8+= 10;
	}
	else {
		position(mem0row,0);
		clrendline();
		printw("Illegal Memory Reference.");
		refresh();
		delayn(200);
		clearscreen();
		printw("Reference to location %4o\n",mar);
		printw("Address outside of memory of simulated machine.\n");
		refresh();
		delayn(100);
		printw("Program will be terminated after you have examined\n");
		printw("machine state with break functions.\n");
		refresh();
		running = false;
		aborted=true;
		delayn(100);
	}
	memcycle();

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

tomemory(instruction)
int instruction;
{
	int disp;
	/*       store[mar]  =  mdr                                       */;
	if((mar>=0) &&  (mar<=corelimit))  {
		disp = (instruction && progdisplay) || ((! instruction) && datadisplay);

		if(disp)  memshow(mar,instruction);

		store[mar] = mdr;

		if(disp)  {
			if(instruction)  highlight(mem1row,mem1col+6,store[mar],4,memchng) ;
			else highlight(mem2row,mem2col+6,store[mar],4,memchng);
		}
		time8 += 10;
	}
	else {
		position(mem0row,0);
		clrendline();
		printw("Illegal Memory Reference.");
		refresh();
		delayn(200);
		clearscreen();
		printw("Reference to location %4o\n",mar);
		printw("Address outside of memory of simulated machine.\n");
		refresh();
		delayn(100);
		printw("Program will be terminated after you have examined\n");
		printw("machine state with break functions.\n");
		refresh();
		running = false;
		aborted=true;
		delayn(100);
	}
	memcycle();

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*               Things of significance really start about here.           */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

fetchinstruction()
{
	/*       Instruction fetch,
 copy pc to memory address register,
access specified memory location, placing copy of its
  contents in memory data register,
 copy from memory data register to instruction register
  prior to instruction decoding.
 increment program counter (limiting to 12 bits)

	 */

	if(regdisplay)  {
		position(tirow,ticol);
		printw("%8d",time8);
	}

	if(dcddisplay)  {
		position(dirow,dicol);
		clrendline();
	}

	highlight(pcrow,pccol,pc,4,10);

	mar = pc;
	frommemory(true);

	ir = mdr;

	pc = (pc+1) & mask7777;

}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


decodeaddress(executing,display)
int executing,display;

{
	int ptr;
	/*
 *** note that this routine is called from debug system
 *** (via decodeinstruction); then real indirection cycle
 *** shouldn"t be performed.
 */


	/* Pick out the bit that indicates if indirection required */
	indirect = (mask400 == (ir & mask400));

	/* Pick out the bit that says if page 0 or current page */
	page0 = (0 == (ir & mask200));


	/* Pick out the bits that define location within page */
	location =  ir & mask177;

	/* Construct address by taking location & page-number,
 if(page0)  the location is the absolute address, otherwise we
 need to split out from program counter the bits that identify
 current page & "or" these in . */

	if(page0)  mar = location;
	else mar = (pc & mask7600) | location;

	if(display)  {
		if(indirect)  printw(" i ");
		else printw("   ");
		if(lookup(mar,label8,&ptr))  printw("%6s",symtab[ptr].name);
		else  printw("%4o  ",mar);
		refresh();
	}

	/* Now determine if indirection needed. If so, go do it */

	if(executing && indirect)  {

		/* deal with special case of autoincrementing memory registers */
		/* & treat as a special case! */
		if((8<= mar) && (mar <=15))  {
			store[mar] = (store[mar]+1) & mask7777;
		}

		frommemory(false);

		mar = mdr;
		time8 += 2;
	}

}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


decodeinstruction(executing,display)
int executing,display;
{
	int temp,ptr;
	/* *** Note that this routine also called from debug package     */
	/* when want dump of memory as instructions                      */

	/* First, break out the opcode*/

	temp = ir & mask7000;
	opcode = temp >> 9;

	/* Now, decide how to deal with rest of data in instruction
 register. If its a Memory Reference Instruction, then want
 the mar to hold the correct address, allowing for indirection etc.


  If the opcode is an IOT, then want device & function identified.

 If the opcode is an operate, the want oprbits set.

					  */

	if(executing && display)  position(dirow,dicol);

	switch( opcode ) {
	case and8:
	case tad:
	case dca:
	case isz:
	case jms:
	case jmp :

		if(display)
			switch( opcode) {
			case and8:
				printw("and");
				break;
			case tad:
				printw("tad");
				break;
			case  dca:
				printw("dca");
				break;
			case  isz:
				printw("isz");
				break;
			case jms:
				printw("jms");
				break;
			case jmp:
				printw("jmp");
				break;
			}

		refresh();
		/* Memory address class, translate rest of word */
		decodeaddress(executing,display);

		break;

	case iot :


		temp = ir & mask770;
		device = temp >> 3;
		inst = mdr & mask7;
		if(display)  {
			if(lookup(ir,iot8,&ptr))  printw("%6s      ",symtab[ptr].name);
			else  printw("iot %2o %1o     ",device,inst);

			refresh();
		}


		break;
	case    opr :

		oprbits = ir & mask777;
		if(display)  {
			if(lookup(ir,opr8,&ptr))  printw("%6s      ",symtab[ptr].name);
			else
				printw("opr %3o     ",oprbits);
			refresh();



		}

		break;
	}
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*       postevent,                                                        */
/*               record an event that is to be signalled sometime later    */
/*       by external event procedure. "events" kept ordered by time        */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
postevent(itype,btime,rtime)
int itype,btime,rtime;

{
	int j,i,ir,t;
	if(numevents==elim)  {
		running = false;
		clear();
		printw("Event queue for peripheral devices has overflowed!\n");
		printw("WHAT ARE YOU DOING????????\n");
		refresh();
	}
	else {

		if(rtime != 0)  ir = rand() % rtime;
		else ir = 0;
		t = time8+btime+ir;
		i = 1;
		while ((i<=numevents) & (t > etime[i]))  i++;
		j = numevents;
		while ((j>=i))  {
			etime[j+1] = etime[j];
			etype[j+1] = etype[j];
			j--;
		}
		numevents++;
		etime[i] = t;
		etype[i] = itype;
	}
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

showperiphs(dev)
int dev;
{
	if(perdisplay)  {
		switch( dev ) {
		case d00 :
			position(d00row,d00col);
			clrendline();
			if(ionf==1)  printw("(interrupts enabled)");
			break;
		case d03 :
			position(d03row,d03col);
			printw("                           ");
			position(d03row,d03col);
			if(d03f==1) printw("Keyboard flag set, data %3o",d03b);
			break;
		case d04 :
			position(d04row,d04col);
			printw("                           ");
			position(d04row,d04col);
			if(d04f==1) printw("Teletype flag set, data %3o",d04b);
			break;
		case d40 :
			position(d40row,d40col);
			clrendline();
			if(d40f==1)  printw("Disc seek flag set.");
			break;
		case d41 :
			position(d41row,d41col);
			clrendline();
			if(d41f==1)   printw("Disc transfer flag set.");
			break;
		case d50 :
			position(d50row,d50col);
			clrendline();
			if(d50f==1)  printw("Clock flag set.");
			break;
		case d60 :
			position(d60row,d60col);
			clrendline();
			if(d60f==1)   printw("A/D flag set, data %4o",d60b);
			break;


		}
	refresh();
	}

}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*      Some special variables etc for new "disc"                          */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

int discpos = 0; /* where "heads" are */
int discfun;

startdma(readonly)
int readonly;
{
	dmacount = 128;
	if(readonly) {
if(!(
  (0==access(".8.disc.1",4)) ||
  (0==access("/usr/pub/cs/211/data/disc",4))
)
)
		{
		running = false;
		aborted = true;
		position(20,0);
		printw("disc access error\n");
		refresh();
		}
	}
	else {
		if(!(0==access(".8.disc.1",2))) {
		running = false;
		aborted = true;
		position(20,0);
		printw("disc access error\n");
		refresh();
		}
		}
	discfun = readonly;
}

dotransfer()
{       int ok;
	int i,j,k;
	FILE *f;
	ok = 1;
	if(discfun) { /* read */
		f=fopen(".8.disc.1","r");
		if(f==NULL) f=fopen("/usr/pub/cs/211/data/disc","r");
		ok = ok && (f != NULL);
		if(ok) {
		fseek(f,512*(d40b & 017),0);
		j = d41b % coresize;
		for(i=1;i<=128;i++) {
		  ok= ok && (1==fread(&k,4,1,f));
		  if(!ok) break;
		  store[j] = k & 07777;
		  j = (j+1) % coresize;
		  }
		fclose(f);
		}
	}
	else { /* write */
		f=fopen(".8.disc.1","a");
		ok = ok && (f != NULL);
		if(ok) {
		fseek(f,512*(d40b & 017),0);
		j = d41b % coresize;
		for(i=1;i<=128;i++) {
		  k = store[j] & 07777;
		  ok= ok && (1==fwrite(&k,4,1,f));
		  if(!ok) break;
		  j = (j+1) % coresize;
		  }
		fclose(f);
		}
	}
	if(!ok) {
		running = false;
		aborted = true;
		position(20,0);
		printw("disc transfer error\n");
		refresh();
		}
	else postevent(d41,20,0);

}

dodma()
{
	if(perdisplay) {
		position(dmarow,dmacol);
		invertvideo();
		printw("D.M.A.");
		invertvideo();
		refresh();
		delayn(10);
		position(dmarow,dmacol);
		clrendline();
		refresh();
		}
	time8 += 10;
	dmacount--;
	if(dmacount==0) dotransfer();
}

memcycle()
{
	cyclecount++;
	if((cyclecount % 2) && (dmacount > 0)) dodma();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                                                                         */
/*       Input-Output,                                                     */
/*                                                                         */
/*       I/O devices:                                                      */
/*              00       cpu status                                        */
/*              03       keyboard                                          */
/*              04       teletype                                          */
/*              40      disc                                             */
/*              41)                                                 */
/*              50       clock                                             */
/*              60       a/d                                               */
/*       "device" 0                                                        */
/*                     cpu status                                          */
/*               6001    enable interrupts                                 */
/*               6002    disable interrupts                                */
/*       "device" 03    (keyboard)                                         */
/*               6031   ksf, keyboard skip flag                            */
/*               6032   clear flag & accumulator                         */
/*               6034   read buffer into acc                               */
/*               N.B. krb 6036 combines 6032, 6034                         */
/*       "device" 04    (teletype)                                         */
/*               6041   tsf                                                */
/*               6042   tcf                                                */
/*               6044   load tty buffer                                    */
/*               N.B. tls 6046 combines 6042, 6044                         */
/*       "device" 50    (clock)                                            */
/*               6501    clear clock flag (starts clock counting down again) */
/*               6502    skip if clock flag set                            */
/*               6504    start/stop clock                                  */
/*                                                                         */
/*       "device" 60                                                       */
/*                       a/d converter                                     */
/*               6601    clear data flag & read into acc                 */
/*               6602    skip if data ready flag set                       */
/*                                                                      */
/*      "device 40/41 " disc */
/*      6400   load disc block number and start seek */
/*      6401   skip if disc seek complete */
/*      6402   clear disc seek flag */
/*      6410   load disc mem address */
/*      6411   start read from disc */
/*      6415   start write to disc */
/*      6412    skip if transfer complete flag set */
/*      6414    clear disc transfer complete flag */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


inputoutput()
{
	int temp;
	if(device==d00)  {
		if(inst & 2)  ionf = 0;
		if(inst & 1)  {
			/* setting of interrupts on must be deferred for */
			/* one instruction, */

			intsused = true; /* flag that using interrupts */
			idelay=1;
		}
		showperiphs(d00);
	}
	else
	if(device==d03)  {
		time8 += 26;
		if((inst & 1) && (d03f==1))  {
			pc++; /* skip if flag set */
			time8 += 2;
			highlight(pcrow,pccol,pc,4,regchng);
		}
		if(inst & 2)  {
			d03f = 0;
			acc = 0;
			highlight(accrow,acccol,acc,4,regchng);
			showperiphs(d03);
			}
		if(inst & 4)  {
			acc |= d03b;
			highlight(accrow,acccol,acc,4,regchng);
			d03b = 0;
		}
	}
	else
	if(device==d04)  {
		time8 += 26;
		if((inst & 1) && (d04f==1))  {
			pc++; /* skip if flag set */
			time8 += 2;
			highlight(pcrow,pccol,pc,4,regchng);
		}
		if(inst & 2)  {
			d04f = 0;
			showperiphs(d04);
		}
		if(inst & 4)  {
			temp = d04b | acc;
			d04b = temp & mask177;
			postevent(d04,times[fast][2],rtimes[fast][2]);
		}
	}
	else
	if(device==d40)  {
		int distance;
		time8 += 20;
		distance = abs(discpos  - (acc & 017));
		if(inst == 0) {
			d40b=acc;
			postevent(d40,distance*times[fast][5],rtimes[fast][5]);
			}
		if((inst == 1) && (d40f==1))  {
			pc++; /* skip if flag set */
			time8 += 2;
			highlight(pcrow,pccol,pc,4,regchng);
			}
		if(inst == 2)  {
				d40f = 0;
				showperiphs(d40);
			}
		}
		else
		if(device==d41)  {
			time8 += 20;
			if(inst==0) d41b=acc;
			if((inst == 2) && (d41f ==1)) {
				pc++; /* skip if flag set */
				time8 += 2;
				highlight(pcrow,pccol,pc,4,regchng);
			}
			if(inst == 4)  {
				d41f = 0;
				showperiphs(d41);
			}

			if(inst == 1)  {

				startdma(true);

			}
			if(inst == 5) startdma(false);
		}       else
				if(device==d50)  {
					time8 += 20;
					if(inst & 1)  {
						/* clock tick, clear flag, postevent for another tick */
						d50f = 0;
						showperiphs(d50);
						postevent(d50,times[fast][3],rtimes[fast][3]);
					}
					if((inst & 2) && (d50f==1))  {
						pc++; /* skip if set */
						time8 += 2;
						highlight(pcrow,pccol,pc,4,regchng);
					}
					if(inst & 4)  {
						if(clockoff)  {
							clockoff = false;
							postevent(d50,(times[fast][3]/ 3),rtimes[fast][3]);
						}
						else clockoff = true;
					}
				}
				else
					if(device==d60)  {
						time8 += 20;
						if(inst & 1)  {
							/* a/d conversion done, clear flag read buffer */
							d60f = 0;
							showperiphs(d60);
							acc = d60b;
						}
						if((inst & 2) && (d60f==1))  {
							pc++; /* skip if set */
							time8 += 2;
							highlight(pcrow,pccol,pc,4,regchng);
						}
						if(inst & 4 )
							postevent(d60,times[fast][4],rtimes[fast][4]);

					}
					else {
						clearscreen();

						printw("Unimplemented Input Output Instructions.\n");
						printw("I/O instruction specified  device #%2o, & operation #%1o\n",device,inst);
						running = false;
						refresh();
					}
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*       External event                                                    */
/*               entered during each instruction cycle                     */
/*               compares count of instructions with times in event queue  */
/*               & performs tasks as required                            */
/*                                                                         */
/*       (some minor problems, if interrupts being used (intsused==true)    */
/*       then want missed kbd, clock etc to count & be errors            */
/*       but if not using interrupts dont want to loose keyboard characters */
/*              (but see comments in"exec8", that correct part of simulation */
/*      had to be suppressed (far too easy to miss characters because of speed up */
/*      of periphs)             */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

externalevent()
{

	int i,j,temp;
	char ch;



	i = 1;
	while ((i <= numevents) && (time8 >= etime[i]))  {
		switch( etype[i] )
		{
		case  d03:
			if(d03f==0)   {
				d03f = 1;
				/* Keyboard, flag now set, data ready */
				/* "or" into kbd-buffer */
				fscanf(kbd,"%c",&ch);
				temp = ch | d03b;
				d03b = temp & mask177;
				if(!feof(kbd))  postevent(d03,times[fast][1],rtimes[fast][1]);
				showperiphs(d03);

			}
			else
			if(d03f==1)  postevent(d03,(times[fast][1]),0);
			break;
		case  d04:

				/* tty ready, do real output of data */
				fputc(d04b,tty);
				if (d04b != '\n') ttyc += (ttyc < 99) ? 1 : 0;
				else ttyc = 0;
				ttybuff[ttyc] = d04b;
				d04f = 1;
				showperiphs(d04);
				d04b = 0;
				break;

		case d40: /* disc has supposedly sought and found */
				d40f = 1;
				discpos = (d40b & 017);
				showperiphs(d40);
				break;

		case d41: /* disc has supposedly written or read */
				d41f = 1;
				showperiphs(d41);
				break;


		case   d50:
			if(!clockoff)  {
				d50f = 1;
				showperiphs(d50);

			}
			break;
		case   d60:
				d60f = 1;
				d60b = rand() & 0777;
				showperiphs(d60);
				break;

		}

		i++;
	}
	j = 0;
	while ((i <= numevents))  {
		j++;
		etime[j] = etime[i];
		etype[j] = etype[i];
		i++;
	}
	numevents = j;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

evaluateinterrupt()

{

	externalevent();

	/* Lines for devices 03, 04, 50 & 60 are wired to interrupt line */

	interrupt = (ionf==1) && ((d40f == 1) || (d41f == 1) || (d50f==1) || (d60f==1) || (d04f==1) || (d03f==1));
	if(interrupt)  {
		if(reglog)  {
			if(!dumpopen)  {
				dumpfile = fopen("dumpfile","w");
				if(dumpfile==NULL)
				{
					clear();
					printw("\n ?\nI couldn't open the 'dumpfile'!\n");
					refresh();
					sleep(1);
					endwin();
					exit(0);
				}
				dumpopen = true;
				fprintf(dumpfile,"Log of Registers at interrupts:\n");

			}

			fprintf(dumpfile,"Interrupt;  pc %4o",pc);
			if(d40f==1)  fprintf(dumpfile," Disc seek\n");
			if(d41f==1)  fprintf(dumpfile," Disc transfer\n");
			if(d50f==1)  fprintf(dumpfile," Clock\n");
			if(d60f==1)  fprintf(dumpfile," A/D\n");
			if(d03f==1)  fprintf(dumpfile," kbd\n");
			if(d04f==1)  fprintf(dumpfile," tty\n");

			fprintf(dumpfile,";\ntime %8d; Instruction Count %8d\n",time8,icount);

		}
		mdr = pc;
		mar = 0;
		tomemory(true);
		pc = 1;
		highlight(pcrow,pccol,pc,4,regchng);
		if(perdisplay)  {
			position(d00row,d00col);
			clrendline();
			invertvideo();
			printw("INTERRUPT");
			invertvideo();
			refresh();
		}
		ionf = 0;
	}

}




/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*       procedures rotateleft & rotateright                             */
/*               provide somewhat clumsy mechanisms for treating           */
/*               acc & link as a single 13-bit register and performing   */
/*               circular shifts on contents thereof                       */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
rotateleft(doubles)
int doubles;
{
	int temp;
	if(doubles)  rotateleft(false);
	time8++;
	temp =  acc << 1; /*  shift left */
	acc = temp & mask7777; /* mask out relevant bits */
	acc |= ( link8 & 1); /* bring link bit end around */
	if(temp > 4095)  link8 = 1;
	else link8 = 0;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
rotateright(doubles)
int doubles;
{
	int temp;

	if(doubles)  rotateright(false);
	time8++;
	temp =  acc & 1; /* pick of bit that will shift into link */
	acc = acc >> 1;   /* divide to achieve shift right */
	acc |=  (link8 << 11); /* add in link with correct offset */
	link8 = temp;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

operate()
{
	/* In principle, there are 9 bits available to identify
 operate class instructions, supposedly therefore allowing some
 500 such instructions. It don"t work that way.
  Operate instructions are "microcoded". Specific bits
 are used to designate specific instructions etc.
 So only have a few valid possibilites.
  Essentially, there are two classes of operate instructions.
 Group-1 instructions manipulate the contents of the accumulator
 and link. Group-2 instructions are primarily concerned with
 testing operations.


 Group-1:

      0   1   2   3   4   5   6   7   8   9  10  11
    +---+---+---+---+---+---+---+---+---+---+---+---+
    | 1 | 1 | 1 | 0 |cla|cll|cma|cml|rar|ral|0/1|iac|
    +---+---+---+---+---+---+---+---+---+---+---+---+
    |< op-code >| ^                           ^
		  |                           |
		  |                      0 : rotate one place
		  |                      1 : rotate two places
	     zero here
	   specifies group 1


  The microcoding can be used to combine the basic
 set of operate instructions; thus, in a single instruction
 one could for example clear the accumulator (cla), clear the
 link (cll) & increment the accumulator (iac).


 Group-2:

      0   1   2   3   4   5   6   7   8   9  10  11
    +---+---+---+---+---+---+---+---+---+---+---+---+
    | 1 | 1 | 1 | 1 |cla|sma|sza|snl| 0 |osr|hlt| 0 |
    |   |   |   |   |cla|spa|sna|szl| 1 |osr|hlt|   |
    +---+---+---+---+---+---+---+---+---+---+---+---+
    |< op-code >| ^   /interpretation\            ^
		  |   |depends on bit |           |
		  |   |   8.          |           zero for group
		  |                               2.
		 one here
		specifies group 2.



				  */

#define cla  128        /* These are the decimal values corresponding */
#define cll  64         /* to those operate instructions that we implement */
#define cma  32
#define cml  16
#define rar  8
#define ral  4
#define shifttwice 2
#define iac  1
#define sma  64
#define spa  64
#define sza  32
#define sna  32
#define snl  16
#define szl  16
#define skp  8
#define hlt  2


	int toskip;




	if(mask400 & oprbits)
	{
		/* Its a Group-2 skip type instruction. */
		/* Time 1, evaluate the skip conditional */
		toskip = false;

		if(skp & oprbits)  {
			/* the AND group & unconditional skip */
			toskip = true; /* unconditional */

			/* Slightly oddly expressed test for
		     positive acc reflects twos complement
		     arithmetic used in PDP-8 */
			if(spa & oprbits)
				toskip = toskip && (acc < 2048);

			if(sna & oprbits)
				toskip = toskip && (acc != 0);

			if(szl & oprbits)
				toskip = toskip && (link8==0);

		}
		else    {
			/* the OR group */

			/* slighly oddly expressed test for
		    minus acc reflect twos complement
		    arithmetic used in PDP-8 */
			if(sma & oprbits)
				toskip = toskip || (acc >= 2048);

			if(sza & oprbits)
				toskip = toskip || (acc == 0);

			if(snl & oprbits)
				toskip = toskip || (link8 != 0);

		}

		/* If appropriate, increment the pc */

		if(toskip)  {
			time8 += 2;
			pc = (pc+1) & mask7777;
		}

		/* Time 2, maybe clear acc */

		if(cla & oprbits)  {
			acc = 0;
			highlight(accrow,acccol,acc,4,regchng);
		}

		/* Time 3, maybe halt */

		if(hlt & oprbits)  {
			running = false;
		}


	}
	else    {
		/* Bit 3 is zero, its a group-1 microinstruction */


		/* First time sequence, cla, cll */

		if(cla & oprbits)  {
			acc = 0;
			highlight(accrow,acccol,acc,4,regchng);
		}

		if(cll & oprbits)  {
			link8 = 0;
			highlight(linkrow,linkcol,link8,1,regchng);
		}


		/* 2nd time sequence, complement acc & link */

		if(cma & oprbits)  {
			acc = (~acc) & 07777;
			highlight(accrow,acccol,acc,4,regchng);
		}

		if(cml & oprbits)  {
			if(link8==1)  link8=0;
			else link8 = 1;
			highlight(linkrow,linkcol,link8,1,regchng);
		}

		/* 3rd time sequence, increment acc */

		if(iac & oprbits)  {
			time8 += 2;
			acc = (acc+1) & mask7777;
			highlight(accrow,acccol,acc,4,regchng);
		}

		/* 4th time sequence, rotates */

		if(ral & oprbits)  {
			rotateleft(shifttwice & oprbits);
			highlight(accrow,acccol,acc,4,regchng);
			highlight(linkrow,linkcol,link8,1,regchng);
		}
		else
			if(rar & oprbits)  {
				rotateright(shifttwice & oprbits);
				highlight(accrow,acccol,acc,4,regchng);
				highlight(linkrow,linkcol,link8,1,regchng);
			}
	}
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

executeinstruction()
{


	switch( opcode) {
	case and8:

		/* The AND instruction causes a bit-by-bit
		   Boolean & operation between the contents of
		   the accumulator & the data word specified
		   by the instruction. The result is left in the
		   accumulator.
			     */
		frommemory(false);
		if(! aborted)  {
			acc =  acc & mdr;
			highlight(accrow,acccol,acc,4,regchng);
		}
		break;


	case  tad:

		/* TAD perfomrs addition between the
		   specified data word & the contents of the
		   accumulator leaving the result of the addition
		   in the accumulator. If a carry out of the
		   most significant bit of the accumulator should
		   occur then the link bit is complemented.
		 */
			frommemory(false);
			if(!aborted)  {
				acclink = acc+mdr;
				if(acclink>4095)  {
					/* have carry bit, complement link */
					if(link8==1)  link8=0;
					else link8 = 1;
					highlight(linkrow,linkcol,link8,1,regchng);
				}

				acc = acclink & mask7777;
				highlight(accrow,acccol,acc,4,regchng);

			}
			break;


	case isz:

		/* The ISZ instruction adds a 1 to the
		referenced data word & then examines the result
		of the addition. If a zero result occurs, the
		instruction following the ISZ is skipped. If the
		result is not zero, the instruction following the
		ISZ is performed. In either case, the result of
		the addition replaces the original data word in
		 memory.
	    */
			frommemory(false);
			if(!aborted)  {
				acclink = mdr+1;
				if(acclink > 4095)  mdr = 0;
				else mdr = acclink;
				tomemory(false);
				if(mdr==0)  {
					time8 += 2;
					pc++;
					highlight(pcrow,pccol,pc,4,regchng);
				}
			}
			break;



	case dca:

	   /* DCA, deposit & clear accumulator.
	   The DCA instruction stores the contents of the
	   acc in the referenced location, destroying the
	   original contents of the location. The acc
	   is the set to zero
	    */
			mdr = acc;
			tomemory(false);
			if(!aborted)  {
				acc = 0;
				highlight(accrow,acccol,acc,4,regchng);
			}
			break;

	case  jms:
	/* JMS, Jump to subroutine.
	The value of the pc (the address of the JMS instruction
	+1) is always stored in the first location of the
	subroutine, replacing its original contents;

	   Program control is always transferred to the location
	of the operand + 1 (second location of the subroutine).

	*/

			mdr = pc;
			tomemory(true);
			if(!aborted)  {
				pc = mar+1;
				highlight(pcrow,pccol,pc,4,regchng);
			}
			break;


	case  jmp:

	  /* JMP, jump (goto) instruction.
	   Loads effective address calculated during
	   instruction decode into the program counter pc.
	   */
			pc = mar;
			highlight(pcrow,pccol,pc,4,regchng);
			break;

	case iot:
		inputoutput();
		break;



	case opr:
		operate();
		break;
	}
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

initialize()
{
	int i;

	slowfactor = 2; /* temporary set at a default value */

	/* VC404 terminals take time to respond to some characters */
	/* so fill with nulls, appropriate number depends on       */
	/* whether have 300 baud phone in line, 1200 normal or     */
	/* 19.2k fast line, more nulls needed for faster terminals */
	/* also depends on load on machine etc etc                 */


	/* Need events mechanism initialized in order to set up keyboard correctly */
	numevents = 0;
	srand(1);



	if (0==access(".8.kbd.1",4)) {

		kbd = fopen(".8.kbd.1","r");
		if(kbd==NULL)
		{
			printf("I couldn't find the .8.kbd.1 file with the keyboard input.\n");
			exit(0);
		}
		postevent(d03,1250,500);

	}



	tty = fopen(".8.tty.1","w");
	if(tty==NULL)
	{
		printf("\n ?\nI couldn't open the '.8.tty.1' output file!\n");
		exit(0);
	}
	regdisplay = false;
	progdisplay = false;
	datadisplay = false;
	dcddisplay = false;
	singlestep = false;
	perdisplay = false;
	reglog = false;
	fast = 0;
	for(i = 0;i<= corelimit ;i++)    breakpts[i] = false;

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

createdisplay()
{
	/* Display is fairly static, so can put up some standard         */
	/* headers, boxes for data etc                                   */

	clearscreen();
	if(regdisplay)  {
		position(0,0);
		invertvideo();
		printw("C.P.U.");
		invertvideo();

		box(accrow,acccol,4);
		position(accrow,acccol-6);
		invertvideo();
		printw("acc");
		invertvideo();

		box(linkrow,linkcol,1);
		position(linkrow,linkcol-6);
		invertvideo();
		printw("link");
		invertvideo();

		box(pcrow,pccol,4);
		position(pcrow,pccol-6);
		invertvideo();
		printw("pc");
		invertvideo();

		position(tirow,ticol-6);
		printw("Time :%8dx 10  seconds",time8);
		position(tirow-1,ticol+13);
		printw("-7");

	}

	if(progdisplay || datadisplay)  {

		position(mem0row-1,0);
		printw("______________________________________________________________________\n");

		position(mem0row,0);
		invertvideo();
		printw("MEMORY");
		invertvideo();

		if(progdisplay)  {
			position(mem1row-4,mem1col-1);
			printw("Instructions");
			position(mem1row-3,mem1col-3);
			printw("Address  Contents");
		}
		if(datadisplay)  {
			position(mem2row-4,mem2col+2);
			printw("Data");
			position(mem2row-3,mem2col-3);
			printw("Address  Contents");
		}
	}

	if(perdisplay)  {

		position(periphrow-1,0);
		printw("______________________________________________________________________\n");

		position(periphrow,0);
		invertvideo();
		printw("PERIPHERAL DEVICES");
		invertvideo();
		showperiphs(d00);
		showperiphs(d03);
		showperiphs(d04);
		showperiphs(d50);
		showperiphs(d60);
	}

	refresh();
	if(regdisplay)  {
		highlight(accrow,acccol,acc,4,10);
		highlight(linkrow,linkcol,link8,1,10);
		highlight(pcrow,pccol,pc,4,10);
	}

	if(progdisplay)  {
		memshow(pc,true);
	}
	refresh();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

dumpinitial()
{

	dumpopen = false;

		dumpfile = fopen("dumpfile","w");
		if(dumpfile==NULL)
		{
			printf("\n ?\nI couldn't open the 'dumpfile'!\n");
			exit(0);
		}

		fprintf(dumpfile,"Printout of contents of memory prior to program execution.\n\n");


		dumpcore(dumpfile,false);
		dumpopen = true;

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

finaldump()
{

		if(!dumpopen)
		{
			dumpfile = fopen("dumpfile","w");
			if(dumpfile==NULL)
			{
				clear();
				printw("\n ?\nI couldn't open the 'dumpfile'!\n");
				refresh();
				sleep(2);
				endwin();
				exit(0);
			}
		}
		else  fprintf(dumpfile,"\n\n\n");

		fprintf(dumpfile,"Printout of contents of registers & memory subsequent to program execution.\n\n\n");


		dumpcore(dumpfile,true);

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

baddata(i)
int i;
{
	int j;

	printw("Malformed command.\n");
	printw("%s\n",userinput);
	for(j=0;j<=i;j++) addch(' ');
	printw("|\n");
	printw("Error detected near marker.\n");
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

findname(wh,val)
int *wh,*val;
{
	char ch;
	char uname[6];
	int found,j;

	found = false;
	for(j=0;j<6;j++) uname[j]='\0';
	ch = userinput[*wh];
	j = 0;
	while (isalnum(ch))  {

		if((j<6))  uname[j]=ch;
		j++;
		(*wh)++;
		ch = userinput[*wh];
	}

	if((0<j) && (j<=6))  {
	  j = 0;
	  while (j<numsym)  {

	   if((symtab[j].stype==label8) && (0==strncmp(symtab[j].name,uname,6)))  {
			    found = true;
			    *val = j;
			    break;
		       }
	   j++;
	   }
	}

	return(found);
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

findoct(where,val)
int *where,*val;
{
	int ch,temp;
	temp = 0;
	ch = userinput[*where];
	while (isonum(ch))  {
		temp = 8*temp + (ch - '0');
		(*where)++;
		ch = userinput[*where];
	}
	*val = temp & mask7777;

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

getusercommand()
{
	int i;
	char ch;

	for(i=1;i<79;i++) userinput[i]=' ';
	userinput[0] = '?';
	userinput[79] = '?';
	chcnt = 0;

	while((ch=getch()) != '\n')
		if(ch != ' ')
			if(chcnt < 79) userinput[chcnt++] = ch;


}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

setbreak(setting)
int setting;
{
	int n,addr,ptr;

	printw("Address for breakpoint>\n");
	refresh();
	getusercommand();
	n = 0;
	if(isonum(userinput[0]))  {
		findoct(&n,&addr);
		if((0 <= addr) & (addr <= corelimit))  breakpts[addr]=setting;
		else printw("Address outside of memory.\n");
		refresh();
	}
	else
	if(isalpha(userinput[0]))  {
		if(findname(&n,&ptr))  {
			addr = symtab[ptr].value;
			breakpts[addr] = setting;
		}
	else {
		baddata(n);
		printw("(Possibly you have mistyped an identifier name.\n");
		refresh();
		}
	}
	else {
	baddata(1);
	printw("You must just specify an address, i.e. one of the labels in\n");
	printw("your program or an octal constant.\n");
	refresh();
}
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

sub4break(flag)
int *flag;
{
	int i,j;
	char ch;


	ch = userinput[1];

	/* : commands                            */
	switch(ch) {
	case 'd':  dumpcore(stdout,true); break;
	case 'r':
			printw("acc : %4o",acc);
			printw("    pc  : %4o",pc);
			printw("    link: %1o\n",link8);
			printw("Time: %8d*10^-7seconds, Instruction Count: %8d\n",time8,icount);

			refresh();
			break;
	case 'c':  *flag=true;  break;
	case 'b':  setbreak(true); break;
	case 'u':  setbreak(false); break;
	case 's':
		printw("User symbols:\n");
		j = 0;
		for( i = 0;i<numsym ;i++)
		    if(symtab[i].stype==label8)  {
			j++;
			printw("%6s  %4o",symtab[i].name,symtab[i].value);

			if(0 == (j % 4)) addch('\n');
			else printw(";  ");
			}
		if(j==0)  printw("(none defined)\n");
		addch('\n');
		refresh();
		break;
	case 'q':
		*flag=true;
		running=false;
		break;
	case 'o':
		if(ttyc>0)
			{
			int i;
			i=0;
			while(i<ttyc) addch(ttybuff[++i]);
			addch('\n');
			refresh();
			}
		else printw("tty line buffer is empty.\n");
		refresh();
		break;
	default:
		printw(" ':' commands are:\n");
		printw(" :?    produce this summary.\n");
		printw(" :b    set breakpoint.\n");
		printw(" :c    continue execution of PDP-8 program.\n");
		printw(" :d    'dump' registers & memory to terminal screen.\n");
		printw(" :o    show characters sent to pseudo-tty.\n");
		printw(" :q    terminate PDP-8 program.\n");
		printw(" :r    'dump' registers to terminal screen.\n");
		printw(" :s    list user defined symbols (labels).\n");
		printw(" :u    undo breakpoint.\n");
		refresh();
			}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

sub3break()
{
	int setting;
	char ch;


	setting = (userinput[0]=='+');
	ch = userinput[1];
	switch(ch) {
	case '*':
		printw("Current display settings:\n");
		printw("C.P.U.                       : %s\n",(regdisplay) ? "TRUE" : "FALSE");
		printw("'Data' Memory Window         : %s\n",(datadisplay) ? "TRUE" : "FALSE");
		printw("'Instructions' Memory Window : %s\n",(datadisplay) ? "TRUE" : "FALSE");
		printw("Status of Peripherals shown  : %s\n",(perdisplay) ? "TRUE" : "FALSE");
		printw("Single step mode             : %s\n",(singlestep) ? "TRUE" : "FALSE");
		printw("Translation of instructions  : %s\n",(dcddisplay) ? "TRUE" : "FALSE");
		refresh();
		break;
	case 'c' :  regdisplay=setting; break;
	case 'd' :  datadisplay=setting; break;
	case 'f' :  fast=(setting & 01); break;
	case 'i' :  progdisplay=setting; break;
	case 'l' :  reglog=setting; break;
	case 'p' :  perdisplay=setting; break;
	case 's' :  singlestep=setting; break;
	case 't' :  dcddisplay=setting; break;
	default :
		printw(" '+' and '-' commands are:\n");
		printw(" ?   produce this summary.\n");
		printw(" *   list current display settings.\n");
		printw(" c   set/reset CPU display.\n");
		printw(" d   set/reset memory display for 'data'.\n");
		printw(" i   set/reset memory display for 'instructions'.\n");
		printw(" l   set/reset logging of registers on interrupts.\n");
		printw(" p   set/reset display for status of peripherals.\n");
		printw(" s   set/reset single step mode.\n");
		printw(" t   set/reset instruction translation display.\n");
		refresh();
	}
}

outch(chi)
int chi;
{
	if((32<=chi) && (chi<=127))  addch(chi);
	else addch('?');
}

sub2break(st,nd,md)
int st,nd;
char md;
{
	int i,j,ptr,v,pcsav;

	pcsav = pc;
	j = 0;
	for(i=st;i<=nd;i++) {

		if(lookup(i,label8,&ptr))  printw("%6s  ",symtab[ptr].name);
		else printw("        ");
		printw("%4o /  ",i);
		v = store[i];
		switch( md ) {
		case  'd' :

			if(v>2047)
				v = -(1+(~v   & mask7777));
			printw("%5d",v);
			break;

		case 'c' :
			outch(v);
			break;
		case 'o' :
			printw("%4o",v);
			break;
		case 'p' :
			outch((v & mask7700) >> 6);
			outch(v & mask77);
			break;
		case 'i' :

			pc = i;
			ir = v;
			decodeinstruction(false,true);
			break;


		}
		j++;
		if(0==(j % 2)) addch('\n');
		else printw("      ");
	}
	addch('\n');
	refresh();

	pc = pcsav;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

sub1break()
{
	int st,n,ptr,rpt,ok;
	char ch;

	ok = true;
	n = 0;
	st = 0;
	ch = userinput[0];

	if(isonum(ch))  findoct(&n,&st);
	else
	if(findname(&n,&ptr))  st = symtab[ptr].value;
	else {
	baddata(1);
	printw("(Possibly you have mistyped an identifier name.\n");
	refresh();
	ok = false;
	}

	if((st<0) || (st>corelimit))  {
		printw("Address lies outside memory of simulated machine.\n");
		refresh();
		ok = false;
	}

	if(ok)  {
		/* deal with optional [,<repeat>]             */

		if(userinput[n] == ',')  {
			n++;
			if(isonum(userinput[n]))  {
				findoct(&n,&rpt);

				if((st+rpt-1) > corelimit)  {
					ok = false;
					printw("Too large a repeat factor!\n");
					refresh();
				}
			}
			else {
				ok = false;
				baddata(n);
				printw("An octal number for repeat factor is required.\n");
				refresh();
			}
		}
		else rpt = 1;

		if(ok)  {
			/* try for /<char>                       */

			if(userinput[n]=='/')  {
			n++;
			ch = userinput[n];
			if((ch=='c') || (ch=='d') || (ch=='i') || (ch=='o') || (ch=='p'))  sub2break(st,st+rpt-1,ch);
			else {
					baddata(n);
					printw("Allowed print formats are :\n");
					printw(" c  one ASCII character/word.\n");
					printw(" d  word value in signed decimal.\n");
					printw(" i  word interpreted as an instruction.\n");
					printw(" o  word value in octal.\n");
					printw(" p  two 6-bit characters packed in word.\n");
					refresh();
				}
			}
			else {
				baddata(n);
				printw(" /<char> expected.\n");
				refresh();
			}
		}

	}

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

breakhelp()
{
	if(userinput[0] != '?') {
		printw("%s\n",userinput);
		printw("Illegal breakpoint command.\n");
	}

	printw("Breakpoint commands:\n");
	printw("  ?        Produces this 'HELP' summary.\n");
	printw("  +/-      Turn on/off various run time displays.\n");
	printw("  :        Set/remove breakpoints, start/resume execution of program.\n");
	printw(" <address>/<char> Print contents of addressed memory location\n");
	printw("                                       in format determined by char.\n");
	printw(" <address>,<repeat-factor>/<char> Print contents of each of a\n");
	printw("                                       range of addressed memory locations.\n");
	refresh();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

breakpoint()
{
	int ptr,done;
	char  ch;
	int row,col;


	if(standouton) { standouton = 0; standend(); }
	clearscreen();
	invertvideo();
	printw("BREAKPOINT\n");
	invertvideo();
	printw("Break address     : ");
	if(lookup(pc,label8,&ptr))  printw("%s\n",symtab[ptr].name);
	else printw("%4o\n",pc);
	done = false;
	refresh();
	do {
		printw("break> \n");
		refresh();
		getusercommand();
		getyx(stdscr,row,col);
		if(row>10) { clear(); refresh(); }
		ch = userinput[0];
		if(isaonum(ch))  sub1break();
		else
		if((ch == '+') || (ch == '-'))  sub3break();
		else
		if(ch == ':')  sub4break(&done);
		else breakhelp();
	}
	while(!done);

	if(running)  createdisplay();
}


/*
 *      Simple Minded Assembler Program
 */


/*
 *      Error Numbers
 */

#define DD      1               /* doubly defined symbol */
#define LN      2               /* too long a name */
#define LO      3               /* too large an octal constant */
#define XP      4               /* cross page reference (pass2) */
#define SX      5               /* some obscure syntax error */
#define UNDEF   6               /* undefined symbol (pass2) */

/*
 *      Symbol Types
 */

#define LABEL8  0
#define MRI     1
#define OPR     2
#define IOT     3
#define INDIR   4

/*
 *      Input Tokens
 */

#define LABELS          0
#define COMMENT         1
#define OCTAL           2
#define ORIGIN          3
#define ENDING          4
#define IDENTIFIER      5
#define NIL             6
#define ERRORS          7

/*
 *      Detailed Input Tokens (Pass2)
 */

#define XLAB8           0
#define XCOMMENT        1
#define XOCTAL          2
#define XORIGIN         3
#define XENDING         4
#define XNIL            5
#define XERRORS         6
#define XVARIABLE       7
#define XMRIOPCODE      8
#define XIOTOPCODE      9
#define XOPROPCODE      10
#define XINDIRECTBIT    11

/*
 *      Lexical States
 */

#define START   0
#define IDENT   1
#define INOCT   2
#define DONE    3

/*
 *      Symbol Table Entry
 */


/*
 *      Global Variables
 */

int     error;          /* flag for fatal error */
int     ended;          /* flag for end of source file */
char    line[128];      /* source input line */
int     numchars;       /* number of characters in line */
char    *posn;          /* where we are on line */

int     where;          /* location counter */
FILE    *source;        /* source file */
FILE    *symbols;       /* standard symbol file */
char    newsym[8];      /* most recent identifier */
int     newval;         /* most recent number */
int     printval;
int     passno;         /* pass number */
int     listing;        /* flag to say if want listing */
int     argextra;       /* count of command line args used as flags, rather than file names. */
int     errpass = 0;        /* pass on which error report (if any) last generated */

errs(errno, passno)
int     errno;
int     passno;
{
	char    *p;
	error++;
	if (errpass != passno)     {
		errpass = passno;
		printf("\n\nAssembly error detected in Pass %d\n", passno);
	}
	printf("\n\nError occurred in or about the line:\n");
	printf("%s\n", line);

	for (p = line; p < posn; p++)
		putchar(' ');

	printf("^\n");

	for (p = line; p < posn; p++)
		putchar(' ');

	printf("|\n");
	printf("\n\nError Type: ");

	switch (errno)  {

	case XP:
		printf("Cross Page Reference\n");
		break;

	case DD:
		printf("doubly defined symbol, %s\n", newsym);
		break;

	case LN:
		printf("too long a name, %s\n", newsym);
		break;

	case LO:
		printf("too large an octal constant\n");
		break;

	case SX:
		printf("syntax error\n");
		break;

	case UNDEF:
		printf("undefined symbol, %s\n", newsym);
		break;

	}
	skipline();
}


skipline()
{

	while (*posn)
		posn++;
}

getline()
{
	int     ch;
	int     i;

	numchars = 0;
	posn = line;

	while ((ch = getc(source)) != '\n' && ch != EOF)        {
		numchars++;
		*posn++ = (isupper(ch) ? tolower(ch) : ch);
	}

	if(ch==EOF) {
		fprintf(stderr,"unexpected end of file\n");
		exit(1);
		}
	*posn++ = ' ';

	for (i = numchars; i < 127; i++)
		*posn++ = 0;

	posn = line;
	numchars += 2;
}

nextsym()
{
	int     state;
	int     ch;
	int     octdigit;
	int     alphabetic;
	int     numeric;
	int     dollars;
	int     comma;
	int     star;
	int     slash;
	int     space;
	int     endln;
	int     readtype;
	int     octval;
	int     symlength;
	int     errno;
	int     i;
	char    symbuf[8];

	state = START;
	octval = 0;
	symlength = 0;

	for (i = 0; i < 8; i++)
		symbuf[i] = 0;

	while (state != DONE)   {
		if (posn - line > numchars)
			getline();

		ch = *posn++;

		alphabetic = isalpha(ch);
		numeric = isdigit(ch);
		octdigit = (ch >= '0' && ch <= '7');
		dollars = (ch == '$');
		star = (ch == '*');
		slash = (ch == '/');
		space = (ch == ' ' || ch == '\t');
		endln = (ch == 0);
		comma = (ch == ',');

		switch (state)  {

		case START:
			if (star)       {
				readtype = ORIGIN;
				state = INOCT;
			}
			else if (slash) {
				readtype = COMMENT;
				state = DONE;
			}
			else if (dollars)       {
				readtype = ENDING;
				state = DONE;
			}
			else if (alphabetic)    {
				readtype = IDENTIFIER;
				state = IDENT;
				symbuf[symlength++] = ch;
			}
			else if (octdigit)      {
				readtype = OCTAL;
				state = INOCT;
				octval = (ch - '0');
			}
			else if (endln) {
				readtype = NIL;
				state = DONE;
			}
			else if (!space)        {
				errno = SX;
				readtype = ERRORS;
				state = DONE;
			}

			continue;

		case INOCT:
			if (octdigit)   {
				if (octval >= 512)      {
					errno = LO;
					readtype = ERRORS;
					state = DONE;
				}
				else
					octval = (octval << 3) + (ch - '0');
			}
			else if (space || endln)
				state = DONE;
			else    {
				errno = SX;
				readtype = ERRORS;
			}

			continue;

		case IDENT:
			if (alphabetic || numeric)      {
				if (symlength < 6)
					symbuf[symlength++] = ch;

				if (symlength > 6)      {
					strcpy(newsym, symbuf);
					errno = LN;
					readtype = ERRORS;
					state = DONE;
				}
			}
			else if (space)
				state = DONE;
			else if (comma) {
				state = DONE;
				readtype = LABELS;
			}

			continue;
		}
	}

	if (readtype == ERRORS)
		errs(errno, passno);

	if (readtype == LABELS || readtype == IDENTIFIER)
		strcpy(newsym, symbuf);

	if (readtype == ORIGIN || readtype == OCTAL)
		newval = octval;

	return (readtype);
}

findposn()
{
	int     top;
	int     bottom;
	int     sposn;
	int     found;

	top = numsym;
	bottom = 0;
	found = 0;

	while (!found)  {
		sposn = (top - bottom) / 2 + bottom;

		if (strcmp(newsym, symtab[sposn].name) == 0)
			found++;
		else if (strcmp(newsym, symtab[sposn].name) > 0)
			bottom = ++sposn;
		else
			top = sposn - 1;

		found = (found || (bottom > top));
	}

	return (sposn);
}

xnextsym()
{
	int     temp;
	int     sympos;
	int     xtemp;

	temp = nextsym();

	switch (temp)   {

	case LABELS:
		xtemp = XLAB8;
		break;

	case COMMENT:
		xtemp = XCOMMENT;
		break;

	case OCTAL:
		xtemp = XOCTAL;
		break;

	case ORIGIN:
		xtemp = XORIGIN;
		break;

	case ENDING:
		xtemp = XENDING;
		break;

	case NIL:
		xtemp = XNIL;
		break;

	case ERRORS:
		xtemp = XERRORS;
		break;

	case IDENTIFIER:
		sympos = findposn();
		if (strcmp(symtab[sympos].name, newsym) != 0)   {
			xtemp = XERRORS;
			errs(UNDEF, passno);
		}
		else    {
			switch (symtab[sympos].stype)   {

			case LABEL8:
				xtemp = XVARIABLE;
				break;

			case MRI:
				xtemp = XMRIOPCODE;
				break;

			case IOT:
				xtemp = XIOTOPCODE;
				break;

			case OPR:
				xtemp = XOPROPCODE;
				break;

			case INDIR:
				xtemp = XINDIRECTBIT;
				break;

			}

			newval = symtab[sympos].value;
		}

		break;
	}

	return (xtemp);
}

insertsymbol()
{
	int     sposn;
	int     n;

	sposn = findposn();

	if (strcmp(newsym, symtab[sposn].name) == 0)
		errs(DD, passno);
	else    {
		n = numsym;

		while (n >= sposn)      {
			symtab[n + 1] = symtab[n];
			n--;
		}

		strcpy(symtab[sposn].name, newsym);
		symtab[sposn].stype = LABEL8;
		symtab[sposn].value = where;
		numsym++;
	}
}

pass1(argc, argv)
int     argc;
char    **argv;
{

	if ((source = fopen(argv[1], "r")) == NULL)     {
		fprintf(stderr, "assemble: Cannot open %s\n", argv[1+argextra]);
		exit(1);
	}

	where = 0;
	ended = 0;
	numchars = 0;
	passno = 1;
	posn = line;

	while (!ended)       {
		switch (nextsym())      {

		case COMMENT:
			skipline();
			break;

		case ORIGIN:
			where = newval;
			break;

		case LABELS:
			insertsymbol();
			break;

		case ENDING:
			ended++;
			break;

		case OCTAL:
		case IDENTIFIER:
			where++;
			skipline();
			break;

		}
	}



}

writecode(cval, next)
int     cval;
int     next;
{
	if(where<=corelimit) store[where] = cval;

	where++;
	if (!(next == XCOMMENT || next == XNIL))
		errs(SX, passno);
	else
		skipline();
}

aiots()
{
	int     next;

	printval = newval;
	next = xnextsym();

	while (next == XOCTAL)  {
		printval |= newval;
		next = xnextsym();
	}

	writecode(printval, next);
}

aoperate()
{
	int     next;

	printval = newval;
	next = xnextsym();

	while (next == XOCTAL || next == XOPROPCODE)    {
		printval |= newval;
		next = xnextsym();
	}

	writecode(printval, next);
}

amemrefs()
{
	int     next;
	int     temp;
	int     pcp;
	int     dp;

	printval = newval;
	next = xnextsym();

	if (next == XINDIRECTBIT)       {
		printval |= newval;
		next = xnextsym();
	}

	if (!(next == XOCTAL || next == XVARIABLE))
		errs(SX, passno);
	else    {
		if (newval < 128)       {
			printval |= newval;
			writecode(printval, xnextsym());
		}
		else    {
			pcp = where & 07600;
			dp = newval & 07600;

			if (pcp != dp)
				errs(XP, passno);
			else    {
				temp = newval & 0177;
				temp |= 128;
				printval |= temp;
				writecode(printval, xnextsym());
			}
		}
	}
}

pass2(argc, argv)
int     argc;
char    **argv;
{


	rewind(source);
	ended = 0;
	numchars = 0;
	posn = line;
	passno = 2;



	while (!ended )       {
		printval = 0;

		switch (xnextsym())     {

		case XINDIRECTBIT:
			errs(SX,passno);
			continue;

		case XCOMMENT:
			skipline();
			continue;

		case XORIGIN:

			where = newval;
			if (xnextsym() != XNIL)
				errs(SX, passno);

			continue;

		case XOCTAL:
		case XVARIABLE:
			writecode(newval, xnextsym());
			continue;

		case XMRIOPCODE:
			amemrefs();
			continue;

		case XIOTOPCODE:
			aiots();
			continue;

		case XOPROPCODE:
			aoperate();
			continue;

		case XENDING:
			ended++;
			continue;

		}
	}


}

ainitialise()
{
	int     i;
	int     j;

	error = 0;

	if ((symbols = fopen("/usr/pub/cs/211/source/symbols", "r")) == NULL)  {
		fprintf(stderr, "assemble: Cannot open symbols file\n");
		exit(1);
	}

	fgets(line, 128, symbols);
	sscanf(line, "%d", &numsym);

	for (i = 0; i < numsym; i++)
		fscanf(symbols, "%s %d %o", symtab[i].name, &symtab[i].stype, &symtab[i].value);

	numsym--;
	fclose(symbols);
}

assmbl(argc,argv)
int argc;
char **argv;
{

	listing = 0;

	ainitialise();
	zeroline();
	pass1(argc, argv);
	zeroline();

	if (error)   printf("\n\nAssembly errors in Pass 1\nError reports from Pass 2 may not be valid\n");
	pass2(argc, argv);

	if (!error)
		printf("\n\n\tassembled ok\n\n");
	else {
	printf("execution aborted because of assembly errors\n");
	exit(0);
	}
	sleep(1);
}

zeroline()
{
	char    *p;
	int     i;

	for (p = line, i = 0; i < 128; i++)
		*p++ = 0;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


main(argc,argv)
int argc;
char **argv;
/* Here is the main program,

 first assemble "PDP-8" code


 initialise program counter to standard start address

 set " running " indicator

 while running
  perform the instruction fetch
       instruction execute cycle


*/



{
	verbose = 0;
	assmbl(argc,argv);
	initialize();

	dumpinitial();

	running = true;
	aborted=false;
	pc = start;
	acc = 0;
	link8 = 0;
	time8 = 0;

	d03f = 0;
	d04f = 0;
	d03b = 0;
	d04b = 0;
	d40f = 0;
	d40b = 0;
	d41f = 0;
	d41b = 0;
	d50f = 0;
	d60f = 0;
	d60b = 0;
	ionf = 0;
	interrupt = false;
	intsused = false;

	clockoff = true;

	icount = 0;
	idelay = -1;

	signal(2,onintr);

	initscr();

	breakpoint();

	while (running)  {

		icount++;
		if(icount>50000) {
			clear();
			printw("50000 instructions executed.\n");
			printw("program terminated\n");
			refresh();
			aborted=true;
			running=false;
			break;
			}
		if (idelay==0) {
			ionf = 1;
			showperiphs(d00);
		}
		idelay--;
		opc = pc;
		evaluateinterrupt();
		fetchinstruction();
		decodeinstruction(true,dcddisplay);
		if(~aborted)  executeinstruction();
		if(singlestep)  waitforit();
		if((pc < 0 ) || (pc > corelimit))  {
			clearscreen();
			printw("Transfer to address outside of memory of simulated machine.\n");
			printw("Referenced address was %4o\n",pc);
			refresh();
			delayn(100);
			printw("executing instruction at about %4o\n",opc);
			refresh();
			delayn(100);
			running = false;
			pc = opc;
			breakpoint();
		}
		else
			if(aborted)  {
				printw("Instruction aborted, entering break package.\n");
				refresh();
				delayn(50);
				breakpoint();
			}
			else
				if (specialbreak) {
					specialbreak = 0;
					breakpoint();
				}
		if(breakpts[pc])  breakpoint();
	}



	clear();
	printw("\nProgram finished.\n\n");
	refresh();
	finaldump();
	endwin();

}

