import re

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

def get_register_number(reg_name):
    reg_name = reg_name.strip().lstrip('$')
    if reg_name.isdigit():
        return int(reg_name)
    elif reg_name in reg_map:
        return reg_map[reg_name]
    else:
        raise ValueError(f"Unknown register name {reg_name}")

def get_register_name(num):
    for name, n in reg_map.items():
        if n == num:
            return name
    raise ValueError(f"Unknown register number {num}")

def read_asm_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    instructions = []
    for line in lines:
        line = re.sub(r'#.*', '', line).strip()
        if line:
            instructions.append(line)
    return instructions

def parse_labels_and_instructions(instructions):
    labels = {}
    parsed_instructions = []
    pc = 0
    data_mode = False
    memory = {}
    current_address = 0x10010000

    for line in instructions:
        if line.startswith(".data"):
            data_mode = True
            continue
        elif line.startswith(".text"):
            data_mode = False
            pc = 0
            continue

        if data_mode:
            if ':' in line:
                label, line_part = line.split(':', 1)
                labels[label.strip()] = current_address
                line = line_part.strip()
            if line.startswith('.word'):
                line = line.replace('.word', '').strip()
                values = line.split(',')
                for value in values:
                    memory[current_address] = int(value)
                    current_address += 4
            elif line.startswith('.asciiz'):
                line = line.replace('.asciiz', '').strip().strip('"')
                for char in line:
                    memory[current_address] = ord(char)
                    current_address += 1
                memory[current_address] = 0
                current_address += 1
        else:
            if ':' in line:
                label, line_part = line.split(':', 1)
                labels[label.strip()] = pc
                line = line_part.strip()
            if line:
                parsed_instructions.append(line)
                pc += 4
    return parsed_instructions, labels, memory

def convert_to_binary(instruction, labels, current_pc):
    opcodes = {
        "addi": "001000",
        "andi": "001100",
        "ori": "001101",
        "beq": "000100",
        "bne": "000101",
        "j": "000010",
        "jal": "000011",
        "lw": "100011",
        "sw": "101011",
        "lui": "001111",
        "and": "000000",
        "or": "000000",
        "slt": "000000",
        "add": "000000",
        "sub": "000000",
        "mul": "011100",
        "sll": "000000",
        "srl": "000000",
        "jr": "000000",
        "xor": "000000",
        "nor": "000000",
        "syscall": "000000",
    }
    functs = {
        "and": "100100",
        "or": "100101",
        "slt": "101010",
        "add": "100000",
        "sub": "100010",
        "mul": "000010",
        "sll": "000000",
        "srl": "000010",
        "jr": "001000",
        "xor": "100110",
        "nor": "100111",
        "syscall": "001100"
    }

    parts = re.split(r'[,\s()]+', instruction)
    parts = [p for p in parts if p]
    op = parts[0]

    try:
        if op == "li":
            rd_num = get_register_number(parts[1])
            imm_value = int(parts[2])
            if -32768 <= imm_value <= 65535:
                rs = format(0, '05b')
                rd = format(rd_num, '05b')
                imm = format(imm_value & 0xFFFF, '016b')
                return f"{opcodes['addi']}{rs}{rd}{imm}"
            else:
                upper = (imm_value >> 16) & 0xFFFF
                lower = imm_value & 0xFFFF
                rd = format(rd_num, '05b')
                rs = format(0, '05b')
                lui_inst = f"{opcodes['lui']}{rs}{rd}{format(upper, '016b')}"
                ori_inst = f"{opcodes['ori']}{rd}{rd}{format(lower, '016b')}"
                return [lui_inst, ori_inst]
        elif op == "la":
            rd_num = get_register_number(parts[1])
            label_address = labels.get(parts[2], 0)
            upper = (label_address >> 16) & 0xFFFF
            lower = label_address & 0xFFFF
            rd = format(rd_num, '05b')
            rs = format(0, '05b')
            lui_inst = f"{opcodes['lui']}{rs}{rd}{format(upper, '016b')}"
            ori_inst = f"{opcodes['ori']}{rd}{rd}{format(lower, '016b')}"
            return [lui_inst, ori_inst]
        elif op == "syscall":
            return f"{opcodes[op]}00000000000000000000{functs[op]}"
        elif op in ["addi", "andi", "ori"]:
            rt_num = get_register_number(parts[1])
            rs_num = get_register_number(parts[2])
            imm = int(parts[3])
            rs = format(rs_num, '05b')
            rt = format(rt_num, '05b')
            imm = format(imm & 0xFFFF, '016b')
            return f"{opcodes[op]}{rs}{rt}{imm}"
        elif op in ["lw", "sw"]:
            rt_num = get_register_number(parts[1])
            if len(parts) == 4:
                offset = int(parts[2])
                base_num = get_register_number(parts[3])
                rs = format(base_num, '05b')
                rt = format(rt_num, '05b')
                imm = format(offset & 0xFFFF, '016b')
                return f"{opcodes[op]}{rs}{rt}{imm}"
            elif len(parts) == 3:
                address = labels.get(parts[2])
                if address is None:
                    raise ValueError(f"Label {parts[2]} not found")
                rs = format(0, '05b')
                rt = format(rt_num, '05b')
                imm = format(address & 0xFFFF, '016b')
                return f"{opcodes[op]}{rs}{rt}{imm}"
            else:
                raise ValueError("Invalid lw/sw instruction format")
        elif op in ["beq", "bne"]:
            rs_num = get_register_number(parts[1])
            rt_num = get_register_number(parts[2])
            label = parts[3]
            if label in labels:
                offset = ((labels[label] - (current_pc + 4)) >> 2)
                imm = format(offset & 0xFFFF, '016b')
            else:
                imm = "0000000000000000"
            rs = format(rs_num, '05b')
            rt = format(rt_num, '05b')
            return f"{opcodes[op]}{rs}{rt}{imm}"
        elif op == "j" or op == "jal":
            label = parts[1]
            if label in labels:
                address = labels[label] >> 2
                return f"{opcodes[op]}{format(address, '026b')}"
            else:
                return None
        elif op in ["sll", "srl"]:
            rd_num = get_register_number(parts[1])
            rt_num = get_register_number(parts[2])
            shamt = int(parts[3])
            rs = format(0, '05b')
            rt = format(rt_num, '05b')
            rd = format(rd_num, '05b')
            shamt = format(shamt & 0x1F, '05b')
            funct = functs[op]
            return f"{opcodes[op]}{rs}{rt}{rd}{shamt}{funct}"
        elif op == "jr":
            rs_num = get_register_number(parts[1])
            rs = format(rs_num, '05b')
            rt = "00000"
            rd = "00000"
            shamt = "00000"
            funct = functs[op]
            return f"{opcodes[op]}{rs}{rt}{rd}{shamt}{funct}"
        elif op == "mul":
            rd_num = get_register_number(parts[1])
            rs_num = get_register_number(parts[2])
            rt_num = get_register_number(parts[3])
            rs = format(rs_num, '05b')
            rt = format(rt_num, '05b')
            rd = format(rd_num, '05b')
            shamt = "00000"
            funct = functs[op]
            return f"{opcodes[op]}{rs}{rt}{rd}{shamt}{funct}"
        else:
            # R-type instructions (add, sub, and, or, slt, etc.)
            rd_num = get_register_number(parts[1])
            rs_num = get_register_number(parts[2])
            rt_num = get_register_number(parts[3])
            rs = format(rs_num, '05b')
            rt = format(rt_num, '05b')
            rd = format(rd_num, '05b')
            shamt = "00000"
            funct = functs[op]
            return f"{opcodes[op]}{rs}{rt}{rd}{shamt}{funct}"
    except Exception as e:
        print(f"Error converting instruction: {instruction} -> {e}")
        return None

def generate_control_signals(op_code):
    signals = {
        'RegDst': 0,
        'RegWrite': 0,
        'ALUSrc': 0,
        'ALUOp': '00',
        'MemRead': 0,
        'MemWrite': 0,
        'MemtoReg': 0,
        'Branch': 0,
        'Jump': 0,
        'Syscall': 0
    }
    if op_code in ['add', 'sub', 'and', 'or', 'slt', 'mul', 'xor', 'nor', 'sll', 'srl']:
        signals['RegDst'] = 1
        signals['RegWrite'] = 1
        signals['ALUOp'] = '10'
    elif op_code in ['addi', 'andi', 'ori', 'li', 'lui']:
        signals['ALUSrc'] = 1
        signals['RegWrite'] = 1
        signals['ALUOp'] = '00'
    elif op_code == 'lw':
        signals['ALUSrc'] = 1
        signals['MemtoReg'] = 1
        signals['RegWrite'] = 1
        signals['MemRead'] = 1
        signals['ALUOp'] = '00'
    elif op_code == 'sw':
        signals['ALUSrc'] = 1
        signals['MemWrite'] = 1
        signals['ALUOp'] = '00'
    elif op_code == 'beq':
        signals['Branch'] = 1
        signals['ALUOp'] = '01'
    elif op_code == 'bne':
        signals['Branch'] = 1
        signals['ALUOp'] = '01'
    elif op_code == 'j':
        signals['Jump'] = 1
    elif op_code == 'jal':
        signals['Jump'] = 1
        signals['RegWrite'] = 1
    elif op_code == 'jr':
        signals['Jump'] = 1
    elif op_code == 'syscall':
        signals['Syscall'] = 1
    else:
        raise ValueError(f"Unsupported operation {op_code}")
    return signals

def display_registers(reg):
    print("Registers:")
    for i in range(0, 32, 4):
        reg_names = []
        for num in range(i, i+4):
            name = get_register_name(num)
            reg_names.append(name)
        print(f"${reg_names[0]:<3}: {reg[reg_names[0]]:10} | "
              f"${reg_names[1]:<3}: {reg[reg_names[1]]:10} | "
              f"${reg_names[2]:<3}: {reg[reg_names[2]]:10} | "
              f"${reg_names[3]:<3}: {reg[reg_names[3]]:10}")
    print()

def display_memory(memory):
    print("Memory:")
    addresses = sorted(memory.keys())
    for addr in addresses:
        value = memory[addr]
        if 32 <= value <= 126:
            display_value = f"{value} ('{chr(value)}')"
        else:
            display_value = str(value)
        print(f"Address {addr:08x}: {display_value}")
    print()

def syscall(reg, memory):
    syscall_num = reg['v0']
    if syscall_num == 1:
        print(f"Output (int): {reg['a0']}")
    elif syscall_num == 4:
        print("Output (string):", end="")
        string_address = reg['a0']
        while memory.get(string_address, 0) != 0:
            print(chr(memory[string_address]), end="")
            string_address +=1
        print()
    elif syscall_num ==10:
        print("Exiting program.")
        return False
    else:
        print(f"Unknown syscall: {syscall_num}")
    return True

def run_simulation(parsed_instructions, labels, memory):
    reg = {name: 0 for name in reg_map}
    reg['zero'] = 0
    reg['sp'] = 0x7FFFFFFC
    pc = 0
    sim_mode = input("Enter 'n' for single instruction mode, 'a' for automatic mode: ")
    single_step = (sim_mode == 'n')
    instructions_list = []
    pc_counter = 0
    for inst in parsed_instructions:
        bin_inst = convert_to_binary(inst, labels, pc_counter)
        if bin_inst:
            if isinstance(bin_inst, list):
                for bi in bin_inst:
                    instructions_list.append((inst, bi, pc_counter))
                    pc_counter += 4
            else:
                instructions_list.append((inst, bin_inst, pc_counter))
                pc_counter += 4
        else:
            instructions_list.append((inst, None, pc_counter))
            pc_counter += 4

    inD = {pc: (inst, mc) for inst, mc, pc in instructions_list}

    while pc in inD:
        current_instruction, mc = inD[pc]
        parts = re.split(r'[,\s()]+', current_instruction)
        parts = [p for p in parts if p]
        op_code = parts[0]

        try:
            control_signals = generate_control_signals(op_code)
        except ValueError as e:
            print(f"Error: {e}")
            break

        if single_step:
            print("\n" + "=" * 80)
            print("Executing Instruction:")
            print("Assembly Code:", current_instruction)
            if mc:
                print("Machine Code:", mc)
            else:
                print("Machine Code: N/A")
            print("PC before execution:", pc)
            print("Control Signals:", control_signals)

        try:
            if op_code == 'syscall':
                if not syscall(reg, memory):
                    break
            elif control_signals['Jump']:
                if op_code == 'j' or op_code == 'jal':
                    label = parts[1]
                    if label in labels:
                        if op_code == 'jal':
                            reg['ra'] = pc + 4
                        pc = labels[label]
                        continue
                    else:
                        raise ValueError(f"Label {label} not found")
                elif op_code == 'jr':
                    rs_name = get_register_name(get_register_number(parts[1]))
                    pc = reg[rs_name]
                    continue
            elif control_signals['Branch']:
                rs_name = get_register_name(get_register_number(parts[1]))
                rt_name = get_register_name(get_register_number(parts[2]))
                label = parts[3]
                if op_code == 'beq' and reg[rs_name] == reg[rt_name]:
                    pc = labels[label]
                    continue
                elif op_code == 'bne' and reg[rs_name] != reg[rt_name]:
                    pc = labels[label]
                    continue
            else:
                ALU_result = 0
                if control_signals['ALUSrc']:
                    if op_code in ['addi', 'andi', 'ori']:
                        rt_name = get_register_name(get_register_number(parts[1]))
                        rs_name = get_register_name(get_register_number(parts[2]))
                        imm = int(parts[3])
                        if op_code == 'addi':
                            ALU_result = reg[rs_name] + imm
                        elif op_code == 'andi':
                            ALU_result = reg[rs_name] & imm
                        elif op_code == 'ori':
                            ALU_result = reg[rs_name] | imm
                        if control_signals['RegWrite']:
                            reg[rt_name] = ALU_result
                    elif op_code == 'lui':
                        rt_name = get_register_name(get_register_number(parts[1]))
                        imm = int(parts[2])
                        if control_signals['RegWrite']:
                            reg[rt_name] = imm << 16
                    elif op_code == 'li':
                        rd_name = get_register_name(get_register_number(parts[1]))
                        imm = int(parts[2])
                        if control_signals['RegWrite']:
                            reg[rd_name] = imm
                    elif op_code == 'lw':
                        rt_name = get_register_name(get_register_number(parts[1]))
                        if len(parts) == 4:
                            offset = int(parts[2])
                            base_name = get_register_name(get_register_number(parts[3]))
                            address = reg[base_name] + offset
                        elif len(parts) == 3:
                            label = parts[2]
                            address = labels.get(label)
                            if address is None:
                                raise ValueError(f"Label {label} not found")
                        else:
                            raise ValueError("Invalid lw instruction format")
                        if control_signals['MemRead']:
                            data = memory.get(address, 0)
                            if control_signals['RegWrite']:
                                reg[rt_name] = data
                    elif op_code == 'sw':
                        rt_name = get_register_name(get_register_number(parts[1]))
                        if len(parts) == 4:
                            offset = int(parts[2])
                            base_name = get_register_name(get_register_number(parts[3]))
                            address = reg[base_name] + offset
                        elif len(parts) == 3:
                            label = parts[2]
                            address = labels.get(label)
                            if address is None:
                                raise ValueError(f"Label {label} not found")
                        else:
                            raise ValueError("Invalid sw instruction format")
                        if control_signals['MemWrite']:
                            memory[address] = reg[rt_name]
                else:
                    # ALU operations with register operands
                    rd_name = get_register_name(get_register_number(parts[1]))
                    rs_name = get_register_name(get_register_number(parts[2]))
                    rt_name = get_register_name(get_register_number(parts[3]))
                    if op_code == 'add':
                        ALU_result = reg[rs_name] + reg[rt_name]
                    elif op_code == 'sub':
                        ALU_result = reg[rs_name] - reg[rt_name]
                    elif op_code == 'and':
                        ALU_result = reg[rs_name] & reg[rt_name]
                    elif op_code == 'or':
                        ALU_result = reg[rs_name] | reg[rt_name]
                    elif op_code == 'slt':
                        ALU_result = 1 if reg[rs_name] < reg[rt_name] else 0
                    elif op_code == 'mul':
                        ALU_result = reg[rs_name] * reg[rt_name]
                    elif op_code == 'xor':
                        ALU_result = reg[rs_name] ^ reg[rt_name]
                    elif op_code == 'nor':
                        ALU_result = ~(reg[rs_name] | reg[rt_name])
                    elif op_code == 'sll':
                        rt_name = get_register_name(get_register_number(parts[2]))
                        shamt = int(parts[3])
                        ALU_result = reg[rt_name] << shamt
                    elif op_code == 'srl':
                        rt_name = get_register_name(get_register_number(parts[2]))
                        shamt = int(parts[3])
                        ALU_result = reg[rt_name] >> shamt
                    else:
                        raise ValueError(f"Unsupported ALU operation {op_code}")
                    if control_signals['RegWrite']:
                        reg[rd_name] = ALU_result
        except Exception as e:
            print(f"Error executing instruction: {current_instruction} -> {e}")
            break

        if single_step:
            display_registers(reg)
            print("PC after execution:", pc + 4)
            input("Press Enter to continue...")

        pc += 4

    if not single_step:
        display_registers(reg)

def main():
    file_path = "program.asm"
    instructions = read_asm_file(file_path)

    parsed_instructions, labels, memory = parse_labels_and_instructions(instructions)

    print("Assembly to Machine Code Conversion:")
    pc_counter = 0
    for inst in parsed_instructions:
        bin_inst = convert_to_binary(inst, labels, pc_counter)
        if bin_inst:
            if isinstance(bin_inst, list):
                for bi in bin_inst:
                    print(f"{inst} -> {bi}")
                    pc_counter += 4
            else:
                print(f"{inst} -> {bin_inst}")
                pc_counter += 4
        else:
            print(f"{inst} -> Invalid instruction")
            pc_counter += 4

    run_simulation(parsed_instructions, labels, memory)

if __name__ == "__main__":
    main() this is the code I have written , there is one problem in run simulation part , the problem is the run simulation function makes use of strings from .asm files rather than using binary code that is generated for each instruction , below I have shared a reference code to execute the siulation using binary instructions , use it to modify all the instruction handling parts of my code and make sure you donot remove anything else  class MIPSCompiler:
    def __init__(self):
        # Define register mappings with integer values
        self.registers = {
            '$zero': 0, '$0': 0,
            '$at': 1, '$v0': 2, '$v1': 3,
            '$a0': 4, '$a1': 5, '$a2': 6, '$a3': 7,
            '$t0': 8, '$t1': 9, '$t2': 10, '$t3': 11,
            '$t4': 12, '$t5': 13, '$t6': 14, '$t7': 15,
            '$s0': 16, '$s1': 17, '$s2': 18, '$s3': 19,
            '$s4': 20, '$s5': 21, '$s6': 22, '$s7': 23,
            '$t8': 24, '$t9': 25,
            '$k0': 26, '$k1': 27,
            '$gp': 28, '$sp': 29, '$fp': 30, '$ra': 31
        }

        # Define opcodes and function codes
        self.r_type = {
            'add': {'opcode': '000000', 'funct': '100000'},
            'sub': {'opcode': '000000', 'funct': '100010'},
            'and': {'opcode': '000000', 'funct': '100100'},
            'or':  {'opcode': '000000', 'funct': '100101'},
            'slt': {'opcode': '000000', 'funct': '101010'}
        }

        self.i_type = {
            'lw':   '100011',
            'sw':   '101011',
            'beq':  '000100',
            'addi': '001000'
        }

        self.j_type = {
            'j': '000010'
        }

        self.data_section = {}
        self.labels = {}
        self.current_address = 0x00400000
        self.data_start_address = 0x10010000

    def parse_data_section(self, line):
        parts = line.strip().split()
        if len(parts) >= 2:
            label = parts[0].rstrip(':')
            data_type = parts[1]
            value = ' '.join(parts[2:])

            if data_type == '.word':
                values = [int(v) for v in value.split()]
                self.data_section[label] = {
                    'type': data_type,
                    'value': values[0],
                    'address': self.data_start_address
                }
                self.data_start_address += 4

    def compile_instruction(self, instruction, line_address):
        parts = instruction.strip().replace(',', '').split()
        if not parts:
            return None

        # Handle labels
        if parts[0].endswith(':'):
            label = parts[0][:-1]
            self.labels[label] = line_address
            parts = parts[1:]
            if not parts:
                return None

        op = parts[0].lower()
        # R-type instructions
        if op in self.r_type:
            opcode = self.r_type[op]['opcode']
            funct = self.r_type[op]['funct']
            rd = parts[1]
            rs = parts[2]
            rt = parts[3]
            binary = f"{opcode}{self.registers[rs]:05b}{self.registers[rt]:05b}{self.registers[rd]:05b}00000{funct}"
            return {'binary': binary, 'original': instruction.strip()}

        # I-type instructions: lw, sw, beq, addi
        elif op in self.i_type:
            opcode = self.i_type[op]
            
            # Load/Store Word: lw, sw
            if op in ['lw', 'sw']:
                rt = parts[1]
                if '(' in parts[2]:  # Offset(base) format
                    offset_base = parts[2].split('(')
                    base_register = offset_base[1].strip(')')
                    imm = format(int(offset_base[0]), '016b')
                else:  # Handle label
                    label = parts[2]
                    if label in self.data_section:
                        imm = format(self.data_section[label]['address'] - self.data_start_address, '016b')
                    else:
                        imm = '0000000000000000'
                    base_register = '$zero'
                    
                rs = base_register
                binary = f"{opcode}{self.registers[rs]:05b}{self.registers[rt]:05b}{imm}"

            # Immediate arithmetic: addi
            elif op == 'addi':
                rt = parts[2] #Source register
                rs = parts[1]  #Destination register
                imm = int(parts[3])  # Immediate value
                # Convert the immediate to 16-bit binary with sign extension
                imm = format(imm & 0xFFFF, '016b')
                binary = f"{opcode}{self.registers[rt]:05b}{self.registers[rs]:05b}{imm}"

            # Branch on equal: beq
            elif op == 'beq':
                rs = parts[1]
                rt = parts[2]
                label = parts[3]
                
                if label in self.labels:
                    offset = (self.labels[label] - (line_address + 4)) // 4
                    imm = format(offset & 0xFFFF, '016b')
                else:
                    imm = '0000000000000000'
                
                binary = f"{opcode}{self.registers[rs]:05b}{self.registers[rt]:05b}{imm}"

            return {'binary': binary, 'original': instruction.strip()}

        # J-type instruction
        elif op in self.j_type:
            opcode = self.j_type[op]
            try:
                target = int(parts[1])
            except ValueError:
                if parts[1] in self.labels:
                    target = self.labels[parts[1]] // 4
                else:
                    target = 0

            binary = f"{opcode}{format(target & 0x3FFFFFF, '026b')}"
            return {'binary': binary, 'original': instruction.strip()}

        return None

    def compile_file(self, filename="task1.asm"):
        try:
            with open(filename, 'r') as file:
                lines = file.readlines()
        except FileNotFoundError:
            print(f"Error: {filename} not found.")
            return [], {}

        text_section = False
        data_section = False
        compiled_instructions = []
        current_address = self.current_address

        # First pass: collect labels and data
        for line in lines:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            
            if '.data' in line:
                data_section = True
                text_section = False
                continue
            elif '.text' in line:
                text_section = True
                data_section = False
                continue
            
            if data_section:
                self.parse_data_section(line)
            elif text_section and ':' in line:
                label = line.split(':')[0]
                self.labels[label] = current_address
            
            if text_section and not line.strip().endswith(':'):
                current_address += 4

        # Second pass: compile instructions
        text_section = False
        current_address = self.current_address
        
        for line in lines:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            
            if '.text' in line:
                text_section = True
                continue
            elif '.data' in line:
                text_section = False
                continue
            
            if text_section and not line.endswith(':'):
                compiled = self.compile_instruction(line, current_address)
                if compiled:
                    compiled['address'] = format(current_address, '08x')
                    compiled_instructions.append(compiled)
                    current_address += 4

        return compiled_instructions, self.data_section



class MIPSSimulator:
    def __init__(self, compiler):
        self.compiler = compiler
        self.registers = {f'${i}': 0 for i in range(32)}  # Initialize all registers to 0
        self.memory = {}  # Simulated main memory
        self.pc = 0x00400000  # Program counter
        
        # Initialize special registers
        self.registers['$zero'] = 0  # Always holds 0
        self.registers['$sp'] = 0x7FFFFFFC  # Stack pointer
        
        # ALU operations
        self.alu_ops = {
            'add': lambda x, y: x + y,
            'sub': lambda x, y: x - y,
            'and': lambda x, y: x & y,
            'or':  lambda x, y: x | y,
            'slt': lambda x, y: 1 if x < y else 0,
        }

    def reset(self):
        """Reset the simulator state"""
        self.__init__(self.compiler)

    def get_control_signals(self, instruction):
        """Generate control signals based on instruction type"""
        op = instruction['binary'][:6]
        
        signals = {
            'RegDst': 0, 'ALUSrc': 0, 'MemtoReg': 0,
            'RegWrite': 0, 'MemRead': 0, 'MemWrite': 0,
            'Branch': 0, 'Jump': 0, 'ALUOp': '00'
        }
        
        # R-type instructions
        if op == '000000':
            signals['RegDst'] = 1
            signals['RegWrite'] = 1
            signals['ALUOp'] = '10'
        
        # Load word
        elif op == '100011':
            signals['ALUSrc'] = 1
            signals['MemtoReg'] = 1
            signals['RegWrite'] = 1
            signals['MemRead'] = 1
            signals['ALUOp'] = '00'
        
        # Store word
        elif op == '101011':
            signals['ALUSrc'] = 1
            signals['MemWrite'] = 1
            signals['ALUOp'] = '00'
        
        # Branch if equal
        elif op == '000100':
            signals['Branch'] = 1
            signals['ALUOp'] = '01'
        
        # Jump instruction
        elif op == '000010':
            signals['Jump'] = 1
        
        return signals

    def load_data_to_memory(self):
        """Load data section into simulated memory."""
        for label, value in self.compiler.data_section.items():
            address = 0x10010000 + list(self.compiler.data_section.keys()).index(label) * 4
            self.memory[address] = value  # Load value into memory
            print(f"Loaded {value} into memory at address 0x{address:08x}")



    def display_initial_memory(self):
        """Display the initial memory state."""
        print("Initial Memory State:")
        for addr in sorted(self.memory.keys()):
            print(f"0x{addr:08x}: {self.memory[addr]}")
        print("-" * 30)  # Separator for clarity

    def execute_instruction(self, instruction):
        """Execute a single instruction."""
        binary = instruction['binary']
        op = binary[:6]

        # R-type instructions
        if op == '000000':
            rs = int(binary[6:11], 2)
            rt = int(binary[11:16], 2)
            rd = int(binary[16:21], 2)
            funct = binary[26:32]

            # Retrieve values from registers
            rs_value = self.registers[f'${rs}']
            rt_value = self.registers[f'${rt}']
            if funct == '100000':  # add
                self.registers[f'${rd}'] = self.alu_ops['add'](rs_value, rt_value)
            elif funct == '100010':  # sub
                self.registers[f'${rd}'] = self.alu_ops['sub'](rs_value, rt_value)
            elif funct == '100100':  # and
                self.registers[f'${rd}'] = self.alu_ops['and'](rs_value, rt_value)
            elif funct == '100101':  # or
                self.registers[f'${rd}'] = self.alu_ops['or'](rs_value, rt_value)
            elif funct == '101010':  # slt
                self.registers[f'${rd}'] = self.alu_ops['slt'](rs_value, rt_value)

            # Update the program counter
            self.pc += 4

        # I-type instructions
        elif op == '001000':  # addi opcode
            rs = int(binary[6:11], 2)  # Source register
            rt = int(binary[11:16], 2)  # Destination register
            imm = int(binary[16:], 2)  # Immediate value

            # Convert imm to signed integer (if negative)
            if imm >= 2**15:
                imm -= 2**16

            # Perform addition and store result in destination register
            self.registers[f'${rt}'] = (self.registers[f'${rs}'] + imm) & 0xFFFFFFFF

            print(f"Executed addi: ${rt} = {self.registers[f'${rt}']} (rs: ${rs} + imm: {imm})")
            self.pc += 4

        elif op == '100011':  # lw opcode
            rt = int(binary[11:16], 2)  # Destination register

            # Check if the second part is a label from the data section
            label = instruction['original'].split()[2]
            if label in self.compiler.data_section:
                # Get the value from the data section
                value = self.compiler.data_section[label]['value']
                # Store this value in the destination register
                self.registers[f'${rt}'] = value
                print(f"Loaded value {value} from label {label} into register ${rt}")
            else:
                # Handle regular lw with base and offset addressing
                rs = int(binary[6:11], 2)  # Base register
                imm = int(binary[16:], 2)  # Immediate value
                # Convert imm to signed integer
                if imm >= 2**15:  # Check if imm is negative
                    imm -= 2**16

                base_value = self.registers[f'${rs}']  # Value in the base register
                address = base_value + imm  # Effective memory address
                self.registers[f'${rt}'] = self.memory.get(address, 0)  # Load value from memory
                print(f"Loaded from address 0x{address:08x} into register ${rt} with value {self.registers[f'${rt}']}")

        # Store word
        elif op == '101011':
            rs = int(binary[6:11], 2)  # base register
            rt = int(binary[11:16], 2)  # source register
            imm = int(binary[16:], 2)   # offset

            base_value = self.registers[f'${rs}']
            address = base_value + imm
            self.memory[address] = self.registers[f'${rt}']  # Store value in memory
            self.pc += 4

        # Branch if equal
        elif op == '000100':
            rs = int(binary[6:11], 2)
            rt = int(binary[11:16], 2)
            imm = int(binary[16:], 2)
            # Before beq execution
            print(f"Executing beq: $rs={rs} ($t0={self.registers[f'${rs}']}), $rt={rt} ($t1={self.registers[f'${rt}']})")

            if self.registers[f'${rs}'] == self.registers[f'${rt}']:
                self.pc += imm * 4  # Branch
            else:
                self.pc += 4  

        # Jump instruction
        elif op == '000010':
            address = int(binary[6:], 2)  # Target address
            self.pc = (self.pc & 0xF0000000) | (address << 2)  # Jump to address

    def execute_program(self, instructions):
        """Execute all instructions in the program."""
        self.reset()
        
        # Load data into memory before executing instructions
        self.load_data_to_memory()
        self.display_initial_memory()  # Show initial memory

        execution_trace = []
        
        for instruction in instructions:
            # Record state before execution
            state = {
                'pc': hex(self.pc),
                'instruction': instruction['original'],
                'registers': self.registers.copy(),
                'memory': self.memory.copy()
            }
            
            # Execute instruction
            self.execute_instruction(instruction)
            
            # Record state after execution
            state['next_pc'] = hex(self.pc)
            execution_trace.append(state)
            
            # Print all registers after each instruction
            print(f"\nAfter executing: {instruction['original']}")
            print("Registers:")
            for reg, value in self.registers.items():
                print(f"{reg}: {value}")
            print("-" * 30)  

        return execution_trace


def main():
    compiler = MIPSCompiler()
    test_file = "program.asm"
    instructions, data_section = compiler.compile_file(test_file)
    simulator = MIPSSimulator(compiler)
    simulator.load_data_to_memory()
    execution_trace = simulator.execute_program(instructions)
    print("\nFinal State of Registers:")
    for reg, value in simulator.registers.items():
        print(f"{reg}: {value}")
    print("\nFinal State of Memory:")
    for addr, value in sorted(simulator.memory.items()):
        print(f"0x{addr:08x}: {value}")


if __name__ == "__main__":
    main()