/****************************************************/ /* File: tm.c */ /* The TM ("Tiny Machine") computer */ /* Compiler Construction: Principles and Practice */ /* Kenneth C. Louden */ /****************************************************/ /* Modification History November 2007 Mike Volz and Eugene Wallingford University of Northern Iowa The simulator now accepts 0 or more command-line arguments, placing them at locations [1..n] in the DMEM. December 2013 Eugene Wallingford University of Northern Iowa Replaced uses of gets() with fgets(), which is safer. Eliminates run-time warning. * Simulates the TM program immediately, rather than interacting with the user through the simulator's menu interface. * Reports rudimentary execution time of the TM program, in terms of clock ticks consumed. November 2015 Brian Paterni and Eugene Wallingford University of Northern Iowa Augmented use of fgets() with strcspn(), to remove EOL characters. Fixes TM commands that require arguments. */ #include #include #include #include #include // added to support execution timing #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /******* const *******/ #define IADDR_SIZE 1024 /* increase for large programs */ #define DADDR_SIZE 1024 /* increase for large programs */ #define NO_REGS 8 #define PC_REG 7 #define LINESIZE 121 #define WORDSIZE 20 /******* type *******/ typedef enum { opclRR, /* reg operands r,s,t */ opclRM, /* reg r, mem d+s */ opclRA /* reg r, int d+s */ } OPCLASS; typedef enum { /* RR instructions */ opHALT, /* RR halt, operands are ignored */ opIN, /* RR read into reg(r); s and t are ignored */ opOUT, /* RR write from reg(r), s and t are ignored */ opADD, /* RR reg(r) = reg(s)+reg(t) */ opSUB, /* RR reg(r) = reg(s)-reg(t) */ opMUL, /* RR reg(r) = reg(s)*reg(t) */ opDIV, /* RR reg(r) = reg(s)/reg(t) */ opRRLim, /* limit of RR opcodes */ /* RM instructions */ opLD, /* RM reg(r) = mem(d+reg(s)) */ opST, /* RM mem(d+reg(s)) = reg(r) */ opRMLim, /* Limit of RM opcodes */ /* RA instructions */ opLDA, /* RA reg(r) = d+reg(s) */ opLDC, /* RA reg(r) = d ; reg(s) is ignored */ opJLT, /* RA if reg(r)<0 then reg(7) = d+reg(s) */ opJLE, /* RA if reg(r)<=0 then reg(7) = d+reg(s) */ opJGT, /* RA if reg(r)>0 then reg(7) = d+reg(s) */ opJGE, /* RA if reg(r)>=0 then reg(7) = d+reg(s) */ opJEQ, /* RA if reg(r)==0 then reg(7) = d+reg(s) */ opJNE, /* RA if reg(r)!=0 then reg(7) = d+reg(s) */ opRALim /* Limit of RA opcodes */ } OPCODE; typedef enum { srOKAY, srHALT, srIMEM_ERR, srDMEM_ERR, srZERODIVIDE } STEPRESULT; typedef struct { int iop ; int iarg1 ; int iarg2 ; int iarg3 ; } INSTRUCTION; /******** vars ********/ int iloc = 0 ; int dloc = 0 ; int traceflag = FALSE; int icountflag = FALSE; INSTRUCTION iMem [IADDR_SIZE]; int dMem [DADDR_SIZE]; int backupMem[DADDR_SIZE]; int reg [NO_REGS]; char * opCodeTab[] = {"HALT","IN","OUT","ADD","SUB","MUL","DIV","????", /* RR opcodes */ "LD","ST","????", /* RM opcodes */ "LDA","LDC","JLT","JLE","JGT","JGE","JEQ","JNE","????" /* RA opcodes */ }; char * stepResultTab[] = {"OK","Halted","Instruction Memory Fault", "Data Memory Fault","Division by 0" }; char pgmName[20]; FILE *pgm ; char in_Line[LINESIZE] ; int lineLen ; int inCol ; int num ; char word[WORDSIZE] ; char ch ; int done ; /********************************************/ int opClass( int c ) { if ( c <= opRRLim) return ( opclRR ); else if ( c <= opRMLim) return ( opclRM ); else return ( opclRA ); } /* opClass */ /********************************************/ void writeInstruction ( int loc ) { printf( "%5d: ", loc) ; if ( (loc >= 0) && (loc < IADDR_SIZE) ) { printf("%6s%3d,", opCodeTab[iMem[loc].iop], iMem[loc].iarg1); switch ( opClass(iMem[loc].iop) ) { case opclRR: printf("%1d,%1d", iMem[loc].iarg2, iMem[loc].iarg3); break; case opclRM: case opclRA: printf("%3d(%1d)", iMem[loc].iarg2, iMem[loc].iarg3); break; } printf ("\n") ; } } /* writeInstruction */ /********************************************/ void getCh (void) { if (++inCol < lineLen) ch = in_Line[inCol] ; else ch = ' ' ; } /* getCh */ /********************************************/ int nonBlank (void) { while ((inCol < lineLen) && (in_Line[inCol] == ' ') ) inCol++ ; if (inCol < lineLen) { ch = in_Line[inCol] ; return TRUE ; } else { ch = ' ' ; return FALSE ; } } /* nonBlank */ /********************************************/ int getNum (void) { int sign; int term; int temp = FALSE; num = 0 ; do { sign = 1; while ( nonBlank() && ((ch == '+') || (ch == '-')) ) { temp = FALSE ; if (ch == '-') sign = - sign ; getCh(); } term = 0 ; nonBlank(); while (isdigit(ch)) { temp = TRUE ; term = term * 10 + ( ch - '0' ) ; getCh(); } num = num + (term * sign) ; } while ( (nonBlank()) && ((ch == '+') || (ch == '-')) ) ; return temp; } /* getNum */ /********************************************/ int getWord (void) { int temp = FALSE; int length = 0; if (nonBlank ()) { while (isalnum(ch)) { if (length < WORDSIZE-1) word [length++] = ch ; getCh() ; } word[length] = '\0'; temp = (length != 0); } return temp; } /* getWord */ /********************************************/ int skipCh ( char c ) { int temp = FALSE; if ( nonBlank() && (ch == c) ) { getCh(); temp = TRUE; } return temp; } /* skipCh */ /********************************************/ int atEOL(void) { return ( ! nonBlank ()); } /* atEOL */ /********************************************/ int error( char * msg, int lineNo, int instNo) { printf("Line %d",lineNo); if (instNo >= 0) printf(" (Instruction %d)",instNo); printf(" %s\n",msg); return FALSE; } /* error */ /********************************************/ int readInstructions (void) { OPCODE op; int arg1, arg2, arg3; int loc, regNo, lineNo; for (regNo = 0 ; regNo < NO_REGS ; regNo++) reg[regNo] = 0 ; dMem[0] = DADDR_SIZE - 1 ; for (loc = 1 ; loc < DADDR_SIZE ; loc++) dMem[loc] = backupMem[loc]; for (loc = 0 ; loc < IADDR_SIZE ; loc++) { iMem[loc].iop = opHALT ; iMem[loc].iarg1 = 0 ; iMem[loc].iarg2 = 0 ; iMem[loc].iarg3 = 0 ; } lineNo = 0 ; while (! feof(pgm)) { fgets( in_Line, LINESIZE-2, pgm ) ; inCol = 0 ; lineNo++; lineLen = strlen(in_Line)-1 ; if (in_Line[lineLen]=='\n') in_Line[lineLen] = '\0' ; else in_Line[++lineLen] = '\0'; if ( (nonBlank()) && (in_Line[inCol] != '*') ) { if (! getNum()) return error("Bad location", lineNo,-1); loc = num; if (loc > IADDR_SIZE) return error("Location too large",lineNo,loc); if (! skipCh(':')) return error("Missing colon", lineNo,loc); if (! getWord ()) return error("Missing opcode", lineNo,loc); op = opHALT ; while ((op < opRALim) && (strncmp(opCodeTab[op], word, 4) != 0) ) op = (OPCODE) ( (int)op + 1) ; if (strncmp(opCodeTab[op], word, 4) != 0) return error("Illegal opcode", lineNo,loc); switch ( opClass(op) ) { case opclRR : /***********************************/ if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) ) return error("Bad first register", lineNo,loc); arg1 = num; if ( ! skipCh(',')) return error("Missing comma", lineNo, loc); if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) ) return error("Bad second register", lineNo, loc); arg2 = num; if ( ! skipCh(',')) return error("Missing comma", lineNo,loc); if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) ) return error("Bad third register", lineNo,loc); arg3 = num; break; case opclRM : case opclRA : /***********************************/ if ( (! getNum ()) || (num < 0) || (num >= NO_REGS) ) return error("Bad first register", lineNo,loc); arg1 = num; if ( ! skipCh(',')) return error("Missing comma", lineNo,loc); if (! getNum ()) return error("Bad displacement", lineNo,loc); arg2 = num; if ( ! skipCh('(') && ! skipCh(',') ) return error("Missing LParen", lineNo,loc); if ( (! getNum ()) || (num < 0) || (num >= NO_REGS)) return error("Bad second register", lineNo,loc); arg3 = num; break; } iMem[loc].iop = op; iMem[loc].iarg1 = arg1; iMem[loc].iarg2 = arg2; iMem[loc].iarg3 = arg3; } } return TRUE; } /* readInstructions */ /********************************************/ STEPRESULT stepTM (void) { INSTRUCTION currentinstruction ; int pc ; int r,s,t,m ; int ok ; pc = reg[PC_REG] ; if ( (pc < 0) || (pc > IADDR_SIZE) ) return srIMEM_ERR ; reg[PC_REG] = pc + 1 ; currentinstruction = iMem[ pc ] ; switch (opClass(currentinstruction.iop) ) { case opclRR : /***********************************/ r = currentinstruction.iarg1 ; s = currentinstruction.iarg2 ; t = currentinstruction.iarg3 ; break; case opclRM : /***********************************/ r = currentinstruction.iarg1 ; s = currentinstruction.iarg3 ; m = currentinstruction.iarg2 + reg[s] ; if ( (m < 0) || (m > DADDR_SIZE)) return srDMEM_ERR ; break; case opclRA : /***********************************/ r = currentinstruction.iarg1 ; s = currentinstruction.iarg3 ; m = currentinstruction.iarg2 + reg[s] ; break; } /* case */ switch ( currentinstruction.iop) { /* RR instructions */ case opHALT : /***********************************/ printf("HALT: %1d,%1d,%1d\n",r,s,t); return srHALT ; /* break; */ case opIN : /***********************************/ do { printf("Enter value for IN instruction: ") ; fflush (stdin); fflush (stdout); // gets(in_Line); // unsafe fgets( in_Line, sizeof(in_Line), stdin ); // safe, with addition in_Line[strcspn(in_Line, "\r\n")] = 0; // of null terminator lineLen = strlen(in_Line) ; inCol = 0; ok = getNum(); if ( ! ok ) printf ("Illegal value\n"); else reg[r] = num; } while (! ok); break; case opOUT : printf ("OUT instruction prints: %d\n", reg[r] ) ; break; case opADD : reg[r] = reg[s] + reg[t] ; break; case opSUB : reg[r] = reg[s] - reg[t] ; break; case opMUL : reg[r] = reg[s] * reg[t] ; break; case opDIV : /***********************************/ if ( reg[t] != 0 ) reg[r] = reg[s] / reg[t]; else return srZERODIVIDE ; break; /*************** RM instructions ********************/ case opLD : reg[r] = dMem[m] ; break; case opST : dMem[m] = reg[r] ; break; /*************** RA instructions ********************/ case opLDA : reg[r] = m ; break; case opLDC : reg[r] = currentinstruction.iarg2 ; break; case opJLT : if ( reg[r] < 0 ) reg[PC_REG] = m ; break; case opJLE : if ( reg[r] <= 0 ) reg[PC_REG] = m ; break; case opJGT : if ( reg[r] > 0 ) reg[PC_REG] = m ; break; case opJGE : if ( reg[r] >= 0 ) reg[PC_REG] = m ; break; case opJEQ : if ( reg[r] == 0 ) reg[PC_REG] = m ; break; case opJNE : if ( reg[r] != 0 ) reg[PC_REG] = m ; break; /* end of legal instructions */ } /* case */ return srOKAY ; } /* stepTM */ /********************************************/ int doCommand (void) { char cmd; int stepcnt=0, i; int printcnt; int stepResult; int regNo, loc; do { printf ("Enter command: "); fflush (stdin); fflush (stdout); // gets(in_Line); // unsafe fgets( in_Line, sizeof(in_Line), stdin ); // safe, with addition in_Line[strcspn(in_Line, "\r\n")] = 0; // of null terminator lineLen = strlen(in_Line); inCol = 0; } while (! getWord ()); cmd = word[0] ; switch ( cmd ) { case 't' : /***********************************/ traceflag = ! traceflag ; printf("Tracing now "); if ( traceflag ) printf("on.\n"); else printf("off.\n"); break; case 'h' : /***********************************/ printf("Commands are:\n"); printf(" s(tep "\ "Execute n (default 1) TM instructions\n"); printf(" g(o "\ "Execute TM instructions until HALT\n"); printf(" r(egs "\ "Print the contents of the registers\n"); printf(" i(Mem > "\ "Print n iMem locations starting at b\n"); printf(" d(Mem > "\ "Print n dMem locations starting at b\n"); printf(" t(race "\ "Toggle instruction trace\n"); printf(" p(rint "\ "Toggle print of total instructions executed"\ " ('go' only)\n"); printf(" c(lear "\ "Reset simulator for new execution of program\n"); printf(" h(elp "\ "Cause this list of commands to be printed\n"); printf(" q(uit "\ "Terminate the simulation\n"); break; case 'p' : /***********************************/ icountflag = ! icountflag ; printf("Printing instruction count now "); if ( icountflag ) printf("on.\n"); else printf("off.\n"); break; case 's' : /***********************************/ if ( atEOL ()) stepcnt = 1; else if ( getNum ()) stepcnt = abs(num); else printf("Step count?\n"); break; case 'g' : stepcnt = 1 ; break; case 'r' : /***********************************/ for (i = 0; i < NO_REGS; i++) { printf("%1d: %4d ", i,reg[i]); if ( (i % 4) == 3 ) printf ("\n"); } break; case 'i' : /***********************************/ printcnt = 1 ; if ( getNum ()) { iloc = num ; if ( getNum ()) printcnt = num ; } if ( ! atEOL ()) printf ("Instruction locations?\n"); else { while ((iloc >= 0) && (iloc < IADDR_SIZE) && (printcnt > 0) ) { writeInstruction(iloc); iloc++ ; printcnt-- ; } } break; case 'd' : /***********************************/ printcnt = 1 ; if ( getNum ()) { dloc = num ; if ( getNum ()) printcnt = num ; } if ( ! atEOL ()) printf("Data locations?\n"); else { while ((dloc >= 0) && (dloc < DADDR_SIZE) && (printcnt > 0)) { printf("%5d: %5d\n",dloc,dMem[dloc]); dloc++; printcnt--; } } break; case 'c' : /***********************************/ iloc = 0; dloc = 0; stepcnt = 0; for (regNo = 0; regNo < NO_REGS ; regNo++) reg[regNo] = 0 ; dMem[0] = DADDR_SIZE - 1 ; for (loc = 1 ; loc < DADDR_SIZE ; loc++) dMem[loc] = backupMem[loc] ; break; case 'q' : return FALSE; /* break; */ default : printf("Command %c unknown.\n", cmd); break; } /* case */ stepResult = srOKAY; if ( stepcnt > 0 ) { if ( cmd == 'g' ) { stepcnt = 0; while (stepResult == srOKAY) { iloc = reg[PC_REG] ; if ( traceflag ) writeInstruction( iloc ) ; stepResult = stepTM (); stepcnt++; } if ( icountflag ) printf("Number of instructions executed = %d\n",stepcnt); } else { while ((stepcnt > 0) && (stepResult == srOKAY)) { iloc = reg[PC_REG] ; if ( traceflag ) writeInstruction( iloc ) ; stepResult = stepTM (); stepcnt-- ; } } printf( "%s\n",stepResultTab[stepResult] ); } return TRUE; } /* doCommand */ /*********************************************/ /* E X E C U T I O N B E G I N S H E R E */ /*********************************************/ int main( int argc, char * argv[] ) { int stringValue, loc; int commandLineCounter = 1; if (argc < 2) { printf("usage: %s \n",argv[0]); exit(1); } strcpy(pgmName,argv[1]) ; if (strchr (pgmName, '.') == NULL) strcat(pgmName,".tm"); pgm = fopen(pgmName,"r"); if (pgm == NULL) { printf("file '%s' not found\n",pgmName); exit(1); } /* * Initialize the memory */ for (loc = 1 ; loc < DADDR_SIZE ; loc++) { dMem[loc] = 0 ; backupMem[loc] = 0; } dMem[0] = DADDR_SIZE - 1 ; backupMem[0] = DADDR_SIZE - 1; // -------------------------------------------------------------- // Modification to pass command-line arguments into the simulator // -------------------------------------------------------------- while (commandLineCounter + 1 < argc) { if (!strcmp("false", argv[commandLineCounter+1])) { backupMem[commandLineCounter] = FALSE; } else if (!strcmp("true", argv[commandLineCounter+1])) { backupMem[commandLineCounter] = TRUE; } else { stringValue = atoi(argv[commandLineCounter+1]); if (stringValue == 0 && strcmp("0", argv[commandLineCounter+1])) { printf("%s is not a valid command line argument\n", argv[commandLineCounter+2]); printf("true, false or integer values are valid arguments\n"); exit(1); } else { backupMem[commandLineCounter] = stringValue; } } commandLineCounter++; } // test the values in dMem // // for (commandLineCounter = 1; // commandLineCounter+1 < argc; // commandLineCounter++) // { // printf("Memory location %d is %d\n",commandLineCounter, // dMem[commandLineCounter]); // } /* read the program */ if ( ! readInstructions ()) exit(1) ; /* switch input file to terminal */ /* reset( input ); */ /* read-eval-print */ // original rest of main // --------------------- // printf("TM simulation (enter h for help)...\n"); // do // done = ! doCommand (); // while (! done ); // printf("Simulation done.\n"); // instead, run the program and exit // report number of clock ticks as execution time // ---------------------------------------------- clock_t time_start; clock_t time_end; time_start = clock(); int stepcnt = 0; int stepResult = srOKAY; while (stepResult == srOKAY) { iloc = reg[PC_REG] ; stepResult = stepTM (); stepcnt++; } time_end = clock(); printf("Number of instructions executed = %d\n", stepcnt); printf("Execution took %ld clicks.\n", (long)time_end - (long)time_start); return 0; }