/*****************************************************************/ /* */ /* Machine Language Simulator */ /* Version 3.0 */ /* */ /* by J. Glenn Brookshear */ /* Marquette University */ /* */ /* Simulates the machine described in Appendix C */ /* of the text Computer Science: An Overview. */ /* Includes op-codes D and E (indirect addressing) */ /* as described in Chapter 7. */ /* */ /* Last update: March 8, 2002 */ /* */ /*****************************************************************/ #define ANSI 1 /* Set ANSI to 1 if this program is */ /* to be compiled on an ANSI C system. */ /* Set it to 0 if a (non ANSI) UNIX */ /* system is to be used. */ /* When the UNIX option is used, an */ /* empty file named SaveFile must be */ /* provided if no other SaveFile is */ /* present. */ /* This flag is used in deciding how */ /* to handle the deletion and renaming */ /* of files in the functions named */ /* save_program and delete_program. */ #include #include #include #define true 1 #define false 0 char buf[80]; /* buffer for input from keyboard */ long memory[16][16] = { 0 }; /* main memory */ long reg[16] = { 0 }; /* registers */ long pc = 0; /* program counter */ long instr_reg = 0; /* instruction register */ long op_code, operand_1, x, y; /* instruction components */ long gethex(); /* utility function for reading */ /* hexadecimal values--avoids */ /* use of scanf(). */ void identify(); /* identifies program on user */ /* screen when program starts. */ void help(); /* functions for menu items */ void display(); void change_mem(); void change_reg(); void change_pc(); void clear(); void single_step(); void run_to_halt(); void process_file(); void retrieve_program(); /* file manipulating functions */ void save_program(); void delete_program(); void list_programs(); void fetch(); /* steps in machine cycle */ void decode(); int execute(); void add_float(); /* machine language instructions */ void or(); void and(); void exclus_or(); void rotate(); void jump(); /*******************************************************/ /* */ /* main() displays the memory and register */ /* contents on the screen and waits for */ /* input from the keyboard. At that time */ /* it calls the appropriate function to */ /* perform the requested action. */ /* */ /*******************************************************/ int main() { identify(); display(); do { gets(buf); switch (buf[0]) { case 'M': case 'm': change_mem(); display(); break; case 'R': case 'r': change_reg(); display(); break; case 'P': case 'p': change_pc(); display(); break; case 'S': case 's': single_step(); display(); break; case 'G': case 'g': run_to_halt(); display(); break; case 'C': case 'c': clear(); display(); break; case 'F': case 'f': process_file(); display(); break; case 'H': case 'h': case '?': help(); display(); break; case 'Q': case 'q': break; } } while (buf[0] != 'Q' && buf[0] != 'q'); return 0; } /*******************************************************/ /* */ /* indentify() displays the program's identification */ /* on the user's screen when the program begins. */ /* */ /*******************************************************/ void identify() { printf("\n\n\n\n\n\n\n\n\n"); printf("***************************************************\n"); printf("* *\n"); printf("* Machine Simulator *\n"); printf("* version 3.0 *\n"); printf("* *\n"); printf("* Simulates the machine described in Appendix C *\n"); printf("* of the text Computer Science: An Overview. *\n"); printf("* *\n"); printf("***************************************************\n"); printf("\n\n\n\n\n\n\n\n"); printf("\n**Type to enter the main display... "); gets(buf); printf("\n"); } /*******************************************************/ /* */ /* help() presents an explanation of the options */ /* on the main display menu. */ /* */ /*******************************************************/ void help() { printf("\n\n\nOptions are as follows:\n\n"); printf(" M Change contents of memory cells.\n"); printf(" R Change contents of registers.\n"); printf(" P Change contents of program counter.\n"); printf(" C Clear memory or registers. (Options will be given.)\n"); printf(" S Single step. (Execute a single machine cycle.)\n"); printf(" G Go. (Execute until a halt instruction is executed\n"); printf(" or until 500 machine cycles have been executed.)\n"); printf(" F Obtain list of file options "); printf("(to save or retrieve programs).\n"); printf(" H Display this help screen.\n"); printf(" Q Terminate this simulation program.\n\n\n"); printf("\n**Type to return to main display... "); gets(buf); printf("\n"); } /*******************************************************/ /* */ /* display() displays the machine state */ /* and selection menu on the screen. */ /* */ /*******************************************************/ void display() { int i, j; printf("\n\n Main Memory\n\n"); for (i = 0; i != 16; i++) { printf(" "); printf(" %X", i); } for (i = 0; i != 16; i++) { printf("\n"); printf("%1X", i); for (j = 0; j != 16; j++) if (memory[i][j] <= 15) /* Allow for leading 0s */ printf(" 0%X ", memory[i][j]); else printf(" %X ", memory[i][j]); } printf("\n\n"); for (i = 0; i < 16; i++) { if (i == 8) { printf(" PC: "); if (pc <= 15) printf("0%X\n", pc); else printf("%X\n", pc); } printf("R%X:", i); if (reg[i] <= 15) printf("0%X ", reg[i]); else printf("%X ", reg[i]); } if (instr_reg == 0) printf(" IR: 0000\n"); else printf(" IR: %X\n", instr_reg); printf("\nType one of the following (H for help): "); printf("M, R, P, C, S, G, F, Q: "); } /*******************************************************/ /* */ /* gethex() is used to read hexadecimal values */ /* from the keyboard to avoid the lack of */ /* control provided by scanf. */ /* gethex() returns the value received when a */ /* valid hexadecimal value is typed. Otherwise */ /* it returns a -1 if only a RETURN was typed */ /* or a -2 if any nonhexadecimal characters were */ /* typed. */ /* */ /*******************************************************/ long gethex() { long input = 0; long return_value; int valid = true; int i = 0; gets(buf); while (buf[i] != '\0') { switch (buf[i]) { case '0': input = input * 16 + 0; break; case '1': input = input * 16 + 1; break; case '2': input = input * 16 + 2; break; case '3': input = input * 16 + 3; break; case '4': input = input * 16 + 4; break; case '5': input = input * 16 + 5; break; case '6': input = input * 16 + 6; break; case '7': input = input * 16 + 7; break; case '8': input = input * 16 + 8; break; case '9': input = input * 16 + 9; break; case 'A': case 'a': input = input * 16 + 10; break; case 'B': case 'b': input = input * 16 + 11; break; case 'C': case 'c': input = input * 16 + 12; break; case 'D': case 'd': input = input * 16 + 13; break; case 'E': case 'e': input = input * 16 + 14; break; case 'F': case 'f': input = input * 16 + 15; break; default: valid = false; break; } i++; } if (valid == true && i != 0) /* Valid hexadecimal value */ return_value = input; if (valid == true && i == 0) /* Only a RETURN was typed */ return_value = -1; if (valid == false) /* Invalid input received */ return_value = -2; return (return_value); } /*******************************************************/ /* */ /* change_mem() is used to change the contents */ /* of consecutive memory cells. */ /* */ /*******************************************************/ void change_mem() { long address, input; int a, b; do { printf("\n\nAddress of the cell to be changed (hexadecimal): "); address = gethex(); } while (address < 0 || address > 255); b = address % 16; a = address / 16; printf("\n\n Cell Current New\n"); printf(" Address contents contents\n"); do { printf(" %X%X ", a, b); if (memory[a][b] <= 15) printf("0%X ", memory[a][b]); else printf("%X ", memory[a][b]); input = gethex(); if (0 <= input) if (input < 256) { memory[a][b] = input; if (b == 15) { b = 0; a++; } else b++; a = a % 16; } else { printf("\nInput value too large"); printf("\n(Type to return to main display.)"); } if (input == -2) { printf("\n**Illegal input**"); printf("\n(Type to return to main display."); } printf("\n"); } while (input != -1); } /*******************************************************/ /* */ /* change_reg() is used to change */ /* the contents of a register. */ /* */ /*******************************************************/ void change_reg() { long reg_num, value; char repeat = 'y'; while (repeat == 'y' || repeat == 'Y') { do { printf("\nRegister to change: "); reg_num = gethex(); if (reg_num < 0 || reg_num > 15) printf("\n**Illegal register number**"); } while (reg_num < 0 || reg_num > 15); do { printf("\nValue of register %X is: ", reg_num); printf("%X\n", reg[reg_num]); printf("New value for register %X: ", reg_num); value = gethex(); if (value < 0 || value > 255) printf("\n***Illegal register value***"); } while (value < 0 || value > 255); reg[reg_num] = value; printf("\nChange another register? (y/n): "); gets(buf); repeat = buf[0]; } printf("\n"); } /*******************************************************/ /* */ /* change_pc() is used to change the value of */ /* the program counter. */ /* */ /*******************************************************/ void change_pc() { do { printf("\nNew value of the program counter: "); pc = gethex(); if (pc < 0 || pc > 255) printf("\n**Invalid value for program counter**"); } while (pc < 0 || pc > 255); printf("\n"); } /*******************************************************/ /* */ /* clear() puts 0s in all memory cells or all */ /* registers depending on the selection from */ /* the keyboard. An invalid input from the */ /* keyboard terminates the function with not */ /* side effects. */ /* */ /*******************************************************/ void clear() { int i, j; printf("\nClear memory, registers, or both? (M, R, or B): "); gets(buf); if (buf[0] == 'M' || buf[0] == 'm') { for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) memory[i][j] = 0; } if (buf[0] == 'R' || buf[0] == 'r') { for (i = 0; i < 16; i++) reg[i] = 0; } if (buf[0] == 'B' || buf[0] == 'b') { for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) memory[i][j] = 0; for (i = 0; i < 16; i++) reg[i] = 0; } printf("\n"); } /******************************************************/ /* */ /* process_file() presents a menu of the file */ /* processing options and branches to the */ /* option selected. */ /* */ /******************************************************/ void process_file() { printf("\n\nRetrieve a program, save a program, "); printf("\ndelete a program, or list available programs? "); printf("\n(R, S, D, or L): "); gets(buf); switch (buf[0]) { case 'R': case 'r': retrieve_program(); printf("\n"); break; case 'S': case 's': save_program(); break; case 'D': case 'd': delete_program(); break; case 'L': case 'l': list_programs(); break; default: printf("\n"); break; } } /*******************************************************/ /* */ /* retrieve_program() retrieves the selected */ /* machine language program (memory image) */ /* from the file "SaveFile" and loads it into */ /* the simulated memory. */ /* */ /*******************************************************/ void retrieve_program() { FILE *in_file; char input[80]; int i, j; long temp; int found = false; printf("\nType the name of the program to be retrieved: "); gets(buf); if ((in_file = fopen("SaveFile", "r")) == NULL) { printf("\nProgram file not available"); printf("\nType RETURN to continue: "); gets(buf); printf("\n"); } else { while ((fscanf(in_file, "%s", &input) != EOF) && found == false) if (strcmp(buf, input) == 0) { found = true; for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) fscanf(in_file, "%d ", &memory[i][j]); } else for (i = 0; i < 256; i++) fscanf(in_file, "%d ", &temp); } fclose(in_file); if (found == false) { printf("\n\nProgram %s not found\n", buf); printf("Type RETURN to continue: "); gets(buf); } } /*******************************************************/ /* */ /* save_program() places a program (memory image) */ /* in the file "SaveFile" under the name selected. */ /* */ /*******************************************************/ void save_program() { FILE *out_file; FILE *in_file; int i, j; int saved = false; long temp; char new_prog[80]; printf("\n\nEnter the name of the program to be saved: "); gets(new_prog); printf("\n"); out_file = fopen("NewFile", "w"); if ((in_file = fopen("SaveFile", "r")) != NULL) { while (fscanf(in_file, "%s", &buf) != EOF) { if (strcmp(buf, new_prog) == 0) { saved = true; fprintf(out_file, "%s\n", new_prog); for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) fprintf(out_file, "%d ", memory[i][j]); for (i = 0; i < 256; i++) fscanf(in_file, "%d ", &temp); } if ((strcmp(buf, new_prog) < 0) || ((strcmp(buf, new_prog) > 0) && (saved == true))) { fprintf(out_file, "%s\n", buf); for (i = 0; i < 256; i++) { fscanf(in_file, "%d ", &temp); fprintf(out_file, "%d ", temp); } } if ((strcmp(buf, new_prog) > 0) && (saved == false)) { saved = true; fprintf(out_file, "%s\n", new_prog); for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) fprintf(out_file, "%d ", memory[i][j]); fprintf(out_file, "%s\n", buf); for (i = 0; i < 256; i++) { fscanf(in_file, "%d ", &temp); fprintf(out_file, "%d ", temp); } } } fclose(in_file); } if (saved == false) { fprintf(out_file, "%s\n", new_prog); for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) fprintf(out_file, "%d ", memory[i][j]); } fclose(out_file); #if (ANSI == 1) remove("SaveFile"); rename("NewFile", "SaveFile"); #else system("rm SaveFile"); system("mv NewFile SaveFile"); #endif } /*******************************************************/ /* */ /* delete_program() removes a specified program */ /* from the file "Save File." */ /* */ /*******************************************************/ void delete_program() { FILE *out_file; FILE *in_file; int i; long temp; char del_prog[80]; printf("\nEnter the name of the program to be deleted: "); gets(del_prog); printf("\n"); out_file = fopen("NewFile", "w"); if ((in_file = fopen("SaveFile", "r")) != NULL) { while (fscanf(in_file, "%s", &buf) != EOF) { if (strcmp(buf, del_prog) != 0) { fprintf(out_file, "%s\n", buf); for (i = 0; i < 256; i++) { fscanf(in_file, "%d ", &temp); fprintf(out_file, "%d ", temp); } } else for (i = 0; i < 256; i++) fscanf(in_file, "%d ", &temp); } fclose(in_file); } fclose(out_file); #if (ANSI == 1) remove("SaveFile"); rename("NewFile", "SaveFile"); #else system("rm SaveFile"); system("mv NewFile SaveFile"); #endif } /*******************************************************/ /* */ /* list_programs() presents a listing of the */ /* names of those programs stored in the file */ /* "SaveFile." */ /* */ /*******************************************************/ void list_programs() { FILE *in_file; char input[80]; int i, j; long count = 0; long temp; if ((in_file = fopen("SaveFile", "r")) == NULL) printf("\n\nNo programs are stored.\n"); else { printf("\n\n\nPrograms available are as follows:\n\n"); while ((fscanf(in_file, "%s", &input) != EOF)) { printf("%s\n", input); count++; for (i = 0; i < 16; i++) for (j = 0; j < 16; j++) fscanf(in_file, "%d ", &temp); } if (count == 0) printf(" Program file is empty.\n"); } fclose(in_file); printf("\n**Type to return to main display."); gets(buf); printf("\n"); } /*******************************************************/ /* */ /* single_step() executes a single machine */ /* instruction and then returns to the main */ /* display. */ /* */ /*******************************************************/ void single_step() { fetch(); decode(); execute(); } /*******************************************************/ /* */ /* run_to_halt() simulates machine cycles until */ /* a halt instruction is executed or until 500 */ /* machine cycles have been simulated. In the */ /* second case, the option of stopping or */ /* continuing for 500 more cycles is given. */ /* */ /*******************************************************/ void run_to_halt() { char symbol[80]; int i = 0; do { fetch(); decode(); if (execute() == 1) break; if (i < 500) i++; else { printf("\n\n**Five hundred machine cycles have been executed**"); printf("\n\n**Continue executing machine language program? (Y/N) "); gets(symbol); printf("\n\n"); if (symbol[0] == 'N' || symbol[0] == 'n') break; else i = 0; } } while (instr_reg != 0xC000); } /*******************************************************/ /* */ /* fetch() simulates the fecth part of the */ /* machine cycle. */ /* */ /*******************************************************/ void fetch() { int a, b; b = pc % 16; a = pc / 16; instr_reg = (long) memory[a][b]; instr_reg *= 256; pc = (pc + 1) % 256; b = pc % 16; a = pc / 16; instr_reg += (long) memory[a][b]; pc = (pc + 1) % 256; } /*******************************************************/ /* */ /* decode() simulates the decode part of the */ /* machine cycle. */ /* */ /*******************************************************/ void decode() { long temp = instr_reg; y = temp % 16; temp = temp / 16; x = temp % 16; temp = temp / 16; operand_1 = temp % 16; op_code = temp / 16; } /*******************************************************/ /* */ /* execute() oversees the simulation of the */ /* execution part of the machine cycle. */ /* */ /*******************************************************/ int execute() { switch (op_code) { case 1: reg[operand_1] = memory[x][y]; break; /* load from mem */ case 2: reg[operand_1] = (x * 16) + y; break; /* load immediate */ case 3: memory[x][y] = reg[operand_1]; break; /* store */ case 4: reg[y] = reg[x]; break; /* move */ case 5: reg[operand_1] = (reg[x] + reg[y]) % 256; break; case 6: add_float(); break; case 7: reg[operand_1] = reg[x] | reg[y]; break; /* or */ case 8: reg[operand_1] = reg[x] & reg[y]; break; /* and */ case 9: reg[operand_1] = reg[x] ^ reg[y]; break; /* exclusive or */ case 10: rotate(); break; case 11: jump(); break; case 12: break; case 13: reg[operand_1] = memory[reg[y] / 16][reg[y] % 16]; /* load indirect */ break; case 14: memory[reg[y] / 16][reg[y] % 16] = reg[operand_1]; /* store indirect */ break; default: printf("\n\n\n"); printf("*** Invalid instruction in instruction register ***"); printf("\n\n\n"); printf("Type RETURN to continue: "); gets(buf); printf("\n"); return (1); } return (0); } /*******************************************************/ /* */ /* add_float() simulates the addition of two */ /* values stored in floating-point notation. */ /* */ /*******************************************************/ void add_float() { int sign1, sign2, exp1, exp2; long mantissa1, mantissa2; int answer, exponent = 4, sign_ans = 0; mantissa1 = reg[x] % 16; mantissa2 = reg[y] % 16; /* separate into components */ sign1 = reg[x] / 128; sign2 = (int) reg[y] / 128; exp1 = reg[x] / 16; exp1 = (exp1 % 8) - 4; exp2 = reg[y] / 16; exp2 = (exp2 % 8) - 4; mantissa1 = mantissa1 << 4; /* Get ready to align mantissas */ mantissa2 = mantissa2 << 4; if (exp1 > 0) mantissa1 = mantissa1 << exp1; /* Align mantissas */ else mantissa1 = mantissa1 >> (-1 * exp1); if (exp2 > 0) mantissa2 = mantissa2 << exp2; else mantissa2 = mantissa2 >> (-1 * exp2); if (sign1 == 1) /* Negate mantissas that */ mantissa1 *= -1; /* represent negative values, */ if (sign2 == 1) mantissa2 *= -1; answer = mantissa1 + mantissa2; /* and then add them. */ if (answer < 0) /* Get absolute value of */ { answer *= -1; /* answer. */ sign_ans = 1; } while ((answer > 255 || answer < 128) && answer != 0) { if (answer > 255) /* Move answer so leftmost 1 */ { answer = answer >> 1; /* is on 8th bit (to normalize */ exponent++; /* answer). */ } else { answer = answer << 1; exponent--; } } answer = answer >> 4; /* Align answer in rightmost four bits. */ if (answer == 0) /* Make entire pattern 0 if answer is 0. */ { exponent = 0; sign_ans = 0; } if (sign_ans == 1) /* Add 1 to 8th bit if answer is negative. */ answer = answer + 128; exponent = exponent << 4; /* Insert exponent into answer. */ answer = answer + exponent; reg[operand_1] = answer; } /*******************************************************/ /* */ /* rotate() rotates the contents of the register */ /* indicated by operand_1 by one bit y times. */ /* */ /*******************************************************/ void rotate() { int i, p; for (i = 1; i <= y; i++) { p = reg[operand_1] % 2; p = p << 7; reg[operand_1] = reg[operand_1] / 2; reg[operand_1] = reg[operand_1] | p; } } /*******************************************************/ /* */ /* jump() simulates the machine language branch */ /* instruction by changing the program counter */ /* if reg[operand_1] equals reg[0]. */ /* */ /*******************************************************/ void jump() { if (reg[operand_1] == reg[0]) pc = (x * 16) + y; } /*******************************************************/ /* */ /* That's all folks! */ /* */ /*******************************************************/