#include <iostream>
#include <fstream>
#include <sstream>
#include <bitset>
#include <vector>
#include <unordered_map>
#include <iomanip>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
#include <cmath>
#include <climits>

using namespace std;

enum Section { UNKNOWN, DATA, TEXT };

struct DataItem {
    int address;
    vector<int> values; // To handle arrays
};

string preprocessLine(const string& line, Section& currentSection) {
    string processed = line;
    size_t commentPos = processed.find('#');
    if (commentPos != string::npos) {
        processed = processed.substr(0, commentPos);
    }
    processed.erase(processed.begin(),
                    find_if(processed.begin(), processed.end(), [](unsigned char ch) { return !isspace(ch); }));
    processed.erase(find_if(processed.rbegin(), processed.rend(), [](unsigned char ch) { return !isspace(ch); }).base(),
                    processed.end());

    if (processed.empty()) {
        return processed;
    }

    if (processed == ".data") {
        currentSection = DATA;
        return "";
    } else if (processed == ".text") {
        currentSection = TEXT;
        return "";
    }

    return processed;
}

int parseRegister(const string& reg) {
    if (reg[0] != '$') {
        cerr << "Invalid register: " << reg << "\n";
        exit(1);
    }
    string regName = reg.substr(1);

    static unordered_map<string, int> regMap = {
        {"zero", 0}, {"0", 0}, {"at", 1}, {"1", 1},
        {"v0", 2}, {"2", 2}, {"v1", 3}, {"3", 3},
        {"a0", 4}, {"4", 4}, {"a1", 5}, {"5", 5}, {"a2", 6}, {"6", 6}, {"a3", 7}, {"7", 7},
        {"t0", 8}, {"8", 8}, {"t1", 9}, {"9", 9}, {"t2", 10}, {"10", 10}, {"t3", 11}, {"11", 11},
        {"t4", 12}, {"12", 12}, {"t5", 13}, {"13", 13}, {"t6", 14}, {"14", 14}, {"t7", 15}, {"15", 15},
        {"s0", 16}, {"16", 16}, {"s1", 17}, {"17", 17}, {"s2", 18}, {"18", 18}, {"s3", 19}, {"19", 19},
        {"s4", 20}, {"20", 20}, {"s5", 21}, {"21", 21}, {"s6", 22}, {"22", 22}, {"s7", 23}, {"23", 23},
        {"t8", 24}, {"24", 24}, {"t9", 25}, {"25", 25},
        {"k0", 26}, {"26", 26}, {"k1", 27}, {"27", 27},
        {"gp", 28}, {"28", 28}, {"sp", 29}, {"29", 29}, {"fp", 30}, {"30", 30}, {"ra", 31}, {"31", 31}
    };

    if (regMap.find(regName) != regMap.end()) {
        return regMap[regName];
    } else {
        cerr << "Unknown register: " << reg << "\n";
        exit(1);
    }
}

int parseImmediate(const string& immStr, const unordered_map<string, DataItem>& dataSymbolTable) {
    int imm = 0;
    size_t plusPos = immStr.find('+');
    size_t minusPos = immStr.find('-', 1);
    try {
        if (plusPos != string::npos) {
            string label = immStr.substr(0, plusPos);
            string offsetStr = immStr.substr(plusPos + 1);
            int offset = stoi(offsetStr);
            if (dataSymbolTable.count(label)) {
                imm = dataSymbolTable.at(label).address + offset;
            } else {
                cerr << "Undefined label: " << label << "\n";
                exit(1);
            }
        } else if (minusPos != string::npos) {
            string label = immStr.substr(0, minusPos);
            string offsetStr = immStr.substr(minusPos);
            int offset = stoi(offsetStr);
            if (dataSymbolTable.count(label)) {
                imm = dataSymbolTable.at(label).address + offset;
            } else {
                cerr << "Undefined label: " << label << "\n";
                exit(1);
            }
        } else if (dataSymbolTable.count(immStr)) {
            imm = dataSymbolTable.at(immStr).address;
        } else if (immStr.substr(0, 2) == "0x" || immStr.substr(0, 2) == "0X") {
            imm = stoi(immStr, nullptr, 16);
        } else if (immStr.substr(0, 1) == "-" || isdigit(immStr[0])) {
            imm = stoi(immStr);
        } else {
            cerr << "Invalid immediate value: " << immStr << "\n";
            exit(1);
        }
    } catch (const invalid_argument& e) {
        cerr << "Invalid immediate value: " << immStr << "\n";
        exit(1);
    } catch (const out_of_range& e) {
        cerr << "Immediate value out of range: " << immStr << "\n";
        exit(1);
    }
    return imm;
}

vector<string> assembleInstruction(const string& instructionLine, int lineNumber,
                                   unordered_map<string, int>& labelAddresses,
                                   const unordered_map<string, int>& opcodes,
                                   const unordered_map<string, int>& functCodes,
                                   const unordered_map<string, string>& instructionTypes,
                                   const unordered_map<string, DataItem>& dataSymbolTable) {
    vector<string> binaryCodes;
    size_t commentPos = instructionLine.find('#');
    string instruction = instructionLine;
    if (commentPos != string::npos) {
        instruction = instructionLine.substr(0, commentPos);
    }
    instruction.erase(remove(instruction.begin(), instruction.end(), ','), instruction.end());
    istringstream iss(instruction);
    vector<string> parts{istream_iterator<string>{iss}, istream_iterator<string>{}};

    if (parts.empty()) {
        return binaryCodes;
    }

    if (parts[0].back() == ':') {
        string label = parts[0].substr(0, parts[0].length() - 1);
        labelAddresses[label] = lineNumber;
        parts.erase(parts.begin());
        if (parts.empty()) {
            return binaryCodes;
        }
    }

    if (parts.empty()) {
        return binaryCodes;
    }

    string opcodeStr = parts[0];
    string binaryCode;

    if (instructionTypes.find(opcodeStr) == instructionTypes.end()) {
        cerr << "Unsupported instruction: " << opcodeStr << "\n";
        exit(1);
    }

    string instrType = instructionTypes.at(opcodeStr);

    if (instrType == "R") {
        int rs = 0, rt = 0, rd = 0, shamt = 0, funct = 0;
        int opcode = 0; // Opcode for R-type instructions is usually 0

        if (functCodes.find(opcodeStr) == functCodes.end()) {
            cerr << "Unsupported R-type instruction: " << opcodeStr << "\n";
            exit(1);
        }
        funct = functCodes.at(opcodeStr);

        if (opcodeStr == "sll" || opcodeStr == "srl") {
            if (parts.size() != 4) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rd = parseRegister(parts[1]);
            rt = parseRegister(parts[2]);
            shamt = parseImmediate(parts[3], dataSymbolTable);
        } else if (opcodeStr == "jr" || opcodeStr == "mthi" || opcodeStr == "mtlo") {
            if (parts.size() != 2) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rs = parseRegister(parts[1]);
        } else if (opcodeStr == "jalr") {
            if (parts.size() == 2) {
                rs = parseRegister(parts[1]);
                rd = 31;
            } else if (parts.size() == 3) {
                rd = parseRegister(parts[1]);
                rs = parseRegister(parts[2]);
            } else {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
        } else if (opcodeStr == "mfhi" || opcodeStr == "mflo") {
            if (parts.size() != 2) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rd = parseRegister(parts[1]);
        } else {
            if (parts.size() != 4) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rd = parseRegister(parts[1]);
            rs = parseRegister(parts[2]);
            rt = parseRegister(parts[3]);
        }

        bitset<6> opcodeBits(opcode);
        bitset<5> rsBits(rs);
        bitset<5> rtBits(rt);
        bitset<5> rdBits(rd);
        bitset<5> shamtBits(shamt);
        bitset<6> functBits(funct);

        binaryCode = opcodeBits.to_string() + rsBits.to_string() + rtBits.to_string() +
                     rdBits.to_string() + shamtBits.to_string() + functBits.to_string();
        binaryCodes.push_back(binaryCode);

    } else if (instrType == "I") {
        int opcode = opcodes.at(opcodeStr);
        int rs = 0, rt = 0;
        int immediate = 0;

        if (opcodeStr == "lw" || opcodeStr == "sw" || opcodeStr == "lui") {
            if (opcodeStr == "lui") {
                if (parts.size() != 3) {
                    cerr << "Invalid format for instruction: " << instructionLine << "\n";
                    exit(1);
                }
                rt = parseRegister(parts[1]);
                immediate = parseImmediate(parts[2], dataSymbolTable);
            } else {
                if (parts.size() != 3) {
                    cerr << "Invalid format for instruction: " << instructionLine << "\n";
                    exit(1);
                }
                rt = parseRegister(parts[1]);
                size_t start = parts[2].find('(');
                size_t end = parts[2].find(')');
                if (start != string::npos && end != string::npos) {
                    string offsetStr = parts[2].substr(0, start);
                    immediate = parseImmediate(offsetStr, dataSymbolTable);
                    rs = parseRegister(parts[2].substr(start + 1, end - start - 1));
                } else {
                    immediate = parseImmediate(parts[2], dataSymbolTable);
                    rs = 0;
                }
            }
        } else if (opcodeStr == "beq" || opcodeStr == "bne" || opcodeStr == "slti") {
            if (parts.size() != 4) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rs = parseRegister(parts[1]);
            rt = parseRegister(parts[2]);
            int labelAddress;
            if (labelAddresses.count(parts[3])) {
                labelAddress = labelAddresses[parts[3]];
                immediate = labelAddress - (lineNumber + 1);
            } else {
                cerr << "Undefined label: " << parts[3] << "\n";
                exit(1);
            }
        } else if (opcodeStr == "li") {
            // 'li' does not produce binary code, handled in simulation
            return {}; // No binary code for li since it's a pseudo-instruction
        } else {
            if (parts.size() != 4) {
                cerr << "Invalid format for instruction: " << instructionLine << "\n";
                exit(1);
            }
            rt = parseRegister(parts[1]);
            rs = parseRegister(parts[2]);
            immediate = parseImmediate(parts[3], dataSymbolTable);
        }

        bitset<6> opcodeBits(opcode);
        bitset<5> rsBits(rs);
        bitset<5> rtBits(rt);
        bitset<16> immBits(immediate);

        binaryCode = opcodeBits.to_string() + rsBits.to_string() + rtBits.to_string() + immBits.to_string();
        binaryCodes.push_back(binaryCode);

    } else if (instrType == "J") {
        int opcode = opcodes.at(opcodeStr);
        int address;

        if (parts.size() != 2) {
            cerr << "Invalid format for instruction: " << instructionLine << "\n";
            exit(1);
        }

        if (labelAddresses.count(parts[1])) {
            address = labelAddresses[parts[1]];
        } else {
            cerr << "Undefined label: " << parts[1] << "\n";
            exit(1);
        }

        bitset<6> opcodeBits(opcode);
        bitset<26> addrBits(address);

        binaryCode = opcodeBits.to_string() + addrBits.to_string();
        binaryCodes.push_back(binaryCode);
    }

    return binaryCodes;
}

void printRegisters(const unordered_map<string, int>& reg) {
    cout << "Registers:\n";
    for (int i = 0; i < 32; i += 4) {
        for (int j = 0; j < 4; ++j) {
            string regName = "$" + to_string(i + j);
            int value = reg.at(regName);
            cout << setw(4) << regName << ": " << setw(10) << value << " | ";
        }
        cout << "\n";
    }
    cout << "\n";
}

void printMemory(const unordered_map<int, int>& memory) {
    cout << "Memory:\n";

    vector<int> addresses;
    for (const auto& [address, value] : memory) {
        if (value != 0) {
            addresses.push_back(address);
        }
    }

    if (addresses.empty()) {
        cout << "\nAll memory values are 0.\n\n";
        return;
    }

    int minAddress = *min_element(addresses.begin(), addresses.end());
    int maxAddress = *max_element(addresses.begin(), addresses.end());

    int startAddress = minAddress - (minAddress % 4);

    while (maxAddress - startAddress < 32 * 4) {
        maxAddress += 4;
    }

    for (int address = startAddress; address <= maxAddress; address += 16) {
        for (int offset = 0; offset < 16; offset += 4) {
            int currentAddress = address + offset;
            int value = memory.count(currentAddress) ? memory.at(currentAddress) : 0;
            cout << "M[" << setw(10) << currentAddress << "]: " << setw(10) << value << " | ";
        }
        cout << "\n";
    }

    cout << "\n";
}

void simulate(const vector<string>& asmList,
              const vector<string>& binList,
              const unordered_map<string, int>& labelAddresses,
              const unordered_map<string, DataItem>& dataSymbolTable,
              unordered_map<int, int>& memory) {
    unordered_map<string, int> reg;
    for (int i = 0; i < 32; ++i) {
        reg["$" + to_string(i)] = 0;
    }
    reg["$29"] = 0x7FFFFFFC;
    int hi = 0, lo = 0;

    int PC = 0;
    unordered_map<string, int> counts = {
        {"Total", 0},
        {"ALU", 0},
        {"Jump", 0},
        {"Branch", 0},
        {"Memory", 0},
        {"Other", 0},
        {"Special", 0}
    };

    cout << "Enter 'n' for single instruction mode, 'a' for automatic mode: ";
    char mode;
    cin >> mode;
    bool single_step = (mode == 'n');

    int numInstructions = static_cast<int>(asmList.size());

    while (PC < numInstructions) {
        string curr = asmList[PC];
        string binCode = binList[PC];

        string line = curr;
        size_t commentPos = line.find('#');
        if (commentPos != string::npos) {
            line = line.substr(0, commentPos);
        }
        line.erase(remove(line.begin(), line.end(), ','), line.end());
        istringstream iss(line);
        vector<string> parts{istream_iterator<string>{iss}, istream_iterator<string>{}};

        if (parts.empty()) {
            PC += 1;
            continue;
        }

        if (single_step) {
            cout << "\n" << string(80, '=') << "\n";
            cout << "Executing Instruction:\n";
            cout << "Line " << PC << ": " << curr << "\n";
            if (!binCode.empty()) {
                cout << "Machine Code: " << binCode << "\n";
            }
            cout << "PC before execution: " << PC << "\n";
        }

        string opcodeStr = parts[0];

        try {
            if (opcodeStr == "add") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = reg["$" + to_string(rs)] + reg["$" + to_string(rt)];
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "addu") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);
                
                reg["$" + to_string(rd)] = static_cast<unsigned int>(reg["$" + to_string(rs)]) + static_cast<unsigned int>(reg["$" + to_string(rt)]);
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "addi") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);
                reg["$" + to_string(rt)] = reg["$" + to_string(rs)] + imm;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "addiu") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);
                reg["$" + to_string(rt)] = static_cast<unsigned int>(reg["$" + to_string(rs)]) + static_cast<unsigned int>(imm);
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "sub") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = reg["$" + to_string(rs)] - reg["$" + to_string(rt)];
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "subu") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = static_cast<unsigned int>(reg["$" + to_string(rs)]) - static_cast<unsigned int>(reg["$" + to_string(rt)]);
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "and") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = reg["$" + to_string(rs)] & reg["$" + to_string(rt)];
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "andi") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);

                reg["$" + to_string(rt)] = reg["$" + to_string(rs)] & imm;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "or") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = reg["$" + to_string(rs)] | reg["$" + to_string(rt)];
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "ori") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);

                reg["$" + to_string(rt)] = reg["$" + to_string(rs)] | imm;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "xor") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = reg["$" + to_string(rs)] ^ reg["$" + to_string(rt)];
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "xori") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);

                reg["$" + to_string(rt)] = reg["$" + to_string(rs)] ^ imm;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "nor") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = ~(reg["$" + to_string(rs)] | reg["$" + to_string(rt)]);
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "sll") {
                int rd = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);
                int shamt = parseImmediate(parts[3], dataSymbolTable);

                reg["$" + to_string(rd)] = reg["$" + to_string(rt)] << shamt;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "mult") {
                int rs = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);

                long long result = static_cast<long long>(reg["$" + to_string(rs)]) * reg["$" + to_string(rt)];
                lo = static_cast<int>(result & 0xFFFFFFFF);
                hi = static_cast<int>(result >> 32);
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "div") {
                int rs = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);

                if (reg["$" + to_string(rt)] != 0) {
                    lo = reg["$" + to_string(rs)] / reg["$" + to_string(rt)];
                    hi = reg["$" + to_string(rs)] % reg["$" + to_string(rt)];
                } else {
                    cerr << "Division by zero error!\n";
                    exit(1);
                }
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "mfhi") {
                int rd = parseRegister(parts[1]);
                reg["$" + to_string(rd)] = hi;
                counts["Special"]++;
                PC += 1;
            }
            else if (opcodeStr == "mflo") {
                int rd = parseRegister(parts[1]);
                reg["$" + to_string(rd)] = lo;
                counts["Special"]++;
                PC += 1;
            }
            else if (opcodeStr == "mthi") {
                int rs = parseRegister(parts[1]);
                hi = reg["$" + to_string(rs)];
                counts["Special"]++;
                PC += 1;
            }
            else if (opcodeStr == "mtlo") {
                int rs = parseRegister(parts[1]);
                lo = reg["$" + to_string(rs)];
                counts["Special"]++;
                PC += 1;
            }
            else if (opcodeStr == "slt") {
                int rd = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int rt = parseRegister(parts[3]);

                reg["$" + to_string(rd)] = (reg["$" + to_string(rs)] < reg["$" + to_string(rt)]) ? 1 : 0;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "slti") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm = parseImmediate(parts[3], dataSymbolTable);
                
                reg["$" + to_string(rt)] = (reg["$" + to_string(rs)] < imm) ? 1 : 0;
                counts["ALU"]++;
                PC += 1;
            }
            else if (opcodeStr == "lw") {
                int rt = parseRegister(parts[1]);
                size_t start = parts[2].find('(');
                size_t end = parts[2].find(')');
                int base = 0, offset = 0;
                if (start != string::npos && end != string::npos) {
                    string offsetStr = parts[2].substr(0, start);
                    string baseRegStr = parts[2].substr(start + 1, end - start - 1);
                    base = reg["$" + to_string(parseRegister(baseRegStr))];
                    offset = parseImmediate(offsetStr, dataSymbolTable);
                }
                int mem_address = base + offset;
                if (memory.count(mem_address)) {
                    reg["$" + to_string(rt)] = memory[mem_address];
                } else {
                    reg["$" + to_string(rt)] = 0;
                }
                counts["Memory"]++;
                PC += 1;
            }
            else if (opcodeStr == "sw") {
                int rt = parseRegister(parts[1]);
                size_t start = parts[2].find('(');
                size_t end = parts[2].find(')');
                int base = 0, offset = 0;
                if (start != string::npos && end != string::npos) {
                    string offsetStr = parts[2].substr(0, start);
                    string baseRegStr = parts[2].substr(start + 1, end - start - 1);
                    base = reg["$" + to_string(parseRegister(baseRegStr))];
                    offset = parseImmediate(offsetStr, dataSymbolTable);
                }
                int mem_address = base + offset;
                memory[mem_address] = reg["$" + to_string(rt)];
                counts["Memory"]++;
                PC += 1;
            }
            else if (opcodeStr == "beq") {
                int rs = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);
                string label = parts[3];
                if (reg["$" + to_string(rs)] == reg["$" + to_string(rt)]) {
                    if (labelAddresses.count(label)) {
                        PC = labelAddresses.at(label);
                    } else {
                        cerr << "Undefined label: " << label << "\n";
                        exit(1);
                    }
                } else {
                    PC += 1;
                }
                counts["Branch"]++;
            }
            else if (opcodeStr == "bne") {
                int rs = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);
                string label = parts[3];
                if (reg["$" + to_string(rs)] != reg["$" + to_string(rt)]) {
                    if (labelAddresses.count(label)) {
                        PC = labelAddresses.at(label);
                    } else {
                        cerr << "Undefined label: " << label << "\n";
                        exit(1);
                    }
                } else {
                    PC += 1;
                }
                counts["Branch"]++;
            }
            else if (opcodeStr == "lui") {
                int rt = parseRegister(parts[1]);
                int imm = parseImmediate(parts[2], dataSymbolTable);

                reg["$" + to_string(rt)] = (imm << 16);
                counts["Other"]++;
                PC += 1;
            }
            else if (opcodeStr == "j") {
                string label = parts[1];
                if (labelAddresses.count(label)) {
                    PC = labelAddresses.at(label);
                } else {
                    cerr << "Undefined label: " << label << "\n";
                    exit(1);
                }
                counts["Jump"]++;
            }
            else if (opcodeStr == "jal") {
                string label = parts[1];
                reg["$ra"] = PC + 1;
                if (labelAddresses.count(label)) {
                    PC = labelAddresses.at(label);
                } else {
                    cerr << "Undefined label: " << label << "\n";
                    exit(1);
                }
                counts["Jump"]++;
            }
            else if (opcodeStr == "jr") {
                int rs = parseRegister(parts[1]);
                PC = reg["$" + to_string(rs)];
                counts["Jump"]++;
            }
            else if (opcodeStr == "jalr") {
                int rd = 31;
                int rs = parseRegister(parts[1]);
                if (parts.size() > 2) {
                    rd = parseRegister(parts[2]);
                }
                reg["$" + to_string(rd)] = PC + 1;
                PC = reg["$" + to_string(rs)];
                counts["Jump"]++;
            }
            else if (opcodeStr == "li") {
                int rt = parseRegister(parts[1]);
                int imm = parseImmediate(parts[2], dataSymbolTable);
                reg["$" + to_string(rt)] = imm;
                counts["Other"]++;
                PC += 1;
            }
           else if (opcodeStr == "syscall") {
            cout<< reg["$v0"] << endl;
    // Ensure $v0 is in the register map
    if (!reg.count("$v0")) {
        cerr << "Register $v0 not initialized before syscall.\n";
        exit(1);
    }
    switch (reg["$v0"]) {
        case 1: // Print an integer
            if (reg.count("$a0")) {
                cout << reg["$a0"];
            } else {
                cerr << "Register $a0 not initialized before syscall for printing integer.\n";
                exit(1);
            }
            break;
        case 4: { // Print a string
            if (reg.count("$a0")) {
                int address = reg["$a0"];
                while (memory.count(address) && memory[address] != 0) {
                    char c = static_cast<char>(memory[address++]);
                    cout << c;
                }
            } else {
                cerr << "Register $a0 not initialized before syscall for printing string.\n";
                exit(1);
            }
            break;
        }
        case 5: { // Read an integer
            int input;
            cin >> input;
            reg["$v0"] = input;
            break;
        }
        case 8: { // Read a string
            if (reg.count("$a0") && reg.count("$a1")) {
                int address = reg["$a0"];
                int maxLength = reg["$a1"];
                string input;
                cin.ignore(); // Clear any leftover newline
                getline(cin, input);
                for (int i = 0; i < min(maxLength - 1, static_cast<int>(input.size())); ++i) {
                    memory[address++] = input[i];
                }
                memory[address] = 0; // Null-terminate the string
            } else {
                cerr << "$a0 or $a1 not initialized before syscall for reading string.\n";
                exit(1);
            }
            break;
        }
        case 10: // Exit
            cout << "\nProgram terminated by syscall.\n";
            return;
        case 11: // Print a character
            if (reg.count("$a0")) {
                cout << static_cast<char>(reg["$a0"]);
            } else {
                cerr << "Register $a0 not initialized before syscall for printing character.\n";
                exit(1);
            }
            break;
        default:
            cerr << "Unknown syscall code: " << reg["$v0"] << "\n";
            exit(1);
    }
    PC += 1;
                }
            else {
                cerr << "Unsupported instruction during simulation: " << opcodeStr << "\n";
                exit(1);
            }

            reg["$0"] = 0;

        } catch (const exception& e) {
            cerr << "Error during instruction execution: " << e.what() << "\n";
            exit(1);
        }

        if (single_step) {
            cout << "PC after execution: " << PC << "\n";
            printRegisters(reg);
            unordered_map<int, int> non_zero_memory;
            for (const auto& [addr, val] : memory) {
                if (val != 0) {
                    non_zero_memory[addr] = val;
                }
            }
            if (!non_zero_memory.empty()) {
                cout << "Updated Memory:\n";
                for (const auto& [addr, val] : non_zero_memory) {
                    cout << "M[" << addr << "]: " << val << "\n";
                }
            } else {
                cout << "No changes in memory.\n";
            }
            cout << string(80, '=') << "\n";

            cout << "\nPress 'n' to execute the next instruction, 'a' to switch to automatic mode: ";
            char cont;
            cin >> cont;
            if (cont == 'a') {
                single_step = false;
            }
        }

        counts["Total"]++;
    }

    cout << "\nRegister Values After Simulation:\n";
    printRegisters(reg);

    cout << "\nMemory Values After Simulation:\n";
    printMemory(memory);

    cout << "\nInstruction Counts:\n";
    for (const auto& [key, value] : counts) {
        cout << key << ": " << value << "\n";
    }
}

int main() {
    ifstream infile("C:/Users/91798/Mips_simulator/program.asm");
    if (!infile) {
        cerr << "Error opening file 'program.asm'.\n";
        return 1;
    }

    vector<string> asmList;
    string line;
    int lineNumber = 0;
    unordered_map<string, int> labelAddresses;
    unordered_map<int, string> addressLabelMap;

    Section currentSection = UNKNOWN;
    unordered_map<string, DataItem> dataSymbolTable;
    int dataAddress = 0x10010000; 
    unordered_map<int, int> memory;

    while (getline(infile, line)) {
        string processedLine = preprocessLine(line, currentSection);
        if (processedLine.empty()) {
            continue;
        }

        string line = processedLine;
        line.erase(remove(line.begin(), line.end(), ','), line.end());
        istringstream iss(line);
        vector<string> parts{istream_iterator<string>{iss}, istream_iterator<string>{}};
        if (parts.empty()) {
            continue;
        }

        if (currentSection == DATA) {
            if (parts[0].back() == ':') {
                string label = parts[0].substr(0, parts[0].length() - 1);
                parts.erase(parts.begin());
                if (parts.empty()) {
                    if (!getline(infile, line)) {
                        cerr << "Unexpected end of file.\n";
                        return 1;
                    }
                    processedLine = preprocessLine(line, currentSection);
                    line = processedLine;
                    line.erase(remove(line.begin(), line.end(), ','), line.end());
                    iss.clear();
                    iss.str(line);
                    parts = {istream_iterator<string>{iss}, istream_iterator<string>{}};
                    if (parts.empty()) {
                        cerr << "Expected data directive after label.\n";
                        return 1;
                    }
                }
                string directive = parts[0];
                if (directive == ".word") {
                    parts.erase(parts.begin());
                    vector<int> values;
                    for (const auto& valStr : parts) {
                        int val = parseImmediate(valStr, dataSymbolTable);
                        values.push_back(val);
                    }
                    DataItem item;
                    item.address = dataAddress;
                    item.values = values;
                    dataSymbolTable[label] = item;
                    dataAddress += static_cast<int>(values.size() * 4);
                }
                else if (directive == ".space") {
                    parts.erase(parts.begin());
                    int size = parseImmediate(parts[0], dataSymbolTable);
                    DataItem item;
                    item.address = dataAddress;
                    int numWords = (size + 3) / 4;
                    item.values.resize(numWords, 0);
                    dataSymbolTable[label] = item;
                    dataAddress += numWords * 4;
                }
                else if (directive == ".ascii" || directive == ".asciiz") {
                    parts.erase(parts.begin());
                    string str = line.substr(line.find('"') + 1);
                    str = str.substr(0, str.rfind('"'));
                    DataItem item;
                    item.address = dataAddress;
                    for (char c : str) {
                        item.values.push_back(static_cast<int>(c));
                        dataAddress += 1;
                    }
                    if (directive == ".asciiz") {
                        item.values.push_back(0);
                        dataAddress += 1;
                    }
                    dataSymbolTable[label] = item;
                    if (dataAddress % 4 != 0) {
                        dataAddress += (4 - (dataAddress % 4));
                    }
                }
                else {
                    cerr << "Unsupported data directive: " << directive << "\n";
                    return 1;
                }
            } else {
                cerr << "Expected label in .data section.\n";
                return 1;
            }
        } else if (currentSection == TEXT) {
            if (!parts.empty() && parts[0].back() == ':') {
                string label = parts[0].substr(0, parts[0].length() - 1);
                labelAddresses[label] = lineNumber;
                addressLabelMap[lineNumber] = label;
                parts.erase(parts.begin());
                if (parts.empty()) {
                    asmList.push_back("");
                    lineNumber++;
                    continue;
                }
                ostringstream oss;
                copy(parts.begin(), parts.end(), ostream_iterator<string>(oss, " "));
                processedLine = oss.str();
            }
            asmList.push_back(processedLine);
            lineNumber++;
        }
    }
    infile.close();

    for (const auto& [label, item] : dataSymbolTable) {
        int addr = item.address;
        for (int value : item.values) {
            memory[addr] = value;
            addr += 4;
        }
    }

    unordered_map<string, int> opcodes = {
        {"lw", 35}, {"sw", 43}, {"beq", 4}, {"bne", 5}, {"addi", 8}, {"addiu", 9}, {"andi", 12}, {"ori", 13},
        {"xori", 14}, {"slti", 10}, {"lui", 15}, {"j", 2}, {"jal", 3}
    };

    unordered_map<string, int> functCodes = {
        {"add", 32}, {"addu", 33}, {"sub", 34}, {"subu", 35}, {"and", 36}, {"or", 37}, {"xor", 38}, {"nor", 39},
        {"sll", 0}, {"mult", 24}, {"div", 26}, {"jr", 8}, {"jalr", 9}, {"mfhi", 16}, {"mflo", 18},
        {"mthi", 17}, {"mtlo", 19}, {"slt", 42}
    };

    unordered_map<string, string> instructionTypes = {
        {"add", "R"}, {"addu", "R"}, {"sub", "R"}, {"subu", "R"}, {"and", "R"}, {"or", "R"}, {"xor", "R"}, {"nor", "R"},
        {"sll", "R"}, {"mult", "R"}, {"div", "R"}, {"mfhi", "R"}, {"mflo", "R"}, {"mthi", "R"}, {"mtlo", "R"},
        {"addi", "I"}, {"addiu", "I"}, {"andi", "I"}, {"ori", "I"}, {"xori", "I"}, {"slti", "I"}, {"li", "I"},
        {"lw", "I"}, {"sw", "I"}, {"beq", "I"}, {"bne", "I"}, {"lui", "I"},
        {"j", "J"}, {"jal", "J"}, {"jr", "R"}, {"jalr", "R"}, {"slt", "R"}, {"syscall", "R"}
    };

    vector<string> newAsmList;
    vector<string> binList;
    for (size_t i = 0; i < asmList.size(); ++i) {
        vector<string> binCodes = assembleInstruction(asmList[i], static_cast<int>(i), labelAddresses,
                                                      opcodes, functCodes, instructionTypes, dataSymbolTable);
        for (size_t j = 0; j < binCodes.size(); ++j) {
            binList.push_back(binCodes[j]);
            newAsmList.push_back(asmList[i]);
        }
    }

    cout << "Assembly Instructions and Corresponding Binary Codes:\n";
    for (size_t i = 0; i < binList.size(); ++i) {
        if (!binList[i].empty()) {
            cout << newAsmList[i] << "\nBinary: " << binList[i] << "\n\n";
        }
    }

    simulate(newAsmList, binList, labelAddresses, dataSymbolTable, memory);

    return 0;
} the register values are not updated they all remain zero   .data
    num1: .word 10       # First operand
    num2: .word 5        # Second operand
    result: .word 0      # Result storage

.text
main:
    # Load the values of num1 and num2 into registers
    lw $t0, num1         # $t0 = num1 (10)
    lw $t1, num2         # $t1 = num2 (5)

    # Perform addition: result = num1 + num2
    add $t2, $t0, $t1    # $t2 = $t0 + $t1
    sw $t2, result       # Store the result in 'result'
    
    # Perform subtraction: result = num1 - num2
    sub $t2, $t0, $t1    # $t2 = $t0 - $t1
    sw $t2, result       # Store the result in 'result'