import re import sys import struct class Y86_64Simulator: # Y86-64 Register IDs REG_RAX = 0 REG_RCX = 1 REG_RDX = 2 REG_RBX = 3 REG_RSP = 4 REG_RBP = 5 REG_RSI = 6 REG_RDI = 7 REG_R8 = 8 REG_R9 = 9 REG_R10 = 10 REG_R11 = 11 REG_R12 = 12 REG_R13 = 13 REG_R14 = 14 REG_NONE = 15 # Used for instructions with no register operand # Status codes STAT_AOK = 1 # Normal operation STAT_HLT = 2 # Halt instruction encountered STAT_ADR = 3 # Invalid address STAT_INS = 4 # Invalid instruction # Condition codes CC_ZF = 0 # Zero flag CC_SF = 1 # Sign flag CC_OF = 2 # Overflow flag def __init__(self, mem_size=0x10000): # Initialize memory, registers, program counter and status self.memory = bytearray(mem_size) self.mem_size = mem_size self.registers = [0] * 15 # 15 registers (RAX to R14) self.pc = 0 self.status = self.STAT_AOK # Condition code flags self.cc = [False] * 3 # ZF, SF, OF # Register name to ID mapping self.reg_name_to_id = { 'rax': self.REG_RAX, '%rax': self.REG_RAX, 'rcx': self.REG_RCX, '%rcx': self.REG_RCX, 'rdx': self.REG_RDX, '%rdx': self.REG_RDX, 'rbx': self.REG_RBX, '%rbx': self.REG_RBX, 'rsp': self.REG_RSP, '%rsp': self.REG_RSP, 'rbp': self.REG_RBP, '%rbp': self.REG_RBP, 'rsi': self.REG_RSI, '%rsi': self.REG_RSI, 'rdi': self.REG_RDI, '%rdi': self.REG_RDI, 'r8': self.REG_R8, '%r8': self.REG_R8, 'r9': self.REG_R9, '%r9': self.REG_R9, 'r10': self.REG_R10, '%r10': self.REG_R10, 'r11': self.REG_R11, '%r11': self.REG_R11, 'r12': self.REG_R12, '%r12': self.REG_R12, 'r13': self.REG_R13, '%r13': self.REG_R13, 'r14': self.REG_R14, '%r14': self.REG_R14 } def load_coe_file(self, filename): """ Load a program from a Xilinx COE format file. COE file format typically has a header section followed by hex values. """ try: with open(filename, 'r') as f: content = f.read() # Find memory initialization section memory_init = re.search(r'memory_initialization_vector\s*=\s*(.*?)(?:;|\Z)', content, re.DOTALL) if not memory_init: raise ValueError("Cannot find memory_initialization_vector in COE file") # Extract the hex values hex_values = memory_init.group(1).replace('\n', '').replace(' ', '').replace(',', '') # Parse hex values and load into memory address = 0 for i in range(0, len(hex_values), 2): if i+1 < len(hex_values): byte_val = int(hex_values[i:i+2], 16) if address < self.mem_size: self.memory[address] = byte_val address += 1 else: print(f"Warning: Address {address} exceeds memory size, truncating program") break print(f"Loaded {address} bytes from {filename}") return True except Exception as e: print(f"Error loading COE file: {e}") return False def read_byte(self, addr): """Read a byte from memory at address addr""" if 0 <= addr < self.mem_size: return self.memory[addr] else: self.status = self.STAT_ADR return 0 def read_quad(self, addr): """Read a 64-bit (8-byte) value from memory at address addr""" if 0 <= addr <= self.mem_size - 8: return int.from_bytes(self.memory[addr:addr+8], byteorder='little') else: self.status = self.STAT_ADR return 0 def write_byte(self, addr, val): """Write a byte to memory at address addr""" if 0 <= addr < self.mem_size: self.memory[addr] = val & 0xFF else: self.status = self.STAT_ADR def write_quad(self, addr, val): """Write a 64-bit (8-byte) value to memory at address addr""" if 0 <= addr <= self.mem_size - 8: val_bytes = val.to_bytes(8, byteorder='little') self.memory[addr:addr+8] = val_bytes else: self.status = self.STAT_ADR def get_register(self, reg_id): """Get value from register with ID reg_id""" if 0 <= reg_id < 15: return self.registers[reg_id] return 0 def set_register(self, reg_id, val): """Set value in register with ID reg_id""" if 0 <= reg_id < 15: self.registers[reg_id] = val & 0xFFFFFFFFFFFFFFFF # Ensure 64-bit value def set_cc(self, result): """Set condition codes based on result""" # Zero flag self.cc[self.CC_ZF] = (result == 0) # Sign flag (negative) self.cc[self.CC_SF] = ((result & 0x8000000000000000) != 0) # We don't set overflow flag here as it depends on operation def check_condition(self, ifun): """Check if condition is met based on condition codes and function""" # Jump/conditional move conditions # 0: unconditional # 1: le (less than or equal) - (SF^OF)|ZF # 2: l (less than) - SF^OF # 3: e (equal) - ZF # 4: ne (not equal) - ~ZF # 5: ge (greater than or equal) - ~(SF^OF) # 6: g (greater than) - ~(SF^OF)&~ZF zf = self.cc[self.CC_ZF] sf = self.cc[self.CC_SF] of = self.cc[self.CC_OF] if ifun == 0: # unconditional return True elif ifun == 1: # le return (sf != of) or zf elif ifun == 2: # l return sf != of elif ifun == 3: # e return zf elif ifun == 4: # ne return not zf elif ifun == 5: # ge return sf == of elif ifun == 6: # g return (sf == of) and (not zf) else: return False def fetch_decode_execute(self): """Fetch, decode and execute a single instruction""" if self.status != self.STAT_AOK: return False # Fetch instr_byte = self.read_byte(self.pc) if self.status != self.STAT_AOK: return False # Decode instruction icode = (instr_byte >> 4) & 0xF ifun = instr_byte & 0xF # Increment PC (will be updated further based on instruction size) self.pc += 1 # Initialize variables for the instruction valA = 0 valB = 0 valC = 0 valE = 0 valM = 0 dstE = self.REG_NONE dstM = self.REG_NONE srcA = self.REG_NONE srcB = self.REG_NONE # Process based on instruction code if icode == 0: # HALT self.status = self.STAT_HLT return False elif icode == 1: # NOP pass # No operation elif icode == 2: # rrmovq or conditional move # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF self.pc += 1 # Check condition if self.check_condition(ifun): # Move value from rA to rB valA = self.get_register(rA) dstE = rB valE = valA elif icode == 3: # irmovq # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF # Should be 0xF (no register) rB = reg_byte & 0xF self.pc += 1 # Get immediate value (8 bytes) valC = self.read_quad(self.pc) self.pc += 8 # Move immediate to register dstE = rB valE = valC elif icode == 4: # rmmovq # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF self.pc += 1 # Get displacement (8 bytes) valC = self.read_quad(self.pc) self.pc += 8 # Calculate memory address valB = self.get_register(rB) valE = valB + valC # Move from register to memory valA = self.get_register(rA) self.write_quad(valE, valA) elif icode == 5: # mrmovq # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF self.pc += 1 # Get displacement (8 bytes) valC = self.read_quad(self.pc) self.pc += 8 # Calculate memory address valB = self.get_register(rB) valE = valB + valC # Move from memory to register valM = self.read_quad(valE) dstM = rA elif icode == 6: # OPq (arithmetic/logical operations) # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF self.pc += 1 # Get operands valA = self.get_register(rA) valB = self.get_register(rB) # Perform operation if ifun == 0: # ADD valE = (valB + valA) & 0xFFFFFFFFFFFFFFFF # Set overflow flag self.cc[self.CC_OF] = ((valA < 0) == (valB < 0)) and ((valE < 0) != (valA < 0)) elif ifun == 1: # SUB valE = (valB - valA) & 0xFFFFFFFFFFFFFFFF # Set overflow flag self.cc[self.CC_OF] = ((valA < 0) != (valB < 0)) and ((valE < 0) != (valB < 0)) elif ifun == 2: # AND valE = valB & valA self.cc[self.CC_OF] = False elif ifun == 3: # XOR valE = valB ^ valA self.cc[self.CC_OF] = False else: self.status = self.STAT_INS return False # Set other condition codes self.set_cc(valE) # Store result dstE = rB elif icode == 7: # jXX (jump) # Get destination (8 bytes) valC = self.read_quad(self.pc) self.pc += 8 # Check condition if self.check_condition(ifun): # Jump self.pc = valC elif icode == 8: # call # Get destination (8 bytes) valC = self.read_quad(self.pc) self.pc += 8 # Push return address onto stack valB = self.get_register(self.REG_RSP) valE = valB - 8 self.set_register(self.REG_RSP, valE) self.write_quad(valE, self.pc) # Jump to function self.pc = valC elif icode == 9: # ret # Pop return address from stack valA = self.get_register(self.REG_RSP) valB = valA valE = valB + 8 valM = self.read_quad(valA) # Update stack pointer self.set_register(self.REG_RSP, valE) # Jump to return address self.pc = valM elif icode == 10: # pushq # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF # Should be 0xF (no register) self.pc += 1 # Get value to push valA = self.get_register(rA) # Decrement stack pointer valB = self.get_register(self.REG_RSP) valE = valB - 8 # Push value onto stack self.set_register(self.REG_RSP, valE) self.write_quad(valE, valA) elif icode == 11: # popq # Get register byte reg_byte = self.read_byte(self.pc) rA = (reg_byte >> 4) & 0xF rB = reg_byte & 0xF # Should be 0xF (no register) self.pc += 1 # Pop value from stack valA = self.get_register(self.REG_RSP) valB = valA valE = valB + 8 valM = self.read_quad(valA) # Update registers dstM = rA self.set_register(self.REG_RSP, valE) else: self.status = self.STAT_INS return False # Write results to registers if dstE != self.REG_NONE: self.set_register(dstE, valE) if dstM != self.REG_NONE: self.set_register(dstM, valM) return self.status == self.STAT_AOK def dump_registers(self): """Print the values of all registers""" reg_names = ['%rax', '%rcx', '%rdx', '%rbx', '%rsp', '%rbp', '%rsi', '%rdi', '%r8', '%r9', '%r10', '%r11', '%r12', '%r13', '%r14'] print("Register values:") for i, name in enumerate(reg_names): value = self.registers[i] print(f"{name} = 0x{value:016x} ({value})") print(f"PC = 0x{self.pc:x}") print(f"ZF = {1 if self.cc[self.CC_ZF] else 0}, SF = {1 if self.cc[self.CC_SF] else 0}, OF = {1 if self.cc[self.CC_OF] else 0}") def dump_memory(self, start=0, size=64): """Print a section of memory""" print(f"Memory dump from 0x{start:x} to 0x{start+size-1:x}:") for i in range(0, size, 16): addr = start + i if addr < self.mem_size: line = f"0x{addr:04x}: " for j in range(16): if addr + j < self.mem_size: line += f"{self.memory[addr+j]:02x} " else: line += " " if j == 7: line += " " # ASCII representation line += " |" for j in range(16): if addr + j < self.mem_size: char = self.memory[addr+j] if 32 <= char <= 126: # Printable ASCII line += chr(char) else: line += "." else: line += " " line += "|" print(line) def run(self, max_instructions=1000000): """Run the program until halt or error, or until max_instructions is reached""" instructions_executed = 0 while self.status == self.STAT_AOK and instructions_executed < max_instructions: if not self.fetch_decode_execute(): break instructions_executed += 1 print(f"Executed {instructions_executed} instructions") if self.status == self.STAT_HLT: print("Program halted normally") elif self.status == self.STAT_ADR: print("Error: Invalid memory address") elif self.status == self.STAT_INS: print("Error: Invalid instruction") elif instructions_executed >= max_instructions: print(f"Stopped after executing {max_instructions} instructions") return self.status def main(): if len(sys.argv) < 2: print("Usage: python y86_64_simulator.py ") return simulator = Y86_64Simulator() if simulator.load_coe_file(sys.argv[1]): simulator.run() simulator.dump_registers() simulator.dump_memory(0, 128) # Dump first 128 bytes of memory if __name__ == "__main__": main()