/***************************************************************/ /* */ /* LC-3b Simulator */ /* */ /* EE 360N */ /* The University of Texas at Austin */ /* */ /***************************************************************/ #include #include #include /***************************************************************/ /* */ /* Files: ucode Microprogram file */ /* isaprogram LC-3b machine language program file */ /* */ /***************************************************************/ /***************************************************************/ /* These are the functions you'll have to write. */ /***************************************************************/ void eval_micro_sequencer(); void cycle_memory_IO(); void eval_bus_drivers(); void drive_bus(); void latch_datapath_values(); /***************************************************************/ /* A couple of useful definitions. */ /***************************************************************/ #define FALSE 0 #define TRUE 1 /***************************************************************/ /* Use this to avoid overflowing 16 bits on the bus. */ /***************************************************************/ #define Low16bits(x) (x & 0xFFFF) /***************************************************************/ /* Definition of the control store layout. */ /***************************************************************/ #define CONTROL_STORE_ROWS 64 #define INITIAL_STATE_NUMBER 18 #define CONTROL_STORE_BITS 35 /***************************************************************/ /* Definition of bit order in control store word. */ /***************************************************************/ typedef struct { unsigned long long LSHF1 : 1; unsigned long long DATA_SIZE : 1; unsigned long long R_W : 1; unsigned long long MIO_EN : 1; unsigned long long ALUK : 2; unsigned long long MARMUX : 1; unsigned long long ADDR2MUX : 2; unsigned long long ADDR1MUX : 1; unsigned long long SR1MUX : 1; unsigned long long DRMUX : 1; unsigned long long PCMUX : 2; unsigned long long GATE_SHF : 1; unsigned long long GATE_MARMUX : 1; unsigned long long GATE_ALU : 1; unsigned long long GATE_MDR : 1; unsigned long long GATE_PC : 1; unsigned long long LD_PC : 1; unsigned long long LD_CC : 1; unsigned long long LD_REG : 1; unsigned long long LD_BEN : 1; unsigned long long LD_IR : 1; unsigned long long LD_MDR : 1; unsigned long long LD_MAR : 1; unsigned long long J : 6; unsigned long long COND : 2; unsigned long long IRD : 1; } ControlStore_BITS; /***************************************************************/ /* The control store rom. */ /***************************************************************/ typedef union { ControlStore_BITS CONTROL_FIELDS ; unsigned long long control_fields ; } CONTROL_STORE_ENTRY ; CONTROL_STORE_ENTRY CONTROL_STORE[CONTROL_STORE_ROWS]; /***************************************************************/ /* Main memory. */ /***************************************************************/ /* MEMORY[A][0] stores the least significant byte of word at word address A MEMORY[A][1] stores the most significant byte of word at word address A There are two write enable signals, one for each byte. WE0 is used for the least significant byte of a word. WE1 is used for the most significant byte of a word. */ #define WORDS_IN_MEM 0x08000 #define MEM_CYCLES 5 int MEMORY[WORDS_IN_MEM][2]; /***************************************************************/ /***************************************************************/ /***************************************************************/ /* LC-3b State info. */ /***************************************************************/ #define LC_3b_REGS 8 int RUN_BIT; /* run bit */ int BUS; /* value of the bus */ typedef struct System_Latches_Struct{ int PC, /* program counter */ MDR, /* memory data register */ MAR, /* memory address register */ IR, /* instruction register */ N, /* n condition bit */ Z, /* z condition bit */ P, /* p condition bit */ BEN, /* ben register */ KBSR, /* Keyboard Status register */ KBDR, /* Keyboard Data register */ DSR, /* Display Status register */ DDR; /* Display Data register */ int READY; /* ready bit */ /* The ready bit is the Logical OR of the READY_MEM and READY_IO. The ready bit is also latched as you dont want the memory/IO system to assert it at a bad point in the cycle. */ int REGS[LC_3b_REGS]; /* register file. */ CONTROL_STORE_ENTRY MICROINSTRUCTION; /* The microintruction */ int STATE_NUMBER; /* Current State Number - Provided for debugging */ } System_Latches; /* Data Structure for Latch */ System_Latches CURRENT_LATCHES, NEXT_LATCHES; /***************************************************************/ /* A cycle counter. */ /***************************************************************/ int CYCLE_COUNT; /***************************************************************/ /* */ /* Procedure : help */ /* */ /* Purpose : Print out a list of commands. */ /* */ /***************************************************************/ void help() { printf("----------------LC-3bSIM Help-------------------------\n"); printf("go - run program to completion \n"); printf("run n - execute program for n cycles \n"); printf("mdump low high - dump memory from low to high \n"); printf("rdump - dump the register & bus values \n"); printf("? - display this help menu \n"); printf("quit - exit the program \n\n"); } /***************************************************************/ /* */ /* Procedure : cycle */ /* */ /* Purpose : Execute a cycle */ /* */ /***************************************************************/ void cycle() { CURRENT_LATCHES = NEXT_LATCHES; eval_micro_sequencer(); cycle_memory_IO(); eval_bus_drivers(); drive_bus(); latch_datapath_values(); CYCLE_COUNT++; } /***************************************************************/ /* */ /* Procedure : run n */ /* */ /* Purpose : Simulate the LC-3b for n cycles. */ /* */ /***************************************************************/ void run(int num_cycles) { int i; if (RUN_BIT == FALSE) { printf("Can't simulate, Simulator is halted\n\n"); return; } printf("Simulating for %d cycles...\n\n", num_cycles); for (i = 0; i < num_cycles; i++) { if (CURRENT_LATCHES.PC == 0x0000) { RUN_BIT = FALSE; printf("Simulator halted\n\n"); break; } cycle(); } } /***************************************************************/ /* */ /* Procedure : go */ /* */ /* Purpose : Simulate the LC-3b until HALTed. */ /* */ /***************************************************************/ void go() { if (RUN_BIT == FALSE) { printf("Can't simulate, Simulator is halted\n\n"); return; } printf("Simulating...\n\n"); while (CURRENT_LATCHES.PC != 0x0000) cycle(); RUN_BIT = FALSE; printf("Simulator halted\n\n"); } /***************************************************************/ /* */ /* Procedure : mdump */ /* */ /* Purpose : Dump a word-aligned region of memory to the */ /* output file. */ /* */ /***************************************************************/ void mdump(FILE * dumpsim_file, int start, int stop) { int address; /* this is a byte address */ printf("\nMemory content [0x%0.4x..0x%0.4x] :\n", start, stop); printf("-------------------------------------\n"); for (address = (start >> 1); address <= (stop >> 1); address++) printf(" 0x%0.4x (%d) : 0x%0.2x%0.2x\n", address << 1, address << 1, MEMORY[address][1], MEMORY[address][0]); printf("\n"); /* dump the memory contents into the dumpsim file */ fprintf(dumpsim_file, "\nMemory content [0x%0.4x..0x%0.4x] :\n", start, stop); fprintf(dumpsim_file, "-------------------------------------\n"); for (address = (start >> 1); address <= (stop >> 1); address++) fprintf(dumpsim_file, " 0x%0.4x (%d) : 0x%0.2x%0.2x\n", address << 1, address << 1, MEMORY[address][1], MEMORY[address][0]); fprintf(dumpsim_file, "\n"); } /***************************************************************/ /* */ /* Procedure : rdump */ /* */ /* Purpose : Dump current register and bus values to the */ /* output file. */ /* */ /***************************************************************/ void rdump(FILE * dumpsim_file) { int k; printf("\nCurrent register/bus values :\n"); printf("-------------------------------------\n"); printf("Cycle Count : %d\n", CYCLE_COUNT); printf("PC : 0x%0.4x\n", CURRENT_LATCHES.PC); printf("IR : 0x%0.4x\n", CURRENT_LATCHES.IR); printf("STATE_NUMBER : 0x%0.4x\n\n", CURRENT_LATCHES.STATE_NUMBER); printf("BUS : 0x%0.4x\n", BUS); printf("MDR : 0x%0.4x\n", CURRENT_LATCHES.MDR); printf("MAR : 0x%0.4x\n", CURRENT_LATCHES.MAR); printf("KBSR : 0x%0.4x\n", CURRENT_LATCHES.KBSR); printf("KBDR : 0x%0.4x\n", CURRENT_LATCHES.KBDR); printf("DSR : 0x%0.4x\n", CURRENT_LATCHES.DSR); printf("DDR : 0x%0.4x\n", CURRENT_LATCHES.DDR); printf("CCs: N = %d Z = %d P = %d\n", CURRENT_LATCHES.N, CURRENT_LATCHES.Z, CURRENT_LATCHES.P); printf("Registers:\n"); for (k = 0; k < LC_3b_REGS; k++) printf("%d: 0x%0.4x\n", k, CURRENT_LATCHES.REGS[k]); printf("\n"); /* dump the state information into the dumpsim file */ fprintf(dumpsim_file, "\nCurrent register/bus values :\n"); fprintf(dumpsim_file, "-------------------------------------\n"); fprintf(dumpsim_file, "Cycle Count : %d\n", CYCLE_COUNT); fprintf(dumpsim_file, "PC : 0x%0.4x\n", CURRENT_LATCHES.PC); fprintf(dumpsim_file, "IR : 0x%0.4x\n", CURRENT_LATCHES.IR); fprintf(dumpsim_file, "STATE_NUMBER : 0x%0.4x\n\n", CURRENT_LATCHES.STATE_NUMBER); fprintf(dumpsim_file, "BUS : 0x%0.4x\n", BUS); fprintf(dumpsim_file, "MDR : 0x%0.4x\n", CURRENT_LATCHES.MDR); fprintf(dumpsim_file, "MAR : 0x%0.4x\n", CURRENT_LATCHES.MAR); fprintf(dumpsim_file, "KBSR : 0x%0.4x\n", CURRENT_LATCHES.KBSR); fprintf(dumpsim_file, "KBDR : 0x%0.4x\n", CURRENT_LATCHES.KBDR); fprintf(dumpsim_file, "DSR : 0x%0.4x\n", CURRENT_LATCHES.DSR); fprintf(dumpsim_file, "DDR : 0x%0.4x\n", CURRENT_LATCHES.DDR); fprintf(dumpsim_file, "CCs: N = %d Z = %d P = %d\n", CURRENT_LATCHES.N, CURRENT_LATCHES.Z, CURRENT_LATCHES.P); fprintf(dumpsim_file, "Registers:\n"); for (k = 0; k < LC_3b_REGS; k++) fprintf(dumpsim_file, "%d: 0x%0.4x\n", k, CURRENT_LATCHES.REGS[k]); fprintf(dumpsim_file, "\n"); } /***************************************************************/ /* */ /* Procedure : get_command */ /* */ /* Purpose : Read a command from standard input. */ /* */ /***************************************************************/ void get_command(FILE * dumpsim_file) { char buffer[20]; int start, stop, cycles; printf("LC-3b-SIM> "); scanf("%s", buffer); printf("\n"); switch(buffer[0]) { case 'G': case 'g': go(); break; case 'M': case 'm': scanf("%i %i", &start, &stop); mdump(dumpsim_file, start, stop); break; case '?': help(); break; case 'Q': case 'q': printf("Bye.\n"); exit(0); case 'R': case 'r': if (buffer[1] == 'd' || buffer[1] == 'D') rdump(dumpsim_file); else { scanf("%d", &cycles); run(cycles); } break; default: printf("Invalid Command\n"); break; } } /***************************************************************/ /* */ /* Procedure : init_control_store */ /* */ /* Purpose : Load microprogram into control store ROM */ /* */ /***************************************************************/ void init_control_store(char *ucode_filename) { FILE *ucode; int i, j,k, index; char line[200]; unsigned long long microinstruction = 0; printf("Loading Control Store from file: %s\n", ucode_filename); /* Open the micro-code file. */ if ((ucode = fopen(ucode_filename, "r")) == NULL) { printf("Error: Can't open micro-code file %s\n", ucode_filename); exit(-1); } /* Read a line for each row in the control store. */ for(i = 0; i < CONTROL_STORE_ROWS; i++) { if (fscanf(ucode, "%[^\n]\n", line) == EOF) { printf("Error: Too few lines (%d) in micro-code file: %s\n", i, ucode_filename); exit(-1); } /* Put in bits one at a time. */ index = 0; microinstruction = 0; for (j = 0; j < CONTROL_STORE_BITS; j++) { /* Needs to find enough bits in line. */ if (line[index] == '\0') { printf("Error: Too few control bits in micro-code file: %s\nLine: %d\n", ucode_filename, i); exit(-1); } if (line[index] != '0' && line[index] != '1') { printf("Error: Unknown value in micro-code file: %s\nLine: %d, Bit: %d\n", ucode_filename, i, j); exit(-1); } /* Set the bit in the Control Store. */ microinstruction = microinstruction * 2 + ((line[index] == '0') ? 0:1); index++; } CONTROL_STORE[i].control_fields = microinstruction; } /* Warn about extra bits in line. */ if (line[index] != '\0') { printf("Warning: Extra bit(s) in control store file %s. Line: %d\n", ucode_filename, i); } printf("\n"); } /***************************************************************/ /* */ /* Procedure : init_memory */ /* */ /* Purpose : Zero out the memory array */ /* */ /***************************************************************/ void init_memory() { int i; for (i=0; i < WORDS_IN_MEM; i++) { MEMORY[i][0] = 0; MEMORY[i][1] = 0; } } /**************************************************************/ /* */ /* Procedure : load_program */ /* */ /* Purpose : Load program and service routines into mem. */ /* */ /**************************************************************/ void load_program(char *program_filename) { FILE * prog; int ii, word, program_base; /* Open program file. */ prog = fopen(program_filename, "r"); if (prog == NULL) { printf("Error: Can't open program file %s\n", program_filename); exit(-1); } /* Read in the program. */ if (fscanf(prog, "%x\n", &word) != EOF) program_base = word >> 1; else { printf("Error: Program file is empty\n"); exit(-1); } ii = 0; while (fscanf(prog, "%x\n", &word) != EOF) { /* Make sure it fits. */ if (program_base + ii >= WORDS_IN_MEM) { printf("Error: Program file %s is too long to fit in memory. %x\n", program_filename, ii); exit(-1); } /* Write the word to memory array. */ MEMORY[program_base + ii][0] = word & 0x00FF; MEMORY[program_base + ii][1] = (word >> 8) & 0x00FF; ii++; } if (CURRENT_LATCHES.PC == 0) CURRENT_LATCHES.PC = (program_base << 1); printf("Read %d words from program into memory.\n\n", ii); } /***************************************************************/ /* */ /* Procedure : initialize */ /* */ /* Purpose : Load microprogram and machine language program */ /* and set up initial state of the machine. */ /* */ /***************************************************************/ void initialize(char *ucode_filename, char *program_filename, int num_prog_files) { int i; init_control_store(ucode_filename); init_memory(); for ( i = 0; i < num_prog_files; i++ ) { load_program(program_filename); while(*program_filename++ != '\0'); } CURRENT_LATCHES.STATE_NUMBER = INITIAL_STATE_NUMBER; CURRENT_LATCHES.MICROINSTRUCTION = CONTROL_STORE[INITIAL_STATE_NUMBER]; NEXT_LATCHES = CURRENT_LATCHES; RUN_BIT = TRUE; } /***************************************************************/ /* */ /* Procedure : main */ /* */ /***************************************************************/ int main(int argc, char *argv[]) { FILE * dumpsim_file; /* Error Checking */ if (argc < 3) { printf("Error: usage: %s ...\n", argv[0]); exit(1); } printf("LC-3b Simulator\n\n"); initialize(argv[1], argv[2], argc - 2); if ( (dumpsim_file = fopen( "dumpsim", "w" )) == NULL ) { printf("Error: Can't open dumpsim file\n"); exit(-1); } while (1) get_command(dumpsim_file); } /***************************************************************/ /* Do not modify the above code. You are allowed to use the following global variables in your code. These are defined above. CONTROL_STORE MEMORY BUS CURRENT_LATCHES NEXT_LATCHES You may define your own local/global variables and functions. You may use the functions to get at the control bits defined above. You can access the control signals like the following: For example, if you want to access the 6-bit "J" signal for the 18th state, use jbits = CONTROL_STORE[18].CONTROL_FIELDS.J (where jbits is an integer, whose lower 6 bits get set to the value of J ) Begin your code here */ /***************************************************************/ void eval_micro_sequencer() { /* * Evaluate the address of the next state according to the * micro sequencer logic. Latch the next microinstruction. */ } void cycle_memory_IO() { /* * This function emulates memory/IO and the WE logic. * Determine if we are accessing memory or IO. * Keep track of which cycle of MEMEN(or IOEN) we are dealing with. * If fourth, we need to latch Ready bit at the end of * cycle to prepare microsequencer for the fifth cycle. */ } void eval_bus_drivers() { /* * Datapath routine emulating operations before driving the bus. * Evaluate the input of tristate drivers * Gate_MARMUX, * Gate_PC, * Gate_ALU, * Gate_SHF, * Gate_MDR. */ } void drive_bus() { /* * Datapath routine for driving the bus from one of the 5 possible * tristate drivers. */ } void latch_datapath_values() { /* * Datapath routine for computing all functions that need to latch * values in the data path at the end of this cycle. Some values * require sourcing the bus; therefore, this routine has to come * after drive_bus. */ }