add load immediate functionality and also write a loop test code for the same to see if the code stops when I do Exit (terminate program):

$v0 = 10
syscall also implement the other common instruction handelling like Print integer:

$v0 = 1
$a0 = integer to print
syscall
Print string:

$v0 = 4
$a0 = address of the string
syscall
Read integer:

$v0 = 5
Result stored in $v0
syscall
Read string:

$v0 = 8
$a0 = address of input buffer
$a1 = maximum buffer length
syscall
Exit (terminate program):

$v0 = 10
syscall
Print character:

$v0 = 11
$a0 = character to print
syscall // mips_simulator.cpp

#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; // Use the standard namespace to avoid 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;
    // Remove comments
    size_t commentPos = processed.find('#');
    if (commentPos != string::npos) {
        processed = processed.substr(0, commentPos);
    }
    // Trim whitespace
    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 "";  // Return empty line since .data is a directive
    } else if (processed == ".text") {
        currentSection = TEXT;
        return "";  // Return empty line since .text is a directive
    } else if (processed[0] == '.') {
        // Handle other directives if necessary
        return processed;
    }

    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); // Start search from position 1 to avoid negative numbers
    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; // offset is negative
            } 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;

    auto instrTypeIt = instructionTypes.find(opcodeStr);
    if (instrTypeIt == instructionTypes.end()) {
        cerr << "Unsupported instruction: " << opcodeStr << "\n";
        exit(1);
    }
    string instrType = instrTypeIt->second;

    if (instrType == "R") {
        int rs = 0, rt = 0, rd = 0, shamt = 0, funct = 0;
        int opcode = 0;  // Opcode for R-type instructions is 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" || opcodeStr == "sra") {
            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") {
            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; // Default return address register
            } 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 (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 == "lb" || opcodeStr == "sb" ||
            opcodeStr == "lui" || opcodeStr == "lh" || opcodeStr == "sh" || opcodeStr == "lbu" || opcodeStr == "lhu") {

            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 == "blez" || opcodeStr == "bgtz" ||
                   opcodeStr == "bltz" || opcodeStr == "bgez") {

            if (opcodeStr == "beq" || opcodeStr == "bne") {
                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 (parts.size() != 3) {
                    cerr << "Invalid format for instruction: " << instructionLine << "\n";
                    exit(1);
                }
                rs = parseRegister(parts[1]);
                rt = 0;
                int labelAddress;
                if (labelAddresses.count(parts[2])) {
                    labelAddress = labelAddresses[parts[2]];
                    immediate = labelAddress - (lineNumber + 1);
                } else {
                    cerr << "Undefined label: " << parts[2] << "\n";
                    exit(1);
                }
            }

        } 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);

    } else {
        cerr << "Unsupported instruction type: " << instrType << "\n";
        exit(1);
    }

    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 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 == "addi") {
                int rt = parseRegister(parts[1]);
                int rs = parseRegister(parts[2]);
                int imm;
                imm = parseImmediate(parts[3], dataSymbolTable);
                reg["$" + to_string(rt)] = reg["$" + to_string(rs)] + 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 == "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 == "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 == "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 == "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);
                } else {
                    offset = parseImmediate(parts[2], dataSymbolTable);
                    base = 0;
                }
                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);
                } else {
                    offset = parseImmediate(parts[2], dataSymbolTable);
                    base = 0;
                }
                int mem_address = base + offset;
                memory[mem_address] = reg["$" + to_string(rt)];
                counts["Memory"]++;
                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 == "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 == "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 == "srl") {
                int rd = parseRegister(parts[1]);
                int rt = parseRegister(parts[2]);
                int shamt = parseImmediate(parts[3], dataSymbolTable);

                reg["$" + to_string(rd)] = static_cast<unsigned int>(reg["$" + to_string(rt)]) >> shamt;
                counts["ALU"]++;
                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 'C:/Users/91798/Mips_simulator/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}, {"andi", 12}, {"ori", 13},
        {"slti", 10}, {"lui", 15}, {"j", 2}, {"jal", 3}
    };

    unordered_map<string, int> functCodes = {
        {"add", 32}, {"sub", 34}, {"and", 36}, {"or", 37}, {"slt", 42},
        {"sll", 0}, {"srl", 2}, {"jr", 8}, {"jalr", 9}
    };

    unordered_map<string, string> instructionTypes = {
        {"add", "R"}, {"sub", "R"}, {"and", "R"}, {"or", "R"}, {"slt", "R"},
        {"sll", "R"}, {"srl", "R"}, {"jr", "R"}, {"jalr", "R"},
        {"addi", "I"}, {"andi", "I"}, {"ori", "I"}, {"slti", "I"},
        {"lw", "I"}, {"sw", "I"}, {"beq", "I"}, {"bne", "I"}, {"lui", "I"},
        {"j", "J"}, {"jal", "J"}
    };

    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;
}