/* Patched version of exec8. This tries to combine use of curses (for terminal independence) with use of technique of sending null pad characters for speed control */ #include #include /* 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 4096 #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 *object; /* file containing the "object code" of the PDP-8 program */ 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 = getch(); 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; } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 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); } else 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=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"); running = false; aborted=true; refresh(); 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); refresh(); } 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."); refresh(); invertvideo(); 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; if(inst == 0) { distance = abs(discpos - (acc & 07)); 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); refresh(); running = false; } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* 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*/ /* WELL, THAT WOULD BE THE BEST WAY OF DOING IT, BUT */ /* THE SPEEDING UP OF PERIPHERALS IS SO EXCESSIVE THAT */ /* IT GET S TO EASY TO LOOSE CHARACTERS */ /* */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ externalevent() { int i,j,temp; char ch; i = 1; while ((i <= numevents) && (time8 >= etime[i])) { switch( etype[i] ) { case d03: /* if((d03f==0) || intsused) { */ 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(); 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"); refresh(); invertvideo(); } 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; char ch; slowfactor = 8; /* 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 */ 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\n"); } printf("Please enter your chosen delay factor: ( a number in range 1 - 16 )\n"); scanf("%d",&slowfactor); if((slowfactor<1) || (slowfactor> 16)) { printf("defaulting delay factor to 6.\n"); slowfactor = 6; } while(getchar()!='\n'); /* Need events mechanism initialized in order to set up keyboard correctly */ numevents = 0; srand(1); if (0==access(".8.kbd.1",4)) { printf(" Does the PDP-8 program, that you intend to run, require 'keyboard'\n"); printf("input from a file? (I need to know before I try to open the file '.8.kbd.1.'\n"); printf("for reading).\n\n"); printf("Any 'keyboard' input? (Y or N)\n"); ch = getchar(); getchar(); /* consume cr */ if((ch=='y') || (ch=='Y')) { 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,0); } } else printf("(Presuming no keyboard input).\n"); tty = fopen(".8.tty.1","w"); if(tty==NULL) { printf("\n ?\nI couldn't open the '.8.tty.1' output file!\n"); exit(0); } if (verbose) { printf("\n You control the execution of your PDP-8 program through the\n"); printf("facilities of the automatic 'debugging' package built into this simulator\n"); printf("program.\n"); printf("\n The object code of your program is now being loaded. When this\n"); printf("loading process is complete control will be transferred to the 'debug'\n"); printf("routines where you may define display options, specify single-step execution\n"); printf("of instructions, or set breakpoints.\n"); delayn(20); } regdisplay = true; progdisplay = verbose; /* let prog & data displays be determined */ datadisplay = verbose; /* by verbosity setting */ dcddisplay = true; singlestep = false; perdisplay = true; 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); delayn(25); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ dumpinitial() { char ch; dumpopen = false; printf("Do you want initial contents of memory 'dumped' to a file?( Y or N)\n"); ch=getchar(); while(getchar() != '\n'); if((ch=='Y') || (ch=='y')) { 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() { char ch; printw("Do you want final contents of memory & registers 'dumped' to a file?( Y or N)"); refresh(); ch=getch(); while(getch()!='\n'); if((ch=='Y') || (ch=='y')) { if(!dumpopen) { dumpfile = fopen("dumpfile","w"); if(dumpfile==NULL) { printw("\n ?\nI couldn't open the 'dumpfile'!\n"); 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"); refresh(); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 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\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;i0) { int i; i=0; while(i2047) 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 [,] */ 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 / */ 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(" / expected.\n"); refresh(); } } } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ breakhelp() { if(userinput[0] != '?') { printw("%s\n",userinput); printw("Illegal breakpoint command.\n"); refresh(); } 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("
/ Print contents of addressed memory location\n"); printw(" in format determined by char.\n"); printw("
,/ 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(); refresh(); invertvideo(); printw("BREAKPOINT\n"); invertvideo(); printw("Break address : "); if(lookup(pc,label8,&ptr)) printw("%s\n",symtab[ptr].name); else printw("%4o\n",pc); refresh(); done = false; 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(); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ main(argc,argv) int argc; char **argv; /* 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 */ { verbose = 0; /* set switches, only verbose in as yet */ while(--argc && **++argv == '-') { while(*++*argv) switch(**argv) { case 'v' : verbose++; } } initialize(); getprogram(); 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 (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(); } clearscreen(); printw("Program finished.\n\n"); refresh(); finaldump(); endwin(); }