#include <stdio.h>
#include <ctype.h>
#define false 0
#define true ~0


/*        This program is a simulator for a somewhat simplified
      version of the DEC PDP-8 computer.
 */




/*  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   512
#define      corelimit  511
#define         symlim  320 /* maximum number of symbols in user symbol table */
/*  These are the decimal equivalents of various octal bit patterns we require  */

#define        mask7    7
#define        mask177  127
#define        mask200  128
#define        mask400  256
#define        mask770  504
#define        mask777  511
#define       mask7000  3584
#define       mask7600  3968
#define       mask7777  4095


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

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

#define irrow     2            /*  instruction register display  */
#define ircol     50

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

#define marrow    5            /*  memory address register display  */
#define marcol    12

#define mdrrow    5            /*  memory data register display  */
#define mdrcol    24


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

#define mem1row    14          /*  Comments on memory  */
#define mem1col    0

#define mem2row    17          /*  Window into Memory  */
#define mem2col    20

#define busrow     11          /*  Bus, row, control, address and data  */
#define busccol    15
#define busacol    30
#define busdcol    45

#define prmptrow    15         /*  prompts re dumpfile etc  */
#define prmptcol   0

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

#define macrow    8            /*  Major Cycle trace  */
#define maccol    40

#define microw    9            /*  Minor Cycle trace  */
#define miccol    0

#define dirow     5            /*  Decode of Instruction  */
#define dicol     40

#define temprow   9            /*  Location of temporary results display  */
#define tempcol   40

/*  Some timing constants used in calls to delayn  */

#define regchng  5           /*  Time to highlight a register, after a change  */
#define memchng   5           /*  Time to highlight a memory location  */
#define miphs     4            /*  Time to delay for a minor phase trace statement  */
#define maphs     12           /*  Time to delay for a major phase  */
#define instrt    10           /*  Time delay at start of each instruction  */




/*  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 iot8            2
#define opr8            3
#define indir8          4
/*  record structure for entries in symbol table  */
struct entry {
	char name[6];
	int stype;
	int value;
};



/*  The files:  */
FILE *object;    /*  file containing the "object code" of the PDP-8 program  */
FILE *dumpfile; /*  file for output  */
int dumpopen; /*  flag to indicate if file opened  */

/*  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 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     */
/*  and 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 show that current instruction to abort as illegal mem-ref  */
int oprbits;
int device;
int inst;



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



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



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

int singlestep = 1;
int dispmem    = 1;
int dispbus    = 1;
int dispminor  = 0;
int verbose;
int specialbreak;

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

/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*        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 ;

{
#define cntrlcoord  16
#define offset 32
	/*  cursor addressing, cntrl + 2 other chars define coords         */
	putchar(cntrlcoord);
	putchar(line+offset);
	putchar(column+offset);
}

/*  * * * * * * display functions * * * * * * *  */

slowdown()

{
#define delaych 0177
	int i;
	/*  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);
}



/*  * * * * * * display functions * * * * * * *  */

clrendline()

{
#define lcntrlclear  22         /*  decimal equivalents of various cntrl characters  */
	putchar(lcntrlclear); /*  clear the rest of this line  */
	slowdown();               /*  give it time!  */
}

/*  * * * * * * display functions * * * * * * *  */

clrendscreen()

{
#define escntrlclear  23        /*  decimal equivalents of various cntrl characters  */
	putchar(escntrlclear); /*  clear from here to end of the screen  */
	slowdown();               /*  give it time!  */
}


/*  * * * * * * display functions * * * * * * *  */

clearscreen()

{
#define scntrlclear  24        /*  decimal equivalents of various cntrl characters  */
	putchar(scntrlclear); /*  clear the screen  */
	slowdown();               /*  give it time!  */
	slowdown();
	slowdown();
}

/*  * * * * * * display functions * * * * * * *  */

invertvideo()

{
#define chinverse 17
/*        putchar(chinverse);           */
/*  inverse video got screen too messed up when break used */
}

/*  * * * * * * display functions * * * * * * *  */


delayn(n)
int n;
/*  simplest reliable way of getting controllable delays seems       */
/*  to be to send a stream of null characters to the terminal        */
{
	int i;
	for(i= 1; i <= n; i++) slowdown();
}



/*  * * * * * * display functions * * * * * * *  */

getoptions()
{       int ch;
       do
	    {
		ch=getchar();
		switch(ch) {
		case 'q' :
			running = false;
			aborted = true;
			break;
		case 's' :
			singlestep = !singlestep;
			break;
		case 'b' :
			dispbus = !dispbus;
			position(busrow,60);
			if(!dispbus)
			printf("(display disabled)");
			else clrendline();
			position(waitrow,waitcol+30);
			break;
		case 'm' :
			dispmem = !dispmem;
			position(mem1row,60);
			if(!dispmem)
			printf("(display disabled)");
			else clrendline();
			position(waitrow,waitcol+30);
			break;
		case 'c' :
			dispminor = !dispminor;
			if(!dispminor) {
			position(microw,miccol);
			clrendline();
			}
			break;
		case '+' :
			slowfactor = (slowfactor > 1) ? slowfactor-1 : 1;
			break;
		case '-' :
			slowfactor++;
			break;

		}
	}
	while (ch  !=  '\n');

}

breakin()
{

	position(waitrow,waitcol);
	printf("Set run options>");
	position(waitrow,waitcol+30);
	getoptions();
	position(waitrow,waitcol);
	clrendline();
	specialbreak = 0;
}
waitforit()
{

	position(waitrow,waitcol);
	printf("To continue press");
	invertvideo();
	printf("RETURN");
	invertvideo();
	position(waitrow,waitcol+30);
	getoptions();
	position(waitrow,waitcol);
	clrendline();
}

/*  * * * * * * display functions * * * * * * *  */

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);
	putchar('+');
	for(i= 1; i <= size; i++) putchar('-');
	putchar('+');
	position(row,col-1);
	putchar('|');
	position(row,col+size);
	putchar('|');
	position(row+1,col-1);
	putchar('+');
	for(i= 1; i <= size; i++) putchar('-');
	putchar('+');
}


/*  * * * * * * display functions * * * * * * *  */


phase(title, row,col,delay, major,inverse)
char *title;
int row,col,delay, major,inverse;
{

    if(major || dispminor) {
	position(row,col);
	clrendline();
	position(row,col);
	if(inverse) invertvideo();
	printf("%s",title);
	if(inverse) invertvideo();

	if(singlestep && major) waitforit();
	else delayn(delay);
	}

}

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


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

	/*  This 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\n",link8);

	}

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


	fprintf(dumpfile,"\nAddress                        Contents\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;

	}
}



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


getprogram()
{
	int  i,n,val,j;
	char ch;
	/*  read in code as produced by "smap" program  */
	object = fopen("object","r");
	if(object==NULL)
	{
		printf("I couldn't find the object file\n");
		exit(0);
	}
	for(i= 0; i <= corelimit; i++) store[i] = 0;

	/*  each line consists of a marker character, * ==> relocate origin  */
	/*        space ==> data for placing in current location             */
	/*        $     ==> end of code                                      */

	fscanf(object,"%c",&ch);

	n = 0;

	while( ch != '$') {
		fscanf(object,"%o",&val);
		fgetc(object); /* consume cr */
		switch( ch ) {
		case ' ' :
			store[n++] = val;
			break;
		case '*' :
			if(val> corelimit) {
				printf("Illegal address in object file.\n");
				exit(0);
			}
			n = val;
			break;
		}
		fscanf(object,"%c",&ch);
	}

	fgetc(object); /* consume cr */

	/*  Now the symbol table                                           */
	fscanf(object,"%d",&numsym);
	fgetc(object);
	if(numsym>0)
		for(i= 0; i <= numsym; i++)
		{

			fscanf(object,"%6s",symtab[i].name);
			fscanf(object,"%d",&j);
			switch( j) {
			case 0:
				symtab[i].stype = label8;
				break;
			case 1:
				symtab[i].stype = mri8;
				break;
			case 2:
				symtab[i].stype = opr8;
				break;
			case 3:
				symtab[i].stype = iot8;
				break;
			case 4:
				symtab[i].stype = indir8;
				break;
			}
			fscanf(object,"%o",&j);
			symtab[i].value=j;
			fgetc(object);
		}


}

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

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

	/*  at position  */
	position(atrow,atcol);

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

	/*  write the value, in a field size defined by howbig  */
	printf("%*o",howbig,value);

	/*  pause a while  */
	delayn(howlong);


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

}

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

lookup(want, st, ptr)
int want,st;
int *ptr;
{
	int  i,found;
	/*  look through symbol table for a symbol whose value field       */
	/*  equals "want" and whose symtype equals "st", if find it        */
	/*  return true and 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)
int loc;
{
	int i,l;

	for(i= -2; i <= 2; i++) {
		l = loc+i;
		if(l<0) l = l+coresize;
		l = l % coresize;
		position(mem2row+i,mem2col);
		if(l==loc) invertvideo();
		printf("%4o",l);
		if(l==loc) invertvideo();

		printf("  %4o",store[l]);
	}

}

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

frommemory()

{
	/*        mdr =  store[mar];                                        */

	if(dispbus) position(busrow,busccol);
	if(dispbus) printf("READ ");
	if(dispbus) position(busrow,busdcol);
	if(dispbus) clrendline();
	if(dispmem) position(mem2row-2,0);
	if(dispmem) clrendscreen();

	if(dispbus) highlight(busrow,busacol,mar,4,regchng);

	if((mar>=0) && (mar<=corelimit)) {
		if(dispmem) memshow(mar);


		if(dispmem) highlight(mem2row,mem2col+6,store[mar],4,memchng);
		if(dispmem) highlight(mem2row,mem2col+16,store[mar],4,memchng);

		if(dispbus) highlight(busrow,busdcol,store[mar],4,regchng);

		mdr = store[mar];

		if(dispmem) highlight(mdrrow,mdrcol,mdr,4,regchng);
		if(dispmem) {
		if(singlestep) waitforit();
		else delayn(miphs);
		}
	}
	else {
		position(mem2row,0);
		printf("Illegal Memory Reference\n");
		printf("Referenced address is outside of store of simulated machine.\n");

		delayn(100);
		printf("Address was %4o",mar);
		delayn(250);
		printf("Program will be terminated.\n");
		delayn(100);
		running = false;
		aborted
		    =true;
		mdr = 0;
	}
}

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

tomemory()
{
	/*        store[mar]  =  mdr                                        */

	if(dispbus) position(busrow,busccol);
	if(dispbus) printf("WRITE");
	if(dispmem) position(mem2row-2,0);
	if(dispmem) clrendscreen();
	if(dispbus) highlight(busrow,busdcol,mdr,4,regchng);
	if(dispbus) highlight(busrow,busacol,mar,4,regchng);

	if((mar>=0) && (mar<=corelimit)) {
		if(dispmem) memshow(mar);

		if(dispbus) highlight(busrow,busdcol,mdr,4,regchng);
		if(dispmem) highlight(mem2row,mem2col+16,mdr,4,regchng);
		if(dispmem) highlight(mem2row,mem2col+6,store[mar],4,memchng);
		if(dispmem) highlight(mem2row,mem2col+16,mdr,4,regchng);
		if(dispmem) position(mem2row,mem2col+6);
		if(dispmem) printf("    ");
		if(dispmem) delayn(memchng);
		if(dispmem) highlight(mem2row,mem2col+16,mdr,4,regchng);
		if(dispmem) highlight(mem2row,mem2col+6,mdr,4,memchng);
		if(dispmem) {
		if(singlestep) waitforit();
		else delayn(miphs);
		}
		store[mar] = mdr;
	}
	else {
		position(mem2row,0);
		printf("Illegal Memory Reference\n");
		printf("Referenced address is outside of store of simulated machine.\n");

		delayn(100);
		printf("Address was %4o\n",mar);
		delayn(250);
		printf("Program will be terminated.\n");
		delayn(100);
		running = false;
		aborted
		    =true;
	}

}

/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*                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 (summing into temporary register,
							copying 12 bits back to program counter)
							 */

	 position(dirow,dicol);
	clrendline();

	if(dispminor) position(microw,miccol);
	if(dispminor) clrendline();

	phase("Fetching Instruction                    ",macrow,maccol,instrt,true,true);

	phase("Copy pc to mar                          ",microw,miccol,miphs,false,false);

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

	mar = pc;

	if(dispminor) highlight(marrow,marcol,mar,4,regchng);

	phase("Access memory, contents to mdr          ",microw,miccol,miphs,false,false);

	frommemory();


	phase("Copy fetched instruction to ir          ",microw,miccol,miphs,false,false);

	ir = mdr;

	highlight(irrow,ircol,ir,4,regchng);

	acclink = pc+1;
	pc = (acclink & mask7777);


	phase("Increment pc                            ",microw,miccol,miphs,false,false);
	highlight(pcrow,pccol,pc,4,regchng);

}


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


decodeaddress()

{
	int ptr;
	/*

							 */

	if(dispminor) position(microw,miccol);
	if(dispminor) clrendline();
	phase("Decoding data address                   ",macrow,maccol,maphs,true,true);


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

	if(indirect)
		phase("Indirect addressing mode, have pointer  ",microw,miccol,miphs,false,false);
	else
		phase("Direct addressing                       ",microw,miccol,miphs,false,false);

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

	if(page0)
		phase("Referenced address/pointer on page 0    ",microw,miccol,miphs,false,false);
	else
		phase("Referenced address/pointer on this page ",microw,miccol,miphs,false,false);


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


	phase("Location on page                        ",microw,miccol,miphs,false,false);
	if(dispminor) highlight(temprow,tempcol,location,4,regchng);

	/*  Construct address by taking location and 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 and "or" these in ).  */

	if(page0) {
		mar = location;
		phase("Page 0, address to mar                  ",microw,miccol,miphs,false,false);
	}
	else {
		phase("This page, location combined with this  ",microw,miccol,miphs,false,false);
		phase("page number from pc, result to mar      ",microw,miccol,miphs,false,false);
		if(dispminor) highlight(temprow,tempcol,location,3,regchng);
		if(dispminor) position(temprow,tempcol+5);
		if(dispminor) printf(" + ");
		if(dispminor) highlight(temprow,tempcol+10,(pc & mask7600),4,regchng);
		if(dispminor) position(temprow,tempcol+15);
		if(dispminor) printf(" = ");
		mar = (pc & mask7600) | location;
		if(dispminor) highlight(temprow,tempcol+20,mar,4,regchng);

	}

	highlight(marrow,marcol,mar,4,regchng);
	position(dirow,dicol+5);
	if(indirect)
		printf(" i ");
	else printf("   ");

	position(dirow,dicol+7);
	if(lookup(mar,label8,&ptr)) printf("%6s",symtab[ptr].name);
	else printf("%4o  ",mar);

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

	if(indirect) {

		phase("Perform indirection, mar identifies ptr ",microw,miccol,miphs,false,false);
		phase("Fetch from that location into mdr       ",microw,miccol,miphs,false,false);

		frommemory();
		if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);

		phase("Copy address from mdr to mar            ",microw,miccol,miphs,false,false);
		mar = mdr;
		if(dispminor) highlight(marrow,marcol,mar,4,regchng);
	}

	phase("Now have effective address in mar       ",microw,miccol,miphs,false,false);

	highlight(marrow,marcol,mar,4,regchng);



}


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


decodeinstruction()

{
	int temp,ptr;

	if(dispminor) position(microw,miccol);
	if(dispminor) clrendline();

	phase("Decoding Instruction                    ",macrow,maccol,maphs,true,true);

	/*  First, break out the opcode */

	temp = (ir & mask7000);
	opcode = temp >> 9;
	phase("Opcode abstracted                       ",microw,miccol,miphs,false,false);
	if(dispminor) highlight(temprow,tempcol,opcode,1,regchng);


	/*  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 and function
					identified.

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

					 */

	position(dirow,dicol);

	switch( opcode ) {

	case and8:
	case tad :
	case dca :
	case isz :
	case jms :
	case jmp :


		switch(opcode) {
		case and8:
			printf("and");
			break;
		case tad:
			printf("tad");
			break;
		case dca:
			printf("dca");
			break;
		case isz:
			printf("isz");
			break;
		case jms:
			printf("jms");
			break;
		case jmp:
			printf("jmp");
			break;
		}
		/*  Memory address class, translate rest of word  */
		phase("Memory Reference Class                  ",microw,miccol,miphs,false,false);
		decodeaddress();
		break;


	case iot :

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



		phase("Input/Output type                       ",microw,miccol,miphs,false,false);
		break;


	case opr :
		oprbits = (ir & mask777);
		if(lookup(ir,opr8,&ptr)) printf("%6s        ",symtab[ptr].name);
		else printf("opr %3o   ",oprbits);

		phase("Microcoded 'Operate' Class              ",microw,miccol,miphs,false,false);
		break;
	}
}


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


inputoutput()

{
	clearscreen();

	printf("\nInput Output Instructions are not being simulated.");
	printf("\nI/O instruction specified  device #%2o",device);
	printf(", and operation #%1o\n",inst);


	running = false;
}


/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*        procedures rotateleft and rotateright                              */
/*                provide somewhat clumsy mechanisms for treating            */
/*                acc and link as a single 13-bit register and performing    */
/*                circular shifts on contents thereof                        */
/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
rotateleft(doubles)
int doubles;
{
	int temp;
	if(doubles) rotateleft(false);
	phase("Rotating Left, initial register values  ",microw,miccol,miphs,false,false);
	if(dispminor) highlight(accrow,acccol,acc,4,regchng);
	if(dispminor) highlight(linkrow,linkcol,link8,1,regchng);
	temp =  acc << 1;
	acc = (temp & mask7777); /*  mask out relevant bits  */
	acc |= (link8 & 1); /*  bring link bit } around  */
	if(temp > 4095) link8 = 1;
	else link8 = 0;
	phase("Rotating Left, final register values    ",microw,miccol,miphs,false,false);
	highlight(accrow,acccol,acc,4,regchng);
	highlight(linkrow,linkcol,link8,1,regchng);
}
/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
rotateright(doubles)
int doubles;
{
	int  temp;
	if(doubles) rotateright(false);
	phase("Rotating Right, initial register values ",microw,miccol,miphs,false,false);
	if(dispminor) highlight(accrow,acccol,acc,4,regchng);
	if(dispminor) highlight(linkrow,linkcol,link8,1,regchng);
	temp =  acc & 1; /*  pick of bit that will shift into link  */
	acc = acc >> 1;   /*  divide to achieve shift right  */
	acc |= ((link8 & 1) << 11); /*  add in link with correct offset  */
	link8 = temp;
	phase("Rotating Right, final register values   ",microw,miccol,miphs,false,false);
	highlight(accrow,acccol,acc,4,regchng);
	highlight(linkrow,linkcol,link8,1,regchng);

}
/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

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) and 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  */
		phase("Skip instruction                        ",microw,miccol,miphs,false,false);
		toskip = false;

		if(skp & oprbits) {
			/*  the AND group and 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) {
			phase("Skip condition true, increment pc       ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(pcrow,pccol,pc,4,regchng);
			acclink = pc+1;
			pc = (acclink & mask7777);
			highlight(pcrow,pccol,pc,4,regchng);
		}
		else
			phase("Skip condition false, pc unchanged      ",microw,miccol,miphs,false,false);

		/*  Time 2, maybe clear acc  */

		if(cla & oprbits) {
			phase("Clear acc,                              ",microw,miccol,miphs,false,false);
			acc = 0;
			highlight(accrow,acccol,acc,4,regchng);
		}

		/*  Time 3, maybe halt  */

		if(hlt & oprbits) {
			phase("Halt                                    ",microw,miccol,miphs,false,false);

			running = false ;
		}


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


		/*  First time sequence, cla, cll  */

		if(cla & oprbits) {
			phase("Clear acc,                              ",microw,miccol,miphs,false,false);
			acc = 0;
			highlight(accrow,acccol,acc,4,regchng);
		}

		if(cll & oprbits) {
			phase("Clear link,                             ",microw,miccol,miphs,false,false);
			link8 = 0;
			highlight(linkrow,linkcol,link8,1,regchng);
		}


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

		if(cma & oprbits) {
			phase("Complement acc                          ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(accrow,acccol,acc,4,regchng);
			acc = (~acc) & 07777;
			highlight(accrow,acccol,acc,4,regchng);
		}

		if(cml & oprbits) {
			phase("Complement link                         ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(linkrow,linkcol,link8,1,regchng);
			link8 = (~link8) & 1;

			highlight(linkrow,linkcol,link8,1,regchng);
		}

		/*  3rd time sequence, increment acc  */

		if(iac & oprbits) {
			phase("Increment acc                           ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(accrow,acccol,acc,4,regchng);
			acclink = acc+1;
			acc = (acclink & mask7777);
			highlight(accrow,acccol,acc,4,regchng);
		}

		/*  4th time sequence, rotates  */

		if(ral & oprbits)
			rotateleft(shifttwice & oprbits);
		else
			if(rar & oprbits)
				rotateright(shifttwice & oprbits);

	}
}


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

executeinstruction()

{
	position(microw,miccol);
	clrendline();


	phase("Executing Instruction                   ",macrow,maccol,maphs,true,true);


	switch( opcode )    {
	case and8:
		/*  The AND instruction causes a bit-by-bit
							Boolean and operation between the contents of
							the accumulator and the data word specified
							by the instruction. The result is left in the
							accumulator.
							 */
		phase("Data Fetch                   ",microw,miccol,miphs,false,false);
		frommemory();
		if(! aborted) {
			phase("AND of acc and mdr                      ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(accrow,acccol,acc,4,regchng);
			if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
			acc = (acc & mdr);
			phase("AND of acc and mdr gives                ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(temprow,tempcol,acc,4,regchng);
			highlight(accrow,acccol,acc,4,regchng);
		}
		break;


	case tad:
		/*  TAD performs addition between the
							specified data word and 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.
							 */
		phase("Data Fetch                   ",microw,miccol,miphs,false,false);
		frommemory();
		if(! aborted) {
			phase("TAD of acc and mdr                      ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(accrow,acccol,acc,4,regchng);
			if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
			acclink = acc+mdr;
			if(acclink>4095) {
				/*  have carry bit, complement link  */
				if(link8==1) link8=0;
				else link8 = 1;
				phase("carry complements link                  ",microw,miccol,miphs,false,false);
				highlight(linkrow,linkcol,link8,1,regchng);
			}
			acc = (acclink & mask7777);
			phase("TAD of acc and mdr gives                ",microw,miccol,miphs,false,false);

			if(dispminor) highlight(temprow,tempcol,acc,4,regchng);
			highlight(accrow,acccol,acc,4,regchng);
		}
		break;





	case isz:
		/*  The ISZ instruction adds a 1 to the
		referenced data word and 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.
		 */
		phase("Data Fetch                   ",microw,miccol,miphs,false,false);
		frommemory();
		if(! aborted) {
			if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
			phase("Increment fetched value by 1 ",microw,miccol,miphs,false,false);
			acclink = mdr+1;
			if(acclink > 4095) mdr = 0;
			else mdr = acclink;
			if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
			phase("Write back new value         ",microw,miccol,miphs,false,false);
			phase("Data Store                   ",microw,miccol,miphs,false,false);
			tomemory();
			if(mdr==0) {
				phase("New value 0, need to skip    ",microw,miccol,miphs,false,false);
				phase("Increment pc                 ",microw,miccol,miphs,false,false);
				if(dispminor) highlight(pcrow,pccol,pc,4,regchng);
				pc = (pc+1) & 07777;
				highlight(pcrow,pccol,pc,4,regchng);
			}
		}
		break;




	case dca:
		/*  DCA, deposit and clear accumulator.
		The DCA instruction stores the contents of the
		ac in the referenced location, destroying the
		original contents of the location. The ac
		is the set to zero
		 */
		phase("acc to mdr                   ",microw,miccol,miphs,false,false);
		if(dispminor) highlight(accrow,acccol,acc,4,regchng);
		mdr = acc;
		if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
		phase("Data Store                   ",microw,miccol,miphs,false,false);
		tomemory();
		if(! aborted) {
			acc = 0;
			phase("clear acc                    ",microw,miccol,miphs,false,false);
			 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).

		 */

		phase("Subroutine call, save return ",microw,miccol,miphs,false,false);
		phase("copy pc to mdr               ",microw,miccol,miphs,false,false);
		if(dispminor) highlight(pcrow,pccol,pc,4,regchng);
		mdr = pc;
		if(dispminor) highlight(mdrrow,mdrcol,mdr,4,regchng);
		phase("Data Store                   ",microw,miccol,miphs,false,false);
		tomemory();
		if(! aborted) {
			phase("Set program counter from mar ",microw,miccol,miphs,false,false);
			if(dispminor) highlight(marrow,marcol,mar,4,regchng);
			pc = mar+1;
			phase("pc = mar+1                    ",microw,miccol,miphs,false,false);
			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.
		 */
		phase("Jmp i.e. GOTO instruction    ",microw,miccol,miphs,false,false);
		phase("Set program counter from mar ",microw,miccol,miphs,false,false);
		if(dispminor) highlight(marrow,marcol,mar,4,regchng);
		pc = mar;
		highlight(pcrow,pccol,pc,4,regchng);
		break;



	case iot:
		inputoutput();

		break;


	case opr:
		operate();
		break;
	}
}

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

initialize()
{
	int ch;
	slowfactor = 8; /*  temporary set at a default value  */
	clearscreen();
	/*  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                  */

	if(verbose) {
		printf("       A delay factor is incoporated into this program. This delay factor\n");
		printf("is intended to make the display run, as near as possible, at an acceptable\n");
		printf("speed. The best value for the delay factor depends on system load, the speed\n");
		printf("of the line connecting your terminal to the computer and on subjective\n");
		printf("feelings. A suitable value is generally in the range 1-16.\n");
		printf("(The delay factor scale is linear, a suitable default might be 6).\n");
		printf("\n");
	}
	printf("Please enter your chosen delay factor: ( a number in range 1 - 16 )");
	scanf("%d",&slowfactor);
	getchar(); /* swallow cr */
	if((slowfactor<1) || (slowfactor> 16)) {
		printf("\ndefaulting delay factor to 6.\n");
		slowfactor = 6;
	}
	if(verbose) {

	delayn(5);

	clearscreen();

		printf("\n\n\n");

		delayn(5);
		printf("       PPPP   DDDD   PPPP          888\n");
		printf("       P   P   D  D  P   P        8   8\n");
		printf("       P   P   D  D  P   P        8   8\n");
		printf("       PPPP    D  D  PPPP  #####   888\n");
		printf("       P       D  D  P            8   8\n");
		printf("       P       D  D  P            8   8\n");
		printf("       P      DDDD   P             888\n");

		delayn(10);
	position(prmptrow,prmptcol);
	}


	printf("Now reading in the PDP-8 object code of your program.\n");

}

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

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

	clearscreen();
	delayn(20);
	position(0,0);
	invertvideo();
	printf("C.P.U.");
	invertvideo();
	if(verbose) delayn(20);

	box(accrow,acccol,4);
	position(accrow,acccol-6);
	invertvideo();
	printf("acc");
	invertvideo();
	delayn(5);

	box(linkrow,linkcol,1);
	position(linkrow,linkcol-6);
	invertvideo();
	printf("link");
	invertvideo();
	delayn(5);

	box(pcrow,pccol,4);
	position(pcrow,pccol-6);
	invertvideo();
	printf("pc");
	invertvideo();
	delayn(5);

	box(marrow,marcol,4);
	position(marrow,marcol-6);
	invertvideo();
	printf("mar");
	invertvideo();
	delayn(5);

	box(mdrrow,mdrcol,4);
	position(mdrrow,mdrcol-6);
	invertvideo();
	printf("mdr");
	invertvideo();
	delayn(5);

	box(irrow,ircol,4);
	position(irrow,ircol-6);
	invertvideo();
	printf("ir");
	invertvideo();
	delayn(5);

	if(verbose) delayn(20);

	position(busrow-1,0);
	printf("______________________________________________________________________");
	delayn(5);
	invertvideo();
	position(busrow,0);
	printf("BUS:");
	position(busrow,busccol-8);
	printf("control");
	position(busrow,busacol-8);
	printf("address");
	position(busrow,busdcol-5);
	printf("data");
	invertvideo();
	delayn(5);
	position(busrow+1,0);
	printf("______________________________________________________________________");

	if(verbose) delayn(20);
	position(busrow+2,0);
	invertvideo();
	printf("MEMORY");
	invertvideo();
	delayn(10);
	position(mem1row,mem1col);
	printf("                 Address  Contents");

	phase("Initial Register Values:                ",macrow,maccol,10,true,true);

	highlight(accrow,acccol,acc,4,10);
	highlight(linkrow,linkcol,link8,1,10);
	highlight(pcrow,pccol,pc,4,10);

	if(verbose) delayn(20);
	phase("Initial Instruction Sequence at 0200oct ",macrow,maccol,10,true,true);

	memshow(pc);

	if(verbose) delayn(25);
	phase("Program execution about to begin        ",macrow,maccol,10,true,true);

	if(verbose) delayn(50);



}
/*  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

dumpinitial()

{
	int ch;
	dumpopen = false;
	if(verbose) {
	position(prmptrow,prmptcol);
	clrendline();
	}
else putchar('\n');
	printf("Do you want initial contents of memory 'dumped' to a file?( Y or N)");
	ch=getchar();
	if((ch=='Y') || (ch=='y')) {
		dumpfile=fopen("dumpfile","w");
		if(dumpfile==NULL) {
			printf("Couldn't open dumpfile\n");
			exit(0);
		}
		fprintf(dumpfile,"Printout of contents of memory prior to program execution.\n\n");


		dumpcore(dumpfile,false);
		dumpopen = true;
	}
	ch=getchar(); /* get rid of cr */
}

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

finaldump()

{
	int ch;
	printf("Do you want final contents of memory and registers 'dumped' to a file?( Y or N)");
	ch=getchar();
	if((ch=='Y') || (ch=='y')) {
		if(! dumpopen) {
		dumpfile=fopen("dumpfile","w");
			if(dumpfile==NULL) {
				printf("Couldn't open dumpfile\n");
				exit(0);
			}


		}
		else fprintf(dumpfile,"\n\n\n");

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


		dumpcore(dumpfile,true);
	}
}

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

/*  Here is the main program,

first load the required "PDP-8" code from a file


initialise program counter to standard start address

set " running " indicator

while running
perform the instruction fetch
    instruction execute cycle


 */


main(argc,argv)
int argc;
char **argv;
{
	verbose = 0;
	while(--argc && **++argv== '-') {
		while(*++*argv)
			switch(**argv) {
			case 'v' :
				verbose++;
				break;
			}

	}
	initialize();

	getprogram();

	dumpinitial();

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

	createdisplay();

	signal(2,onintr);

	while( running ) {
		if(specialbreak) breakin();
		if(!running) break;
		fetchinstruction();
		if(specialbreak) breakin();
	if(!running) break;
		decodeinstruction();
		if(specialbreak) breakin();
	if(!running) break;
		if(! aborted) executeinstruction();
		if((pc<0) || (pc > corelimit)) {
			delayn(50);
			clearscreen();
			delayn(50);
			printf("Your program has bugs (or is too large?).\n\n");
			printf("Attempt to transfer control to an address\n\n");
			printf("outside of available memory.\n\n");

			printf("Address referenced was ");
			printf("%*o\n",4,pc);

			delayn(250);
			running = false;
		}
	}

	clearscreen();
	delayn(10);
	printf("Program finished.\n");
	finaldump();
	position(22,0); /*  position cursor at bottom of screen on exit  */

}

