import os
import hashlib


class obsidianvm:
    op_nop = 0x00
    op_xor = 0x01
    op_add = 0x02
    op_sub = 0x03
    op_mul = 0x04
    op_div = 0x05
    op_mod = 0x06
    op_and = 0x07
    op_or = 0x08
    op_not = 0x09
    op_rol = 0x0a
    op_ror = 0x0b
    op_shl = 0x0c
    op_shr = 0x0d
    op_sbox = 0x0e
    op_perm = 0x0f
    op_mix = 0x10
    op_jmp = 0x20
    op_jz = 0x21
    op_jnz = 0x22
    op_jg = 0x23
    op_jl = 0x24
    op_je = 0x25
    op_jne = 0x26
    op_call = 0x30
    op_ret = 0x31
    op_push = 0x40
    op_pop = 0x41
    op_pushf = 0x42
    op_popf = 0x43
    op_load = 0x50
    op_store = 0x51
    op_loadb = 0x52
    op_storeb = 0x53
    op_loadw = 0x54
    op_storew = 0x55
    op_mov = 0x60
    op_movi = 0x61
    op_cmp = 0x62
    op_test = 0x63
    op_inc = 0x64
    op_dec = 0x65
    op_neg = 0x66
    op_swap = 0x67
    op_xchg = 0x68
    op_lea = 0x69
    op_int = 0x70
    op_iret = 0x71
    op_cli = 0x72
    op_sti = 0x73
    op_in = 0x80
    op_out = 0x81
    op_syscall = 0x90
    op_sysret = 0x91
    op_hash = 0xa0
    op_encrypt = 0xa1
    op_decrypt = 0xa2
    op_rand = 0xa3
    op_halt = 0xff
    
    flag_zero = 0x01
    flag_carry = 0x02
    flag_sign = 0x04
    flag_overflow = 0x08
    flag_interrupt = 0x10
    
    int_timer = 0x00
    int_keyboard = 0x01
    int_disk = 0x02
    int_crypto = 0x03
    
    syscall_read = 0x00
    syscall_write = 0x01
    syscall_open = 0x02
    syscall_close = 0x03
    syscall_malloc = 0x04
    syscall_free = 0x05
    syscall_exit = 0x06
    syscall_getpid = 0x07
    syscall_time = 0x08
    syscall_random = 0x09
    
    def __init__(self, memory_size=1048576, num_registers=32):
        self._memory_size = memory_size
        self._memory = bytearray(memory_size)
        self._num_registers = num_registers
        self._registers = [0] * num_registers
        self._pc = 0
        self._sp = memory_size - 0x10000
        self._bp = self._sp
        self._flags = 0
        self._running = False
        self._halted = False
        self._interrupt_enabled = True
        self._interrupt_vector = [0] * 256
        self._interrupt_pending = []
        self._call_stack = []
        self._max_instructions = 100000000
        self._instruction_count = 0
        self._sbox = self._generate_sbox()
        self._inv_sbox = self._generate_inv_sbox()
        self._perm_table = self._generate_perm()
        self._inv_perm_table = self._generate_inv_perm()
        self._heap_start = 0x100000
        self._heap_end = memory_size - 0x20000
        self._heap_ptr = self._heap_start
        self._allocations = {}
        self._io_ports = {}
        self._devices = {}
        self._timer_counter = 0
        self._timer_interval = 1000
        self._stdin_buffer = bytearray()
        self._stdout_buffer = bytearray()
        self._files = {}
        self._next_fd = 3
        self._pid = os.getpid() & 0xffff
        self._entropy_pool = bytearray(256)
        self._entropy_index = 0
        self._init_entropy()
    
    def _init_entropy(self):
        import time
        seed = int(time.time() * 1000000) ^ id(self)
        for i in range(256):
            seed = (seed * 1103515245 + 12345) & 0xffffffff
            self._entropy_pool[i] = seed & 0xff
    
    def _generate_sbox(self):
        sbox = list(range(256))
        state = 0x5a3c9f7e2d1b4a8c
        for i in range(255, 0, -1):
            state ^= (state << 13) & 0xffffffffffffffff
            state ^= (state >> 7) & 0xffffffffffffffff
            state ^= (state << 17) & 0xffffffffffffffff
            j = state % (i + 1)
            sbox[i], sbox[j] = sbox[j], sbox[i]
        return sbox
    
    def _generate_inv_sbox(self):
        inv = [0] * 256
        for i, v in enumerate(self._sbox):
            inv[v] = i
        return inv
    
    def _generate_perm(self):
        perm = list(range(8))
        state = 0xa5c3e1f7b9d2840c
        for i in range(7, 0, -1):
            state ^= (state << 21) & 0xffffffffffffffff
            state ^= (state >> 35) & 0xffffffffffffffff
            j = state % (i + 1)
            perm[i], perm[j] = perm[j], perm[i]
        return perm
    
    def _generate_inv_perm(self):
        inv = [0] * 8
        for i, v in enumerate(self._perm_table):
            inv[v] = i
        return inv
    
    def reset(self):
        self._memory = bytearray(self._memory_size)
        self._registers = [0] * self._num_registers
        self._pc = 0
        self._sp = self._memory_size - 0x10000
        self._bp = self._sp
        self._flags = 0
        self._running = False
        self._halted = False
        self._interrupt_enabled = True
        self._call_stack = []
        self._instruction_count = 0
        self._heap_ptr = self._heap_start
        self._allocations = {}
        self._stdout_buffer = bytearray()
        self._stdin_buffer = bytearray()
    
    def load_program(self, program, offset=0):
        if offset + len(program) > self._memory_size:
            raise MemoryError("program too large")
        for i, byte in enumerate(program):
            self._memory[offset + i] = byte
    
    def load_data(self, data, offset):
        if offset + len(data) > self._memory_size:
            raise MemoryError("data too large")
        for i, byte in enumerate(data):
            self._memory[offset + i] = byte
    
    def get_data(self, offset, length):
        if offset + length > self._memory_size:
            raise MemoryError("read beyond bounds")
        return bytes(self._memory[offset:offset + length])
    
    def set_register(self, reg, value):
        if 0 <= reg < self._num_registers:
            self._registers[reg] = value & 0xffffffff
    
    def get_register(self, reg):
        if 0 <= reg < self._num_registers:
            return self._registers[reg]
        return 0
    
    def _read_byte(self, addr):
        if 0 <= addr < self._memory_size:
            return self._memory[addr]
        return 0
    
    def _write_byte(self, addr, value):
        if 0 <= addr < self._memory_size:
            self._memory[addr] = value & 0xff
    
    def _read_word(self, addr):
        low = self._read_byte(addr)
        high = self._read_byte(addr + 1)
        return (high << 8) | low
    
    def _write_word(self, addr, value):
        self._write_byte(addr, value & 0xff)
        self._write_byte(addr + 1, (value >> 8) & 0xff)
    
    def _read_dword(self, addr):
        b0 = self._read_byte(addr)
        b1 = self._read_byte(addr + 1)
        b2 = self._read_byte(addr + 2)
        b3 = self._read_byte(addr + 3)
        return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
    
    def _write_dword(self, addr, value):
        self._write_byte(addr, value & 0xff)
        self._write_byte(addr + 1, (value >> 8) & 0xff)
        self._write_byte(addr + 2, (value >> 16) & 0xff)
        self._write_byte(addr + 3, (value >> 24) & 0xff)
    
    def _fetch(self):
        if self._pc >= self._memory_size:
            return self.op_halt
        opcode = self._memory[self._pc]
        self._pc += 1
        return opcode
    
    def _fetch_byte(self):
        return self._fetch()
    
    def _fetch_word(self):
        low = self._fetch_byte()
        high = self._fetch_byte()
        return (high << 8) | low
    
    def _fetch_dword(self):
        b0 = self._fetch_byte()
        b1 = self._fetch_byte()
        b2 = self._fetch_byte()
        b3 = self._fetch_byte()
        return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
    
    def _push_byte(self, value):
        self._sp -= 1
        self._write_byte(self._sp, value)
    
    def _pop_byte(self):
        value = self._read_byte(self._sp)
        self._sp += 1
        return value
    
    def _push_word(self, value):
        self._sp -= 2
        self._write_word(self._sp, value)
    
    def _pop_word(self):
        value = self._read_word(self._sp)
        self._sp += 2
        return value
    
    def _push_dword(self, value):
        self._sp -= 4
        self._write_dword(self._sp, value)
    
    def _pop_dword(self):
        value = self._read_dword(self._sp)
        self._sp += 4
        return value
    
    def _set_flag(self, flag, value):
        if value:
            self._flags |= flag
        else:
            self._flags &= ~flag
    
    def _get_flag(self, flag):
        return (self._flags & flag) != 0
    
    def _update_flags(self, result, is_signed=False):
        self._set_flag(self.flag_zero, result == 0)
        self._set_flag(self.flag_sign, (result & 0x80000000) != 0)
        self._set_flag(self.flag_carry, result > 0xffffffff or result < 0)
        if is_signed:
            self._set_flag(self.flag_overflow, result > 0x7fffffff or result < -0x80000000)
    
    def _rol8(self, val, shift):
        shift %= 8
        return ((val << shift) | (val >> (8 - shift))) & 0xff
    
    def _ror8(self, val, shift):
        shift %= 8
        return ((val >> shift) | (val << (8 - shift))) & 0xff
    
    def _rol32(self, val, shift):
        shift %= 32
        return ((val << shift) | (val >> (32 - shift))) & 0xffffffff
    
    def _ror32(self, val, shift):
        shift %= 32
        return ((val >> shift) | (val << (32 - shift))) & 0xffffffff
    
    def _permute_byte(self, val):
        bits = [(val >> i) & 1 for i in range(8)]
        permuted = [bits[self._perm_table[i]] for i in range(8)]
        result = 0
        for i in range(8):
            result |= permuted[i] << i
        return result
    
    def _inv_permute_byte(self, val):
        bits = [(val >> i) & 1 for i in range(8)]
        permuted = [bits[self._inv_perm_table[i]] for i in range(8)]
        result = 0
        for i in range(8):
            result |= permuted[i] << i
        return result
    
    def _malloc(self, size):
        if self._heap_ptr + size > self._heap_end:
            return 0
        addr = self._heap_ptr
        self._heap_ptr += size
        self._allocations[addr] = size
        return addr
    
    def _free(self, addr):
        if addr in self._allocations:
            del self._allocations[addr]
    
    def _get_random(self):
        idx = self._entropy_index
        self._entropy_index = (self._entropy_index + 1) % 256
        
        val = self._entropy_pool[idx]
        self._entropy_pool[idx] = (val * 1103515245 + 12345) & 0xff
        
        return val
    
    def _get_random_dword(self):
        b0 = self._get_random()
        b1 = self._get_random()
        b2 = self._get_random()
        b3 = self._get_random()
        return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
    
    def _hash_block(self, addr, length):
        data = self.get_data(addr, length)
        h = hashlib.sha256(data).digest()
        return int.from_bytes(h[:4], "little")
    
    def _trigger_interrupt(self, int_num):
        if self._interrupt_enabled:
            self._interrupt_pending.append(int_num)
    
    def _handle_interrupts(self):
        if not self._interrupt_enabled or not self._interrupt_pending:
            return
        
        int_num = self._interrupt_pending.pop(0)
        handler = self._interrupt_vector[int_num]
        
        if handler != 0:
            self._push_dword(self._flags)
            self._push_dword(self._pc)
            self._interrupt_enabled = False
            self._pc = handler
    
    def _handle_syscall(self):
        syscall_num = self._registers[0]
        
        if syscall_num == self.syscall_read:
            fd = self._registers[1]
            buf = self._registers[2]
            count = self._registers[3]
            
            if fd == 0 and self._stdin_buffer:
                data = self._stdin_buffer[:count]
                self._stdin_buffer = self._stdin_buffer[count:]
                for i, b in enumerate(data):
                    self._write_byte(buf + i, b)
                self._registers[0] = len(data)
            else:
                self._registers[0] = 0
        
        elif syscall_num == self.syscall_write:
            fd = self._registers[1]
            buf = self._registers[2]
            count = self._registers[3]
            
            if fd == 1:
                data = self.get_data(buf, count)
                self._stdout_buffer.extend(data)
                self._registers[0] = count
            else:
                self._registers[0] = 0
        
        elif syscall_num == self.syscall_malloc:
            size = self._registers[1]
            addr = self._malloc(size)
            self._registers[0] = addr
        
        elif syscall_num == self.syscall_free:
            addr = self._registers[1]
            self._free(addr)
            self._registers[0] = 0
        
        elif syscall_num == self.syscall_exit:
            self._running = False
            self._halted = True
        
        elif syscall_num == self.syscall_getpid:
            self._registers[0] = self._pid
        
        elif syscall_num == self.syscall_time:
            import time
            self._registers[0] = int(time.time()) & 0xffffffff
        
        elif syscall_num == self.syscall_random:
            self._registers[0] = self._get_random_dword()
        
        else:
            self._registers[0] = 0xffffffff
    
    def _execute(self, opcode):
        if opcode == self.op_nop:
            pass
        
        elif opcode == self.op_xor:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] ^ self._registers[src]
            self._registers[dst] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_add:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] + self._registers[src]
            self._registers[dst] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_sub:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] - self._registers[src]
            self._registers[dst] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_mul:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] * self._registers[src]
            self._registers[dst] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_div:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            if self._registers[src] != 0:
                result = self._registers[dst] // self._registers[src]
                self._registers[dst] = result & 0xffffffff
            self._update_flags(self._registers[dst])
        
        elif opcode == self.op_mod:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            if self._registers[src] != 0:
                result = self._registers[dst] % self._registers[src]
                self._registers[dst] = result & 0xffffffff
            self._update_flags(self._registers[dst])
        
        elif opcode == self.op_and:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] & self._registers[src]
            self._registers[dst] = result
            self._update_flags(result)
        
        elif opcode == self.op_or:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            result = self._registers[dst] | self._registers[src]
            self._registers[dst] = result
            self._update_flags(result)
        
        elif opcode == self.op_not:
            reg = self._fetch_byte() & 0x1f
            result = ~self._registers[reg] & 0xffffffff
            self._registers[reg] = result
            self._update_flags(result)
        
        elif opcode == self.op_rol:
            reg = self._fetch_byte() & 0x1f
            shift = self._fetch_byte()
            self._registers[reg] = self._rol32(self._registers[reg], shift)
        
        elif opcode == self.op_ror:
            reg = self._fetch_byte() & 0x1f
            shift = self._fetch_byte()
            self._registers[reg] = self._ror32(self._registers[reg], shift)
        
        elif opcode == self.op_shl:
            reg = self._fetch_byte() & 0x1f
            shift = self._fetch_byte()
            result = (self._registers[reg] << shift) & 0xffffffff
            self._registers[reg] = result
            self._update_flags(result)
        
        elif opcode == self.op_shr:
            reg = self._fetch_byte() & 0x1f
            shift = self._fetch_byte()
            result = self._registers[reg] >> shift
            self._registers[reg] = result
            self._update_flags(result)
        
        elif opcode == self.op_sbox:
            reg = self._fetch_byte() & 0x1f
            val = self._registers[reg] & 0xff
            self._registers[reg] = (self._registers[reg] & 0xffffff00) | self._sbox[val]
        
        elif opcode == self.op_perm:
            reg = self._fetch_byte() & 0x1f
            val = self._registers[reg] & 0xff
            self._registers[reg] = (self._registers[reg] & 0xffffff00) | self._permute_byte(val)
        
        elif opcode == self.op_mix:
            r1 = self._fetch_byte() & 0x1f
            r2 = self._fetch_byte() & 0x1f
            a = self._registers[r1] & 0xff
            b = self._registers[r2] & 0xff
            a = self._rol8(a, 3)
            b = self._ror8(b, 5)
            a ^= b
            b = (b + a) & 0xff
            self._registers[r1] = (self._registers[r1] & 0xffffff00) | a
            self._registers[r2] = (self._registers[r2] & 0xffffff00) | b
        
        elif opcode == self.op_jmp:
            addr = self._fetch_dword()
            self._pc = addr
        
        elif opcode == self.op_jz:
            addr = self._fetch_dword()
            if self._get_flag(self.flag_zero):
                self._pc = addr
        
        elif opcode == self.op_jnz:
            addr = self._fetch_dword()
            if not self._get_flag(self.flag_zero):
                self._pc = addr
        
        elif opcode == self.op_jg:
            addr = self._fetch_dword()
            if not self._get_flag(self.flag_zero) and not self._get_flag(self.flag_sign):
                self._pc = addr
        
        elif opcode == self.op_jl:
            addr = self._fetch_dword()
            if self._get_flag(self.flag_sign):
                self._pc = addr
        
        elif opcode == self.op_je:
            addr = self._fetch_dword()
            if self._get_flag(self.flag_zero):
                self._pc = addr
        
        elif opcode == self.op_jne:
            addr = self._fetch_dword()
            if not self._get_flag(self.flag_zero):
                self._pc = addr
        
        elif opcode == self.op_call:
            addr = self._fetch_dword()
            self._push_dword(self._pc)
            self._call_stack.append(self._pc)
            self._pc = addr
        
        elif opcode == self.op_ret:
            if self._call_stack:
                self._call_stack.pop()
            self._pc = self._pop_dword()
        
        elif opcode == self.op_push:
            reg = self._fetch_byte() & 0x1f
            self._push_dword(self._registers[reg])
        
        elif opcode == self.op_pop:
            reg = self._fetch_byte() & 0x1f
            self._registers[reg] = self._pop_dword()
        
        elif opcode == self.op_pushf:
            self._push_dword(self._flags)
        
        elif opcode == self.op_popf:
            self._flags = self._pop_dword() & 0x1f
        
        elif opcode == self.op_load:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._registers[reg] = self._read_dword(addr)
        
        elif opcode == self.op_store:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._write_dword(addr, self._registers[reg])
        
        elif opcode == self.op_loadb:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._registers[reg] = self._read_byte(addr)
        
        elif opcode == self.op_storeb:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._write_byte(addr, self._registers[reg] & 0xff)
        
        elif opcode == self.op_loadw:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._registers[reg] = self._read_word(addr)
        
        elif opcode == self.op_storew:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._write_word(addr, self._registers[reg] & 0xffff)
        
        elif opcode == self.op_mov:
            dst = self._fetch_byte() & 0x1f
            src = self._fetch_byte() & 0x1f
            self._registers[dst] = self._registers[src]
        
        elif opcode == self.op_movi:
            reg = self._fetch_byte() & 0x1f
            imm = self._fetch_dword()
            self._registers[reg] = imm
        
        elif opcode == self.op_cmp:
            r1 = self._fetch_byte() & 0x1f
            r2 = self._fetch_byte() & 0x1f
            result = self._registers[r1] - self._registers[r2]
            self._update_flags(result, is_signed=True)
        
        elif opcode == self.op_test:
            r1 = self._fetch_byte() & 0x1f
            r2 = self._fetch_byte() & 0x1f
            result = self._registers[r1] & self._registers[r2]
            self._update_flags(result)
        
        elif opcode == self.op_inc:
            reg = self._fetch_byte() & 0x1f
            result = self._registers[reg] + 1
            self._registers[reg] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_dec:
            reg = self._fetch_byte() & 0x1f
            result = self._registers[reg] - 1
            self._registers[reg] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_neg:
            reg = self._fetch_byte() & 0x1f
            result = -self._registers[reg]
            self._registers[reg] = result & 0xffffffff
            self._update_flags(result)
        
        elif opcode == self.op_swap:
            r1 = self._fetch_byte() & 0x1f
            r2 = self._fetch_byte() & 0x1f
            self._registers[r1], self._registers[r2] = self._registers[r2], self._registers[r1]
        
        elif opcode == self.op_xchg:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            mem_val = self._read_dword(addr)
            self._write_dword(addr, self._registers[reg])
            self._registers[reg] = mem_val
        
        elif opcode == self.op_lea:
            reg = self._fetch_byte() & 0x1f
            addr = self._fetch_dword()
            self._registers[reg] = addr
        
        elif opcode == self.op_int:
            int_num = self._fetch_byte()
            self._trigger_interrupt(int_num)
        
        elif opcode == self.op_iret:
            self._pc = self._pop_dword()
            self._flags = self._pop_dword()
            self._interrupt_enabled = True
        
        elif opcode == self.op_cli:
            self._interrupt_enabled = False
        
        elif opcode == self.op_sti:
            self._interrupt_enabled = True
        
        elif opcode == self.op_in:
            reg = self._fetch_byte() & 0x1f
            port = self._fetch_word()
            self._registers[reg] = self._io_ports.get(port, 0)
        
        elif opcode == self.op_out:
            port = self._fetch_word()
            reg = self._fetch_byte() & 0x1f
            self._io_ports[port] = self._registers[reg]
        
        elif opcode == self.op_syscall:
            self._handle_syscall()
        
        elif opcode == self.op_sysret:
            self._pc = self._pop_dword()
        
        elif opcode == self.op_hash:
            reg = self._fetch_byte() & 0x1f
            addr = self._registers[reg]
            length = self._registers[(reg + 1) & 0x1f]
            self._registers[reg] = self._hash_block(addr, length)
        
        elif opcode == self.op_encrypt:
            data_reg = self._fetch_byte() & 0x1f
            key_reg = self._fetch_byte() & 0x1f
            len_reg = self._fetch_byte() & 0x1f
            data_addr = self._registers[data_reg]
            key_addr = self._registers[key_reg]
            length = self._registers[len_reg]
            
            for i in range(length):
                d = self._read_byte(data_addr + i)
                k = self._read_byte(key_addr + (i % 32))
                d ^= k
                d = self._sbox[d]
                d = self._rol8(d, 3)
                self._write_byte(data_addr + i, d)
        
        elif opcode == self.op_decrypt:
            data_reg = self._fetch_byte() & 0x1f
            key_reg = self._fetch_byte() & 0x1f
            len_reg = self._fetch_byte() & 0x1f
            data_addr = self._registers[data_reg]
            key_addr = self._registers[key_reg]
            length = self._registers[len_reg]
            
            for i in range(length):
                d = self._read_byte(data_addr + i)
                k = self._read_byte(key_addr + (i % 32))
                d = self._ror8(d, 3)
                d = self._inv_sbox[d]
                d ^= k
                self._write_byte(data_addr + i, d)
        
        elif opcode == self.op_rand:
            reg = self._fetch_byte() & 0x1f
            self._registers[reg] = self._get_random_dword()
        
        elif opcode == self.op_halt:
            self._running = False
            self._halted = True
        
        else:
            pass
    
    def run(self, start_address=0):
        self._pc = start_address
        self._running = True
        self._halted = False
        self._instruction_count = 0
        
        while self._running and self._instruction_count < self._max_instructions:
            self._handle_interrupts()
            
            opcode = self._fetch()
            self._execute(opcode)
            self._instruction_count += 1
            
            self._timer_counter += 1
            if self._timer_counter >= self._timer_interval:
                self._timer_counter = 0
                self._trigger_interrupt(self.int_timer)
        
        return self._registers[0]
    
    def step(self):
        if self._pc >= self._memory_size:
            self._running = False
            return False
        
        self._handle_interrupts()
        opcode = self._fetch()
        self._execute(opcode)
        return self._running
    
    def is_running(self):
        return self._running
    
    def is_halted(self):
        return self._halted
    
    def get_pc(self):
        return self._pc
    
    def get_sp(self):
        return self._sp
    
    def get_bp(self):
        return self._bp
    
    def get_flags(self):
        return self._flags
    
    def get_stdout(self):
        return bytes(self._stdout_buffer)
    
    def set_stdin(self, data):
        if isinstance(data, str):
            data = data.encode()
        self._stdin_buffer.extend(data)
    
    def set_interrupt_vector(self, int_num, handler_addr):
        if 0 <= int_num < 256:
            self._interrupt_vector[int_num] = handler_addr
    
    def add_device(self, port, device):
        self._devices[port] = device
    
    def create_encryption_program(self, data_offset, key_offset, output_offset, length):
        program = []
        
        program.extend([self.op_movi, 0])
        program.extend(data_offset.to_bytes(4, "little"))
        
        program.extend([self.op_movi, 1])
        program.extend(key_offset.to_bytes(4, "little"))
        
        program.extend([self.op_movi, 2])
        program.extend(length.to_bytes(4, "little"))
        
        program.extend([self.op_encrypt, 0, 1, 2])
        
        for i in range(length):
            program.extend([self.op_loadb, 3])
            program.extend((data_offset + i).to_bytes(4, "little"))
            program.extend([self.op_storeb, 3])
            program.extend((output_offset + i).to_bytes(4, "little"))
        
        program.append(self.op_halt)
        return bytes(program)
    
    def create_decryption_program(self, data_offset, key_offset, output_offset, length):
        program = []
        
        for i in range(length):
            program.extend([self.op_loadb, 0])
            program.extend((data_offset + i).to_bytes(4, "little"))
            program.extend([self.op_storeb, 0])
            program.extend((output_offset + i).to_bytes(4, "little"))
        
        program.extend([self.op_movi, 0])
        program.extend(output_offset.to_bytes(4, "little"))
        
        program.extend([self.op_movi, 1])
        program.extend(key_offset.to_bytes(4, "little"))
        
        program.extend([self.op_movi, 2])
        program.extend(length.to_bytes(4, "little"))
        
        program.extend([self.op_decrypt, 0, 1, 2])
        
        program.append(self.op_halt)
        return bytes(program)
    
    def assemble(self, source):
        instructions = {
            "nop": self.op_nop,
            "xor": self.op_xor,
            "add": self.op_add,
            "sub": self.op_sub,
            "mul": self.op_mul,
            "div": self.op_div,
            "mod": self.op_mod,
            "and": self.op_and,
            "or": self.op_or,
            "not": self.op_not,
            "rol": self.op_rol,
            "ror": self.op_ror,
            "shl": self.op_shl,
            "shr": self.op_shr,
            "sbox": self.op_sbox,
            "perm": self.op_perm,
            "mix": self.op_mix,
            "jmp": self.op_jmp,
            "jz": self.op_jz,
            "jnz": self.op_jnz,
            "jg": self.op_jg,
            "jl": self.op_jl,
            "je": self.op_je,
            "jne": self.op_jne,
            "call": self.op_call,
            "ret": self.op_ret,
            "push": self.op_push,
            "pop": self.op_pop,
            "load": self.op_load,
            "store": self.op_store,
            "mov": self.op_mov,
            "movi": self.op_movi,
            "cmp": self.op_cmp,
            "inc": self.op_inc,
            "dec": self.op_dec,
            "syscall": self.op_syscall,
            "halt": self.op_halt,
        }
        
        dword_imm_ops = {"movi", "jmp", "jz", "jnz", "jg", "jl", "je", "jne", "call", "load", "store"}
        
        program = []
        labels = {}
        lines = source.strip().split("\n")
        
        offset = 0
        for line in lines:
            line = line.strip()
            if not line or line.startswith(";"):
                continue
            if line.endswith(":"):
                labels[line[:-1]] = offset
            else:
                parts = line.split()
                mnem = parts[0].lower()
                if mnem in instructions:
                    offset += 1
                    for i, arg in enumerate(parts[1:]):
                        arg = arg.rstrip(",")
                        if arg.startswith("r"):
                            offset += 1
                        elif mnem in dword_imm_ops:
                            offset += 4
                        else:
                            offset += 1
        
        for line in lines:
            line = line.strip()
            if not line or line.startswith(";") or line.endswith(":"):
                continue
            
            parts = line.split()
            mnemonic = parts[0].lower()
            
            if mnemonic in instructions:
                program.append(instructions[mnemonic])
                
                for i, arg in enumerate(parts[1:]):
                    arg = arg.rstrip(",")
                    if arg.startswith("r"):
                        program.append(int(arg[1:]))
                    elif arg in labels:
                        addr = labels[arg]
                        program.extend(addr.to_bytes(4, "little"))
                    elif mnemonic in dword_imm_ops and not arg.startswith("r"):
                        if arg.startswith("0x"):
                            val = int(arg, 16)
                        else:
                            val = int(arg)
                        program.extend(val.to_bytes(4, "little"))
                    elif arg.startswith("0x"):
                        val = int(arg, 16)
                        program.append(val & 0xff)
                    else:
                        val = int(arg)
                        program.append(val & 0xff)
        
        return bytes(program)
