
/***************************************************************/
/*                                                             */
/*   LC-3b Simulator                                           */
/*                                                             */
/*   ECE 3056A                                                 */
/*   Courtesy "The University of Texas at Austin"              */
/*                                                             */
/***************************************************************/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/***************************************************************/
/*                                                             */
/* 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();
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

/***************************************************************/
/* Definition of bit order in control store word.              */

/***************************************************************/
enum CS_BITS {
    IRD,
    COND1, COND0,
    J5, J4, J3, J2, J1, J0,
    LD_MAR,
    LD_MDR,
    LD_IR,
    LD_BEN,
    LD_REG,
    LD_CC,
    LD_PC,
    GATE_PC,
    GATE_MDR,
    GATE_ALU,
    GATE_MARMUX,
    GATE_SHF,
    PCMUX1, PCMUX0,
    DRMUX,
    SR1MUX,
    ADDR1MUX,
    ADDR2MUX1, ADDR2MUX0,
    MARMUX,
    ALUK1, ALUK0,
    MIO_EN,
    R_W,
    DATA_SIZE,
    LSHF1,
    CONTROL_STORE_BITS
} CS_BITS;

/***************************************************************
 * HELPER FUNCTIONS
 ***************************************************************/
void zeroExtension(char *input, char *result);
void getBinary(int num, char *result);
int binaryToInteger(char *result);
void signExtension(char *result, int number);
void rightArithmeticShift(char *value, int shift, int num);

/***************************************************************/
/* Functions to get at the control bits.                       */

/***************************************************************/
int GetIRD(int *x) {
    return (x[IRD]);
}

int GetCOND(int *x) {
    return ((x[COND1] << 1) + x[COND0]);
}

int GetJ(int *x) {
    return ((x[J5] << 5) + (x[J4] << 4) +
            (x[J3] << 3) + (x[J2] << 2) +
            (x[J1] << 1) + x[J0]);
}

int GetLD_MAR(int *x) {
    return (x[LD_MAR]);
}

int GetLD_MDR(int *x) {
    return (x[LD_MDR]);
}

int GetLD_IR(int *x) {
    return (x[LD_IR]);
}

int GetLD_BEN(int *x) {
    return (x[LD_BEN]);
}

int GetLD_REG(int *x) {
    return (x[LD_REG]);
}

int GetLD_CC(int *x) {
    return (x[LD_CC]);
}

int GetLD_PC(int *x) {
    return (x[LD_PC]);
}

int GetGATE_PC(int *x) {
    return (x[GATE_PC]);
}

int GetGATE_MDR(int *x) {
    return (x[GATE_MDR]);
}

int GetGATE_ALU(int *x) {
    return (x[GATE_ALU]);
}

int GetGATE_MARMUX(int *x) {
    return (x[GATE_MARMUX]);
}

int GetGATE_SHF(int *x) {
    return (x[GATE_SHF]);
}

int GetPCMUX(int *x) {
    return ((x[PCMUX1] << 1) + x[PCMUX0]);
}

int GetDRMUX(int *x) {
    return (x[DRMUX]);
}

int GetSR1MUX(int *x) {
    return (x[SR1MUX]);
}

int GetADDR1MUX(int *x) {
    return (x[ADDR1MUX]);
}

int GetADDR2MUX(int *x) {
    return ((x[ADDR2MUX1] << 1) + x[ADDR2MUX0]);
}

int GetMARMUX(int *x) {
    return (x[MARMUX]);
}

int GetALUK(int *x) {
    return ((x[ALUK1] << 1) + x[ALUK0]);
}

int GetMIO_EN(int *x) {
    return (x[MIO_EN]);
}

int GetR_W(int *x) {
    return (x[R_W]);
}

int GetDATA_SIZE(int *x) {
    return (x[DATA_SIZE]);
}

int GetLSHF1(int *x) {
    return (x[LSHF1]);
}

/***************************************************************/
/* The control store rom.                                      */
/***************************************************************/
int CONTROL_STORE[CONTROL_STORE_ROWS][CONTROL_STORE_BITS];

/***************************************************************/
/* 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 */

    int READY; /* ready bit */
    /* The ready bit is also latched as you dont want the memory system to assert it 
       at a bad point in the cycle*/

    int REGS[LC_3b_REGS]; /* register file. */

    int MICROINSTRUCTION[CONTROL_STORE_BITS]; /* 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;
int MEMORY_COUNT = 0;


int GatePC, GateMARMUX, GateMDR, GateALU, GateSHF, ADDER_VALUE, TEMPORARY_MULTIPLEXER;

/***************************************************************/
/*                                                             */
/* 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() {

    eval_micro_sequencer();
    cycle_memory();
    eval_bus_drivers();
    drive_bus();
    latch_datapath_values();

    CURRENT_LATCHES = NEXT_LATCHES;

    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("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, "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, index;
    char line[200];

    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;

        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. */
            CONTROL_STORE[i][j] = (line[index] == '0') ? 0 : 1;
            index++;
        }

        /* 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.Z = 1;
    CURRENT_LATCHES.STATE_NUMBER = INITIAL_STATE_NUMBER;
    memcpy(CURRENT_LATCHES.MICROINSTRUCTION, CONTROL_STORE[INITIAL_STATE_NUMBER], sizeof (int) *CONTROL_STORE_BITS);

    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 <micro_code_file> <program_file_1> <program_file_2> ...\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.

   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.
     */

    //Fetch all the values from the current microinstruction which
    //decides the address for the next microinstruction.

    int cond0 = CURRENT_LATCHES.MICROINSTRUCTION[COND0];
    int cond1 = CURRENT_LATCHES.MICROINSTRUCTION[COND1];
    int ready_bit = CURRENT_LATCHES.READY;
    int j0 = CURRENT_LATCHES.MICROINSTRUCTION[J0];
    int j1 = CURRENT_LATCHES.MICROINSTRUCTION[J1];
    int j2 = CURRENT_LATCHES.MICROINSTRUCTION[J2];
    int j3 = CURRENT_LATCHES.MICROINSTRUCTION[J3];
    int j4 = CURRENT_LATCHES.MICROINSTRUCTION[J4];
    int j5 = CURRENT_LATCHES.MICROINSTRUCTION[J5];
    int branch_enable_bit = CURRENT_LATCHES.BEN;
    int ir11 = (CURRENT_LATCHES.IR & 0x800) >> 11;
    int ird = GetIRD(CURRENT_LATCHES.MICROINSTRUCTION);

    if (ird == 0) {
        int branch = cond1 & ~cond0 & branch_enable_bit;
        int j2NextState = j2 | branch;
        int ready = ~cond1 & cond0 & ready_bit;
        int j1NextState = j1 | ready;
        int addrMode = cond1 & cond0 & ir11;
        int j0NextState = j0 | addrMode;
        NEXT_LATCHES.STATE_NUMBER = (j5 << 5) + (j4 << 4) + (j3 << 3) +
                (j2NextState << 2) + (j1NextState << 1) + j0NextState;
    } else if (ird == 1) {
        int ir15to12 = (CURRENT_LATCHES.IR & 0xF000) >> 12;
        NEXT_LATCHES.STATE_NUMBER = ir15to12;
    }
    int i;
    for (i = 0; i < CONTROL_STORE_BITS; i++) {
        NEXT_LATCHES.MICROINSTRUCTION[i] = CONTROL_STORE[NEXT_LATCHES.STATE_NUMBER][i]; 
    }
}

void cycle_memory() {

    /* 
     * This function emulates memory and the WE logic. 
     * Keep track of which cycle of MEMEN we are dealing with.  
     * If fourth, we need to latch Ready bit at the end of 
     * cycle to prepare microsequencer for the fifth cycle.  
     */

    int MIOEN = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);
    int MAR = CURRENT_LATCHES.MAR & 0x0001;
    int MemRow = CURRENT_LATCHES.MAR > 1;
    int MemCol = CURRENT_LATCHES.MAR % 2;
    int readWrite = GetR_W(CURRENT_LATCHES.MICROINSTRUCTION);
    int dataSize = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
    if (MIOEN == 0) {
        MEMORY_COUNT = 0;
        CURRENT_LATCHES.READY = 1;
    } else if (MIOEN == 1) {
        if (MEMORY_COUNT < 3) {
            MEMORY_COUNT++;
            NEXT_LATCHES.READY = 0;
        } else {
            NEXT_LATCHES.READY = 1;
            MEMORY_COUNT = 0;
            if (readWrite == 1) {
                if (dataSize == 1) {
                    MEMORY[MemRow][0] = CURRENT_LATCHES.MDR & 0x00FF;
                    MEMORY[MemRow][1] = (CURRENT_LATCHES.MDR & 0xFF00) > 8;
                } else if (dataSize == 0) {
                    if (MAR == 1) {
                        MEMORY[MemRow][MemCol] = ((CURRENT_LATCHES.MDR & 0xFF00) > 8);
                    } else if (MAR == 0) {
                        MEMORY[MemRow][MemCol] = (CURRENT_LATCHES.MDR & 0x00FF);
                    }
                }
            }
        }
    }
}

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
     */

    int ADDR1MUXEnable = GetADDR1MUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int ADDR1MUXOut;
    int ADDR2MUXEnable = GetADDR2MUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int ADDR2MUXOut;
    int LSHF1Enable = GetLSHF1(CURRENT_LATCHES.MICROINSTRUCTION);
    int MARMUXEnable = GetMARMUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int MARMUXOut;
    char sEXTADDR2MUXOut[16] = "";
    if (ADDR2MUXEnable == 0) {
        ADDR2MUXOut = 0;
    } else if (ADDR2MUXEnable == 1) {
        ADDR2MUXOut = (CURRENT_LATCHES.IR) & 0x003F;
        if (((ADDR2MUXOut & 0x10) > 5) == 1) {
            getBinary(ADDR2MUXOut, sEXTADDR2MUXOut);
            signExtension(sEXTADDR2MUXOut, 5);
            ADDR2MUXOut = Low16bits(binaryToInteger(sEXTADDR2MUXOut));
        }
    } else if (ADDR2MUXEnable == 2) {
        ADDR2MUXOut = (CURRENT_LATCHES.IR) & 0x01FF;
        if (((ADDR2MUXOut & 0x100) > 8) == 1) {
            getBinary(ADDR2MUXOut, sEXTADDR2MUXOut);
            signExtension(sEXTADDR2MUXOut, 5);
            ADDR2MUXOut = Low16bits(binaryToInteger(sEXTADDR2MUXOut));
        }
    } else if (ADDR2MUXEnable == 3) {
        ADDR2MUXOut = (CURRENT_LATCHES.IR) & 0x07FF;
        if (((ADDR2MUXOut & 0x400) > 10) == 1) {
            getBinary(ADDR2MUXOut, sEXTADDR2MUXOut);
            signExtension(sEXTADDR2MUXOut, 5);
            ADDR2MUXOut = Low16bits(binaryToInteger(sEXTADDR2MUXOut));
        }
    }

    if (LSHF1Enable == 1) {
        ADDR2MUXOut = ADDR2MUXOut << 1;
    }

    if (ADDR1MUXEnable == 1) {
        ADDR1MUXOut = CURRENT_LATCHES.PC;
    } else if (ADDR1MUXEnable == 0) {
        int temp = ((CURRENT_LATCHES.IR) & 0x01C0) > 6;
        ADDR1MUXOut = CURRENT_LATCHES.REGS[temp];
    }

    ADDER_VALUE = ADDR2MUXOut + ADDR1MUXOut;

    if (MARMUXEnable == 0) { /*select LSHF(ZEXT(IR[7:0]),1) */
        MARMUXOut = Low16bits(CURRENT_LATCHES.IR << 1);
    } else if (MARMUXEnable == 1) {
        MARMUXOut = Low16bits(ADDER_VALUE);
    }
    
    GateMARMUX = MARMUXOut;
    
    GatePC = CURRENT_LATCHES.PC;

    int ALUKEnable = GetALUK(CURRENT_LATCHES.MICROINSTRUCTION);
    int SR1MUXEnable = GetSR1MUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int SR1Out;
    int SR2Out;
    char* sEXTSR2 = "";
    int IR5 = ((CURRENT_LATCHES.IR) & 0x0020) > 5;

    if (SR1MUXEnable == 0) {
        SR1Out = ((CURRENT_LATCHES.IR) & (0x0E00)) > 9;
    } else if (SR1MUXEnable == 1) {
        SR1Out = ((CURRENT_LATCHES.IR) & 0x01C0) > 6;
    }
    if (IR5 == 0) {
        SR2Out = CURRENT_LATCHES.REGS[(Low16bits(CURRENT_LATCHES.IR) & 0x0007)];
    } else if (IR5 == 1) {
        SR2Out = (CURRENT_LATCHES.IR) & 0x001F;
        if (((SR2Out & 0x10) > 3) == 1) {
            getBinary(SR2Out, sEXTSR2);
            signExtension(sEXTSR2, 4);
            SR2Out = Low16bits(binaryToInteger(sEXTSR2));
        }
    }

    if (ALUKEnable == 0) {
        GateALU = CURRENT_LATCHES.REGS[SR1Out] + SR2Out;
    } else if (ALUKEnable == 1) {
        GateALU = CURRENT_LATCHES.REGS[SR1Out] & SR2Out;
    } else if (ALUKEnable == 2) {
        GateALU = CURRENT_LATCHES.REGS[SR1Out] ^ SR2Out;
    } else if (ALUKEnable == 3) {
        GateALU = CURRENT_LATCHES.REGS[SR1Out];
    }

    int SHIFTEnable = ((CURRENT_LATCHES.IR) & 0x0030) > 4;
    int SHIFTAMT = (CURRENT_LATCHES.IR) & 0x000F;
    char *shiftTemp = "";

    if (SHIFTEnable == 0) {
        GateSHF = CURRENT_LATCHES.REGS[SR1Out] << SHIFTAMT;
    } else if (SHIFTEnable == 1) {
        GateSHF = (CURRENT_LATCHES.REGS[SR1Out] > SHIFTAMT);
    } else if (SHIFTEnable == 3) {
        rightArithmeticShift(shiftTemp, SHIFTAMT, CURRENT_LATCHES.REGS[SR1Out]);
        GateSHF = binaryToInteger(shiftTemp);
    }

    int DataSize = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
    int MAR = CURRENT_LATCHES.MAR & 0x0001;

    if (DataSize == 0) {
        if (MAR == 0) {
            GateMDR = Low16bits(CURRENT_LATCHES.MDR & 0x00FF);
        } else if (MAR == 1) {
            GateMDR = Low16bits(CURRENT_LATCHES.MDR & 0xFF00);
        }
    } else if (DataSize == 1) {
        GateMDR = CURRENT_LATCHES.MDR;
    }
}

void drive_bus() {
    /* 
     * Datapath routine for driving the bus from one of the 5 possible 
     * tristate drivers.
     */
    int MARGateEnable = GetGATE_MARMUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int PCGateEnable = GetGATE_PC(CURRENT_LATCHES.MICROINSTRUCTION);
    int MDRGateEnable = GetGATE_MDR(CURRENT_LATCHES.MICROINSTRUCTION);
    int ALUGateEnable = GetGATE_ALU(CURRENT_LATCHES.MICROINSTRUCTION);
    int SHIFTGateEnable = GetGATE_SHF(CURRENT_LATCHES.MICROINSTRUCTION);
    if (CURRENT_LATCHES.READY == 1) {
        if (MARGateEnable == 1) {
            BUS = GateMARMUX;
        } else if (PCGateEnable == 1) {
            BUS = GatePC;
        } else if (MDRGateEnable == 1) {
            BUS = GateMDR;
        } else if (ALUGateEnable == 1) {
            BUS = GateALU;
        } else if (SHIFTGateEnable == 1) {
            BUS = GateSHF;
        }else{
                BUS = 0;
        }
    }else{
        BUS = 0;
    }
}

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.
     */

    int LOAD_IR = GetLD_IR(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_MAR = GetLD_MAR(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_MDR = GetLD_MDR(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_BEN = GetLD_BEN(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_REG = GetLD_REG(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_CC = GetLD_CC(CURRENT_LATCHES.MICROINSTRUCTION);
    int LOAD_PC = GetLD_PC(CURRENT_LATCHES.MICROINSTRUCTION);
    int PCMux = GetPCMUX(CURRENT_LATCHES.MICROINSTRUCTION);
    int MEMIOEnable = GetMIO_EN(CURRENT_LATCHES.MICROINSTRUCTION);
    int MAR = CURRENT_LATCHES.MAR & 0x0001;
    int DataSize = GetDATA_SIZE(CURRENT_LATCHES.MICROINSTRUCTION);
    int DRMux = GetDRMUX(CURRENT_LATCHES.MICROINSTRUCTION);

    if (CURRENT_LATCHES.READY == 1) {
        if (LOAD_IR == 1) {
            NEXT_LATCHES.IR = BUS;
        }
        if (LOAD_PC == 1) {
            if (PCMux == 0) {
                NEXT_LATCHES.PC = Low16bits(CURRENT_LATCHES.PC + 2);
            } else if (PCMux == 1) {
                NEXT_LATCHES.PC = Low16bits(BUS);
            } else if (PCMux == 2) {
                NEXT_LATCHES.PC = Low16bits(ADDER_VALUE);
            }
        }

        if (LOAD_MAR == 1) {
            NEXT_LATCHES.MAR = BUS;
        }

        int temporary;

        if (LOAD_MDR == 1) {
            if (MEMIOEnable == 0) {
                if (DataSize == 0) {
                    temporary = ((BUS & 0x00FF) << 8) + (BUS & 0x00FF);
                    if (MAR == 0) {
                        NEXT_LATCHES.MDR = Low16bits(temporary & 0x00FF);
                    } else if (MAR == 1) {
                        NEXT_LATCHES.MDR = Low16bits((temporary & 0xFF00) >> 8);
                    }
                } else if (DataSize == 1) {
                    NEXT_LATCHES.MDR = BUS;
                }
            } else if (MEMIOEnable == 1) {
                int row = CURRENT_LATCHES.MAR>>1;
                int leftHalf = Low16bits((MEMORY[row][1] << 8) & 0xFF00);
                int rightHalf = Low16bits((MEMORY[row][0] & 0x00FF));
                NEXT_LATCHES.MDR = leftHalf | rightHalf;
            }
        }

        int DR_REG;
        if (LOAD_REG == 1) {
            if (DRMux == 0) {
                DR_REG = (CURRENT_LATCHES.IR & 0xE00) > 9;
            } else {
                DR_REG = 7;
            }
            NEXT_LATCHES.REGS[DR_REG] = Low16bits(BUS);
        }

        if (LOAD_CC == 1) {
            int temporary;
            temporary = (BUS & 0x8000) > 15;

            if (BUS < 0 || temporary == 1) {
                NEXT_LATCHES.P = Low16bits(0);
                NEXT_LATCHES.Z = Low16bits(0);
                NEXT_LATCHES.N = Low16bits(1);
            } else if (BUS == 0) {
                NEXT_LATCHES.P = Low16bits(0);
                NEXT_LATCHES.Z = Low16bits(1);
                NEXT_LATCHES.N = Low16bits(0);
            } else {
                NEXT_LATCHES.P = Low16bits(1);
                NEXT_LATCHES.Z = Low16bits(0);
                NEXT_LATCHES.N = Low16bits(0);
            }
        }

        if (LOAD_BEN == 1) {
            int n = (CURRENT_LATCHES.IR & 0x0800) > 11;
            int z = (CURRENT_LATCHES.IR & 0x0400) > 10;
            int p = (CURRENT_LATCHES.IR & 0x0200) > 9;

            if ((n & CURRENT_LATCHES.N) || (z & CURRENT_LATCHES.Z) || (p & CURRENT_LATCHES.P)) {
                NEXT_LATCHES.BEN = 1;
            } else {
                NEXT_LATCHES.BEN = 0;
            }
        }
    }
}

void zeroExtension(char *input, char *result) {
    char prefix[8];
    strcpy(prefix, "00000000");
    strncat(result, prefix, 8);
    strncat(result, input + 24, 8);
}

void getBinary(int num, char *result) {
    *(result + 16) = '\0';
    int mask = 0x8000 << 1;
    while (mask >= 1)
        *result++ = !!(mask & num) + '0';
}

int binaryToInteger(char *result) {
    int i;
    int temporary;
    int out = 0;
    int len = strlen(result);
    for (i = 0; i < len; i++) {
        temporary = result[i] - ((int) '0');
        out = out * 2 + temporary;
    }
    return out;

}

void signExtension(char *result, int number) {
    int i;
    for (i = 0; i < (15-number); i++) {
    result[i] = '1';
    }
}

void rightArithmeticShift(char *value, int shift, int num) {
    char temp[16] = "";
    getBinary(num, temp);
    char first = temp[0];
    num = num >> shift;
    getBinary(num, value);
    int i;
    for (i = 0; i < shift; i++) {
        value[i] = first;
    }
}
