#include #include "pdp8sim.h" /* PDP-8 instruction simulator. Written January 1992 by Wayne Wolf. */ typedef int pdp8word; /* use this for type safety */ typedef int bit; /* a one-bit value, such as the link */ /* Machine state. */ bit Link /* link bit */; pdp8word PC /* program counter */, IR /* instruction register */, AC /* accumulator */, MA /* memory address */, MB /* memory buffer */, SwitchRegister /* switch register */; bit Intr; /* set to 1 if interrupts are on */ int turn_on_Intr = -1; /* when = 0, signals that interrupts should be turned on */ bit Intr_request = 0; /* set to 1 if interrupt is requested */ pdp8word Mem[MEMSIZE]; /* program memory */ /* other globals */ bool haltme = FALSE; /* set to true to signal end of fetch-execute cycle */ /* let user print multiple memory fragments */ int nfragments = 0; /* number of fragments to print */ #define MAX_FRAGMENTS 20 int lbounds[MAX_FRAGMENTS], ubounds[MAX_FRAGMENTS]; /* lower and upper bounds */ /* utilities */ char *decode_op(pdp8word op) { /* Given opcode, returns string for major instruction. */ switch (op) { case 0x0: return "AND"; case 0x1: return "TAD"; case 0x2: return "ISZ"; case 0x3: return "DCA"; case 0x4: return "JMS"; case 0x5: return "JMP"; case 0x6: return "IOT"; case 0x7: return "MICRO"; } /* should never reach */ (void)fprintf(stderr,"unknown opcode %o\n",op); return "FOO"; } void print_machine_state(FILE *afile) { int i, j; /* truncate to avoid extra digits */ PC = PC & WORDMASK; IR = IR & WORDMASK; AC = AC & WORDMASK; MA = MA & WORDMASK; MB = MB & WORDMASK; SwitchRegister = SwitchRegister & WORDMASK; Link = Link & 0x1; Intr = Intr & 0x1; /* Print current machine state to given file. */ (void)fprintf(afile,"\nPC = %4o\t\tIR = %4o (%s)\n",PC,IR,decode_op(EXTRACT_OPCODE(IR))); (void)fprintf(afile,"AC = %4o\t\tlink = %1o\nSR = %4o\n",AC,Link,SwitchRegister); (void)fprintf(afile,"MA = %4o\t\tMB = %4o\t\tIntr = %1o\n",MA,MB,Intr); /* print memory block */ for (i=0; i= 0x8 & MA <= 0xf); /* handle autoindexing */ if (autoindex) { Mem[MA] = (Mem[MA] + 1) & WORDMASK; } MA = Mem[MA]; /* get address */ } } void read_memory(pdp8word reladrs, bit page, bit indir) { /* Reads memory given relative address, current/zero page, indirection. Leaves result in MB. Handles autoindexing. */ /* compute the absolute address */ calculate_EA(reladrs,page,indir); /* do the read */ MB = Mem[MA]; } void write_memory(pdp8word reladrs, bit page, bit indir) { /* Writes memory given relative address, current/zero page, indirection. Value to write is in MB. Handles autoindexing. */ /* compute the absolute address */ calculate_EA(reladrs,page,indir); /* do the write */ Mem[MA] = MB; } /* simulation stuff */ void rotate_left() { /* Rotates the accumulator with link left by one bit. */ int templink = Link; Link = (AC >> (NBITS-1)) & 0x1; AC = (((AC << 1) & WORDMASK) | (templink & 0x1)); } void rotate_right() { /* Rotates the accumulator with link right by one bit. */ int templink = AC & 0x1; AC = ((AC >> 1) | (Link << (NBITS - 1))) & WORDMASK; Link = templink; } void do_group1(pdp8word instr) { /* Executes group1 microinstructions. */ /* phase 1 */ if (instr & CLA_MICRO) AC = 0; if (instr & CLL_MICRO) Link = 0; /* phase 2 */ if (instr & CMA_MICRO) AC = ~AC; if (instr & CML_MICRO) Link = ~Link; /* phase 3 */ if (instr & IAC_MICRO) AC = (AC + 1) & WORDMASK; /* phase 4 */ if ((instr & ROTATE_AC_LEFT_MICRO) && (instr & ROTATE_2_MICRO)) { rotate_left(); rotate_left(); } else if (instr & ROTATE_AC_LEFT_MICRO) rotate_left(); if ((instr & ROTATE_AC_RIGHT_MICRO) && (instr & ROTATE_2_MICRO)) { rotate_right(); rotate_right(); } else if (instr & ROTATE_AC_RIGHT_MICRO) rotate_right(); /* update PC */ PC = (PC + 1) & WORDMASK; } void do_group2(pdp8word instr) { /* Executes group2 microinstructions. */ /* phase 1 */ if ((instr & REVERSE_SKIP_MICRO) && !((instr & SMA_MICRO) || (instr & SZA_MICRO) || (instr & SNL_MICRO))) { /* must be SKP */ PC = (PC + 1) & WORDMASK; } else if (!(instr & REVERSE_SKIP_MICRO)) { bool cond1 = FALSE, cond2 = FALSE, cond3 = FALSE; /* regular skip */ if (instr & SMA_MICRO) { /* skip on minus acc */ cond1 = ((AC >> (NBITS -1)) & 0x1); } if (instr & SZA_MICRO) { /* skip on zero acc */ cond2 = ((AC & WORDMASK) == 0); } if (instr & SNL_MICRO) { /* skip on non-zero link */ cond3 = ((Link & 0x1) == 1); } /* finally, decide whether to skip */ if (cond1 || cond2 || cond3) /* satisfied OR---skip */ PC = (PC + 1) & WORDMASK; } else { /* reverse sense of skip */ bool cond1 = TRUE, cond2 = TRUE, cond3 = TRUE; if (instr & SMA_MICRO) { /* skip on pos acc */ cond1 = (!((AC >> (NBITS -1)) & 0x1)); } if (instr & SZA_MICRO) { /* skip on zero acc */ cond2 = (AC != 0); } if (instr & SNL_MICRO) { /* skip on non-zero link */ cond3 = (!Link); } /* test conjunction */ if (cond1 && cond2 && cond3) PC = (PC + 1) & WORDMASK; } /* phase 2 */ if (instr & CLA_MICRO) AC = 0; /* phase 3 */ if (instr & OSR_MICRO) AC = (SwitchRegister | AC) & WORDMASK; if (instr & HLT_MICRO) haltme = TRUE; /* update PC */ PC = (PC + 1) & WORDMASK; } void cycle() { /* Execute one instruction. */ /* if interrupt, JMS Z 0, else fetch next instruction */ if (Intr_request && Intr) { /* yes, we have an interrupt */ Intr = 0; /* turn off interrupts */ MB = (PC) & WORDMASK; /* jump back to this instruction */ write_memory(0,0,0); /* jms to 0 */ PC = 1; /* jump to 1 */ IR = Mem[PC]; /* get the interrupt-driven instruction */ } else /* regular execution */ IR = Mem[PC]; /* decode and execute */ switch (EXTRACT_OPCODE(IR)) { case 0x0: /* AND */ read_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); AC &= MB; PC = (PC + 1) & WORDMASK; break; /* wasn't that easy? */ case 0x1: /* TAD */ read_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); AC += MB; if ((AC & ~WORDMASK) != 0) Link = ~Link; AC = AC & WORDMASK; PC = (PC + 1) & WORDMASK; break; case 0x2: /* ISZ */ read_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* increment value and write back to memory */ MB = (MB + 1) & WORDMASK; write_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* skip? */ if (MB == 0) PC = PC + 2; else /* nope */ PC = PC + 1; break; case 0x3: /* DCA */ /* deposit */ MB = AC; write_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* clear */ AC = 0; PC = (PC + 1) & WORDMASK; break; case 0x4: /* JMS */ /* write PC to memory */ MB = (PC + 1) & WORDMASK; write_memory(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* jump */ calculate_EA(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* get the effective address to jump to */ PC = (MA + 1) & WORDMASK; break; case 0x5: /* JMP */ calculate_EA(EXTRACT_REL_ADRS(IR),EXTRACT_PAGE(IR),EXTRACT_INDIRECT(IR)); /* get the effective address to jump to */ /* jump */ PC = MA; break; case 0x6: /* IOT */ if (EXTRACT_DEVICE(IR) == 0 && EXTRACT_IOT1(IR) == 1) { /* must delay turning on interrupts until END of NEXT instruction */ turn_on_Intr = 2; } else if (EXTRACT_DEVICE(IR) == 0 && EXTRACT_IOT2(IR) == 1) Intr = 0; else (void)fprintf(stderr,"Error: IOT to device not implemented.\n"); PC = (PC + 1) & WORDMASK; break; case 0x7: /* microops */ if (EXTRACT_GROUP(IR) == GROUP1) do_group1(IR); else do_group2(IR); break; } /* should we turn interrupts back on? */ turn_on_Intr--; if (turn_on_Intr == 0) { /* yes */ Intr = 1; } } /* main program stuff */ void main(int argc, char **argv) { int PLC, i; FILE *src; bool single_step = FALSE, check_for_interrupts = FALSE; /* check user arguments */ if (argc < 2) { (void)fprintf(stderr,"usage: %s [-sstep] [-lbound adrs] [-ubound adrs] [-start PC] [-interrupt] object-file\n",argv[0]); /* exit(-1); */ } /* process user arguments */ PC = -1; /* signals not set by argument */ for (i=1; i= MAX_FRAGMENTS) (void)fprintf(stderr,"Error: too many memory fragments to print.\n"); } else if (strcmp(argv[i],"-start") == 0) { /* set PC */ i++; (void)sscanf(argv[i],"%o",&PC); } else { /* must be a file */ if ((src = fopen(argv[i],"r")) == NULL) { (void)fprintf(stderr,"%s: couldn't open input file %s.\n",argv[0],argv[1]); /* exit(-1); */ } } } /* read in object file */ while (!feof(src)) { int instr; /* read it */ (void)fscanf(src,"%o\n",&instr); if (IS_ADDRESS_RECORD(instr)) PLC = DECODE_ADDRESS_RECORD(instr); else { /* load into memory */ Mem[PLC] = instr; PLC++; } } /* execute */ if (PC < 0) { printf("Start at what octal address? "); scanf("%o",&PC); } fflush(stdin); /* synchronize input for possible single step */ do { /* should we check for interrupts? */ if (check_for_interrupts) { char foo[128]; printf("Interrupt? "); scanf("%s",foo); Intr_request = (strcmp(foo,"1") == 0); } /* execute one instruction */ cycle(); /* report */ (void)fprintf(stderr,"At end of cycle:\n"); print_machine_state(stderr); if (single_step && !haltme) { /* wait for user */ char scum[129]; (void)printf("\tnext?"); fflush(stdout); (void)gets(scum); (void)printf("\n"); } } while (!haltme); }