"""САМАЯ КОРРЕКТНАЯ ВЕРСИЯ С ПОДРОБНЫМИ ОШИБКАМИ ~70% корректной машинной кодогенерации по возможности: автоматизировать тестирование кодогенерации """ # === СТИЛЬ КОДА И ОГРАНИЧЕНИЯ ДЛЯ САМОКОМПИЛЯЦИИ === # # Этот код написан с учётом будущей возможности самокомпиляции # (например, трансляции на C или на собственный ассемблер). # Поэтому соблюдаются следующие правила: # # 1. НЕ ИСПОЛЬЗУЮТСЯ f-строки. # → Вместо них — конкатенация строк и str()/hex()/int(). # # 2. НЕ ИСПОЛЬЗУЕТСЯ оператор `with` для работы с файлами. # → Вместо него — явные open() и close(). # # 3. НЕ ИСПОЛЬЗУЕТСЯ ООП (классы, методы, self и т.п.). # → Весь код — глобальные функции и переменные. # # 4. ИЗБЕГАЕТСЯ сложный Python-специфичный синтаксис: # - нет list/dict comprehensions, # - нет лямбд, # - нет eval/exec (кроме временного парсинга, если необходимо), # - нет распаковки (*args, **kwargs), # - нет декораторов. # # 5. ДУБЛИРОВАНИЕ КОДА ДОПУСТИМО, если оно: # - упрощает чтение, # - изолирует логику, # - облегчает будущую ручную перезапись или трансляцию. # # 6. Все структуры данных — простые (dict, list, bytearray). # → Нет namedtuple, dataclass, enum и т.п. # # 7. Используются только базовые конструкции: # if/elif/else, while, for, функции, глобальные переменные. # # === РАСШИРЕНИЕ: ПРЯМАЯ ВСТАВКА ОПКОДОВ === # # Для гибкости и поддержки экспериментальных или ещё не описанных # инструкций добавлена директива: # # .байт 0xXX 0xYY ... # # Она позволяет вставлять произвольные байты машинного кода # непосредственно в секцию (.text или .data). # # ВАЖНО: # - Цифровые опкоды — НЕ замена мнемоникам. # - Русские мнемоники будут постепенно расширяться и покрывать # всё больше инструкций x86-64. # - .байт используется ТОЛЬКО как временный или специализированный # механизм для случаев, когда мнемоника ещё не реализована, # недокументирована или требует точного контроля, если мнемоника # описана - .байт не использовать. # - Данные (.строка, .константа) остаются в человекочитаемом виде. # # Цель: сделать код максимально "механически переносимым" # в другой язык или в собственный ассемблер без потери логики, # при этом сохраняя возможность полного контроля над генерируемым # машинным кодом. # # =================================================== import sys # Глобальная переменная для имени исходного файла (для сообщений об ошибках) source_filename = "" def pack_u16_le(val): return bytes([ (val >> 0) & 0xFF, (val >> 8) & 0xFF ]) def pack_u32_le(val): return bytes([ (val >> 0) & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF, (val >> 24) & 0xFF ]) def pack_u64_le(val): return bytes([ (val >> 0) & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF, (val >> 24) & 0xFF, (val >> 32) & 0xFF, (val >> 40) & 0xFF, (val >> 48) & 0xFF, (val >> 56) & 0xFF ]) def pack_i32_le(val): if val < 0: val = (1 << 32) + val return pack_u32_le(val) def safe_close(f): if f is not None: f.close() # === Глобальные данные === INSTRUCTIONS = { # Базовые "переместить": {"code": b"\x48\x89", "type": "reg_reg"}, # mov "переместить_имм": {"code": None, "type": "reg_imm"}, # mov "прибавить": {"code": b"\x48\x01", "type": "reg_reg"}, # add "вычесть": {"code": b"\x48\x29", "type": "reg_reg"}, # sub "вызвать": {"code": b"\xE8", "type": "call"}, # call "вернуться": {"code": b"\xC3", "type": "none"}, # ret "вызов_системы": {"code": b"\x0F\x05", "type": "none"}, # syscall "сравнить": {"code": b"\x48\x39", "type": "reg_reg"}, # cmp "переход": {"code": b"\xE9", "type": "jmp"}, # jmp "переход_если_равно": {"code": b"\x0F\x84", "type": "jcc"}, # je / jz "переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"}, # jne / jnz "втолкнуть": {"code": b"\x50", "type": "push"}, # push "вытолкнуть": {"code": b"\x58", "type": "pop"}, # pop "нет_операции": {"code": b"\x90", "type": "none"}, # nop "остановить": {"code": b"\xF4", "type": "none"}, # hlt # Арифметика "увеличить": {"code": b"\xFF", "subop": 0, "type": "incdec"}, # inc "уменьшить": {"code": b"\xFF", "subop": 1, "type": "incdec"}, # dec "отрицать": {"code": b"\xF7", "subop": 3, "type": "unary"}, # neg "умножить": {"code": b"\xF7", "subop": 4, "type": "muldiv"}, # mul "умножить_знаковое": {"code": b"\xF7", "subop": 5, "type": "muldiv"}, # imul "разделить": {"code": b"\xF7", "subop": 6, "type": "muldiv"}, # div "разделить_знаковое": {"code": b"\xF7", "subop": 7, "type": "muldiv"}, # idiv # Логика "и": {"code": b"\x48\x21", "type": "reg_reg"}, # and "или": {"code": b"\x48\x09", "type": "reg_reg"}, # or "исключающее_или": {"code": b"\x48\x31", "type": "reg_reg"}, # xor "инвертировать": {"code": b"\xF7", "subop": 2, "type": "unary"}, # not "проверить": {"code": b"\x48\x85", "type": "test"}, # test # Адресация "загрузить_адрес": {"code": b"\x48\x8D", "type": "lea"}, # lea # Сдвиги и вращения "сдвиг_влево": {"code": b"\x48\xC1\xE0", "type": "shift_imm"}, # shl "сдвиг_вправо": {"code": b"\x48\xC1\xE8", "type": "shift_imm"}, # shr "сдвиг_арифметический_влево": {"code": b"\x48\xC1\xE0", "type": "shift_imm"}, # sal (same as shl) "сдвиг_арифметический_вправо": {"code": b"\x48\xC1\xF8", "type": "shift_imm"}, # sar "вращать_влево": {"code": b"\x48\xC1\xC0", "type": "rotate_imm"}, # rol "вращать_вправо": {"code": b"\x48\xC1\xC8", "type": "rotate_imm"}, # ror # Флаги "установить_перенос": {"code": b"\xF9", "type": "none"}, # stc "сбросить_перенос": {"code": b"\xF8", "type": "none"}, # clc "установить_направление": {"code": b"\xFD", "type": "none"}, # std "сбросить_направление": {"code": b"\xFC", "type": "none"}, # cld # Прерывания "прервать": {"code": b"\xCD", "type": "int"}, # int # Расширение регистра "переместить_с_нулями": {"code": b"\x48\x0F\xB6", "type": "movzx"}, # movzx "переместить_со_знаком": {"code": b"\x48\x0F\xBE", "type": "movsx"}, # movsx # Обмен "обменять": {"code": b"\x48\x87", "type": "xchg"}, # xchg # Флаги (полные) "втолкнуть_флаги": {"code": b"\x9C", "type": "none"}, # pushf "вытолкнуть_флаги": {"code": b"\x9D", "type": "none"}, # popf # Условные переходы по CF "переход_если_перенос": {"code": b"\x0F\x82", "type": "jcc"}, # jc "переход_если_нет_переноса": {"code": b"\x0F\x83", "type": "jcc"}, # jnc # Цикл "цикл": {"code": b"\xE2", "type": "loop"}, # loop # Альтернативный системный вызов "войти_в_систему": {"code": b"\x0F\x34", "type": "none"}, # sysenter # Строковые операции "переместить_байт": {"code": b"\xA4", "type": "none"}, # movsb "переместить_слово": {"code": b"\xA5", "type": "none"}, # movsw / movsd / movsq (depends on prefix) "сравнить_байты": {"code": b"\xA6", "type": "none"}, # cmpsb "сканировать_байт": {"code": b"\xAE", "type": "none"}, # scasb # Специальные команды "идентифицировать_процессор": {"code": b"\x0F\xA2", "type": "none"}, # cpuid "прочитать_счетчик": {"code": b"\x0F\x31", "type": "none"}, # rdtsc "получить_управление": {"code": b"\x0F\x01\xD0", "type": "none"}, # vmcall (or other, context-dependent) # восьмибитные "загрузить_байт": {"code": None, "type": "mov_reg8_imm8"}, # mov "прибавить_байт": {"code": b"\x00", "type": "add_reg8_reg8"}, # add "сохранить_байт": {"code": b"\x88", "type": "mov_mem8_reg8"}, # mov # Условные переходы по знаку (SF = OF) "переход_если_больше_или_равно": {"code": b"\x0F\x8D", "type": "jcc"}, # jge "сохранить_в_адрес": {"code": b"\x88", "type": "store_reg8_to_reg64"}, # mov # Сравнение с непосредственным значением "сравнить_с": {"code": None, "type": "cmp_reg_imm"}, # cmp # Условные переходы по нулю/не нулю (альтернативные названия) "переход_если_ноль": {"code": b"\x0F\x84", "type": "jcc"}, # jz "переход_если_не_ноль": {"code": b"\x0F\x85", "type": "jcc"}, # jnz # Условные переходы по знаку "переход_если_меньше": {"code": b"\x0F\x8C", "type": "jcc"}, # jl "переход_если_больше": {"code": b"\x0F\x8F", "type": "jcc"}, # jg "переход_если_меньше_или_равно": {"code": b"\x0F\x8E", "type": "jcc"}, # jle "переход_если_больше_или_равно": {"code": b"\x0F\x8D", "type": "jcc"}, # jge # Безусловный короткий переход (опционально) "короткий_переход": {"code": b"\xEB", "type": "short_jmp"}, # jmp (short) # Чтение из памяти в регистр (64-бит) "загрузить": {"code": b"\x48\x8B", "type": "mov_reg_mem"}, # mov # Сохранение регистра в память (64-бит) "сохранить": {"code": b"\x48\x89", "type": "mov_mem_reg"}, # mov # 32-битные операции (часто нужны для совместимости и оптимизации) "переместить32": {"code": b"\x89", "type": "mov32_reg_reg"}, # mov "прибавить32": {"code": b"\x01", "type": "add32_reg_reg"}, # add "вычесть32": {"code": b"\x29", "type": "sub32_reg_reg"}, # sub "сравнить32": {"code": b"\x39", "type": "cmp32_reg_reg"}, # cmp # 8-битные операции (расширение) "вычесть_байт": {"code": b"\x28", "type": "sub_reg8_reg8"}, # sub "сравнить_байт": {"code": b"\x38", "type": "cmp_reg8_reg8"}, # cmp # Арифметика с непосредственным значением (64-бит, но через add/sub imm32 sign-extended) "прибавить_непосредственно": {"code": None, "type": "add_reg_imm"}, # add "вычесть_непосредственно": {"code": None, "type": "sub_reg_imm"}, # sub # Обмен с памятью "обменять_с_памятью": {"code": b"\x48\x87", "type": "xchg_mem_reg"}, # xchg # Очистка регистра (xor reg, reg — идиома) "очистить": {"code": b"\x48\x31", "type": "xor_self"}, # xor # Стековые операции с памятью "втолкнуть_из_памяти": {"code": b"\xFF", "subop": 6, "type": "push_mem"}, # push "вытолкнуть_в_память": {"code": b"\x8F", "subop": 0, "type": "pop_mem"}, # pop # Отладка "отладка": {"code": b"\xCC", "type": "none"}, # int3 # Ввод-вывод (для простых демонстраций) "ввод_байта": {"code": b"\xE4", "type": "in_imm"}, # in "вывод_байта": {"code": b"\xE6", "type": "out_imm"}, # out # === Короткие условные переходы (8-битное смещение) === "короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"}, "короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"}, "короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"}, "короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"}, "короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"}, "короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"}, "короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"}, "короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"}, "короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"}, # синоним "короткий_переход_если_не_ноль" : {"code": b"\x75", "type": "short_jcc"}, # синоним } REGISTERS = { # 64-битные регистры "раикс": 0, # RAX "рсикс": 1, # RCX "рдикс": 2, # RDX "рбикс": 3, # RBX "рсипи": 4, # RSP "рбипи": 5, # RBP "рсиай": 6, # RSI "рдиай": 7, # RDI "р8": 8, # R8 "р9": 9, # R9 "р10": 10, # R10 "р11": 11, # R11 "р12": 12, # R12 "р13": 13, # R13 "р14": 14, # R14 "р15": 15, # R15 # 32-битные регистры "еаикс": 0, # EAX "есикс": 1, # ECX "едикс": 2, # EDX "ебикс": 3, # EBX "есипи": 4, # ESP "ебипи": 5, # EBP "есиай": 6, # ESI "едиай": 7, # EDI "р8д": 8, # R8D "р9д": 9, # R9D "р10д": 10, # R10D "р11д": 11, # R11D "р12д": 12, # R12D "р13д": 13, # R13D "р14д": 14, # R14D "р15д": 15, # R15D # 16-битные регистры "аикс": 0, # AX "сикс": 1, # CX "дикс": 2, # DX "бикс": 3, # BX "эсп": 4, # SP (Stack Pointer) "бипи": 5, # BP (Base Pointer) "эс": 6, # SI (Source Index) "ди": 7, # DI (Destination Index) "р8в": 8, # R8W "р9в": 9, # R9W "р10в": 10, # R10W "р11в": 11, # R11W "р12в": 12, # R12W "р13в": 13, # R13W "р14в": 14, # R14W "р15в": 15, # R15W # 8-битные регистры (младшие байты) "ал": 0, # AL "кл": 1, # CL "дл": 2, # DL "бл": 3, # BL "спл": 4, # SPL "бпл": 5, # BPL "сил": 6, # SIL "дил": 7, # DIL "р8б": 8, # R8B "р9б": 9, # R9B "р10б": 10, # R10B "р11б": 11, # R11B "р12б": 12, # R12B "р13б": 13, # R13B "р14б": 14, # R14B "р15б": 15, # R15B # 8-битные регистры (старшие байты - только для первых 4 регистров) "аш": 0, # AH "чш": 1, # CH "дш": 2, # DH "бш": 3, # BH } # Глобальное состояние ассемблера labels = {} label_sections = {} symbols = {} sections = {".text": bytearray(), ".data": bytearray()} current_section = ".text" entry_point = "_start" position = {".text": 0, ".data": 0} pass_num = 0 code_start = 0x400000 # ELF layout text_size = 0 data_size = 0 vaddr_text = 0 vaddr_data = 0 offset_text = 0 offset_data = 0 memsz_text = 0 memsz_data = 0 # Логи log_file = None tokens_log = None # === Вспомогательные функции === def log(message): log_file.write(message + "\n") log_file.flush() def log_tokens(line_num, tokens): tokens_log.write("Line " + str(line_num) + ": " + str(tokens) + "\n") tokens_log.flush() def close_log(): safe_close(log_file) safe_close(tokens_log) def log_labels(): log("=== Метки и их адреса ===") for label, pos in labels.items(): section = label_sections.get(label, "неизвестно") base_addr = vaddr_text if section == ".text" else vaddr_data abs_addr = base_addr + pos value = symbols.get(label, "N/A") log("Метка '" + label + "': секция = " + section + ", позиция = " + str(pos) + ", абсолютный адрес = " + hex(abs_addr) + ", значение = " + str(value)) def align_up(x, align): return (x + align - 1) & ~(align - 1) def get_reg_info(reg_name): if reg_name in ["аш", "чш", "дш", "бш"]: return {"size": 8, "index": REGISTERS[reg_name], "high8": True} elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]: return {"size": 8, "index": REGISTERS[reg_name], "high8": False} elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]: return {"size": 16, "index": REGISTERS[reg_name]} elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]: return {"size": 32, "index": REGISTERS[reg_name]} else: return {"size": 64, "index": REGISTERS[reg_name]} # === Функции формирования ошибок === def make_error_msg(line_num, line_text, detail): return ( "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n" + " " + line_text + "\n" + detail ) def error_invalid_operand_count(mnemonic, expected, got): return ( "Инструкция '" + mnemonic + "':\n" + " ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got) ) def error_unknown_mnemonic(mnemonic): return "Неизвестная инструкция: '" + mnemonic + "'" def error_invalid_register(op_name, reg_name, mnemonic): return ( "Инструкция '" + mnemonic + "':\n" + " операнд '" + op_name + "' = '" + reg_name + "' не является допустимым регистром.\n" + " Допустимые регистры: " + ", ".join(list(REGISTERS.keys())) ) def error_expected_register(op_name, value, mnemonic): return ( "Инструкция '" + mnemonic + "':\n" + " операнд '" + op_name + "' = '" + value + "' должен быть регистром, но это число или метка." ) def error_invalid_number_format(s): return "Недопустимый формат числа: '" + s + "'" def error_byte_out_of_range(val_str, val): return "Байт должен быть в диапазоне 0–255: '" + val_str + "' = " + str(val) def error_unknown_directive(word): return "Неизвестная директива: '" + word + "'" def error_missing_label(label): return "Метка не найдена: '" + label + "'" def error_unexpected_token_in_operands(tok): return "Недопустимый токен в операндах: " + str(tok) def error_unexpected_string_in_byte(): return ".байт требует числовые значения, не строки" def error_unterminated_string(): return "Незакрытая кавычка в строке" def error_invalid_address_operand(addr_op): return "Первый операнд должен быть [регистр], получено: " + addr_op # === Лексер === def tokenize_line(line): semi = line.find(';') if semi != -1: line = line[:semi] line = line.rstrip() if not line: return [] tokens = [] i = 0 n = len(line) while i < n: ch = line[i] if ch.isspace(): i += 1 continue if ch == '"': i += 1 s = '' while i < n and line[i] != '"': if line[i] == '\\' and i + 1 < n: i += 1 esc = line[i] if esc == 'n': s += '\n' elif esc == 't': s += '\t' elif esc == '"': s += '"' elif esc == '\\': s += '\\' else: s += '\\' + esc i += 1 else: s += line[i] i += 1 if i >= n: raise ValueError(error_unterminated_string()) i += 1 tokens.append(('string', s)) continue if ch == ',': tokens.append(('comma', ',')) i += 1 continue if ch == ':': tokens.append(('colon', ':')) i += 1 continue j = i while j < n and not (line[j].isspace() or line[j] in ',:;'): j += 1 word = line[i:j] if word: tokens.append(('word', word)) i = j return tokens # === Парсинг === def parse_operand(operand): if operand.isdigit(): return int(operand) if operand.startswith("0x"): return int(operand, 16) if operand in labels: section = label_sections[operand] base_addr = vaddr_text if section == ".text" else vaddr_data return labels[operand] + base_addr if operand in symbols: return symbols[operand] raise ValueError("Неизвестный операнд: " + operand) def parse_number_token(token): if token[0] != 'word': raise ValueError("Ожидалось число, получено: " + str(token)) s = token[1] if s.isdigit(): return int(s) if s.startswith("0x"): return int(s, 16) raise ValueError(error_invalid_number_format(s)) def encode_instruction(mnemonic, operands, line_num, line_text): instr = INSTRUCTIONS[mnemonic] code = bytearray() itype = instr["type"] if itype == "none": code.extend(instr["code"]) elif itype == "reg_reg": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) dst = dst_info["index"] src = src_info["index"] dst_size = dst_info["size"] src_size = src_info["size"] if dst_size != src_size: raise ValueError("Размеры операндов не совпадают в инструкции '" + mnemonic + "'") # Определяем, нужен ли REX.W use_rex_w = (dst_size == 64) use_66_prefix = (dst_size == 16) rex = 0x40 if use_rex_w: rex |= 0x08 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 # Старшие байты запрещают REX if dst_info.get("high8") or src_info.get("high8"): if rex != 0x40: raise ValueError("Нельзя использовать старшие байты (аш/чш/дш/бш) с расширенными регистрами (р8–р15)") rex = 0 op_base = instr["code"] if op_base[0] == 0x48 and len(op_base) == 2: # Это 64-битная инструкция по умолчанию, но мы пересчитываем actual_op = op_base[1] else: actual_op = op_base[0] if use_66_prefix: code.append(0x66) if rex != 0x40 or use_rex_w: code.append(rex) code.append(actual_op) modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype == "reg_imm": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] imm = parse_operand(operands[1]) if size == 64: if 0 <= reg <= 7: code.extend(b'\x48') code.append(0xB8 + reg) code.extend(pack_u64_le(imm)) elif 8 <= reg <= 15: code.extend(b'\x49') code.append(0xB8 + (reg - 8)) code.extend(pack_u64_le(imm)) else: raise ValueError("Недопустимый регистр в '" + mnemonic + "'") elif size == 32: if -0x80000000 <= imm <= 0x7FFFFFFF: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xC7) modrm = 0xC0 | (reg & 7) code.append(modrm) code.extend(pack_u32_le(imm & 0xFFFFFFFF)) else: raise ValueError("32-битное непосредственное значение вне диапазона: " + str(imm)) elif size == 16: if 0 <= imm <= 0xFFFF: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xC7) modrm = 0xC0 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) code.append((imm >> 8) & 0xFF) else: raise ValueError("16-битное непосредственное значение вне диапазона: " + str(imm)) elif size == 8: if not (0 <= imm <= 255): raise ValueError("8-битное значение вне диапазона 0–255: " + str(imm)) if reg_info.get("high8"): if reg < 4: code.append(0xB0 + reg + 4) # AH=4, CH=5, DH=6, BH=7 → B4..B7 code.append(imm & 0xFF) else: raise ValueError("Старшие байты доступны только для первых 4 регистров") else: if reg < 4: code.append(0xB0 + reg) code.append(imm & 0xFF) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xB0 + (reg & 7)) code.append(imm & 0xFF) else: raise ValueError("Неподдерживаемый размер регистра в '" + mnemonic + "'") elif itype == "call" or itype == "jmp": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) code.extend(instr["code"]) target = parse_operand(operands[0]) current_addr = vaddr_text + position[".text"] offset = target - (current_addr + 5) if offset < -0x80000000 or offset > 0x7FFFFFFF: raise ValueError( "Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset) + " (должно быть в [-2^31, 2^31-1])" ) code.extend(pack_i32_le(offset)) elif itype == "jcc": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) code.extend(instr["code"]) target = parse_operand(operands[0]) current_addr = vaddr_text + position[".text"] offset = target - (current_addr + 6) if offset < -0x80000000 or offset > 0x7FFFFFFF: raise ValueError("Цель условного перехода слишком далеко: " + str(offset)) code.extend(pack_i32_le(offset)) elif itype == "push" or itype == "pop": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] if size not in (64, 32): raise ValueError("Инструкция '" + mnemonic + "' поддерживает только 32/64-битные регистры") base = 0x50 if itype == "push" else 0x58 if 0 <= reg <= 7: code.append(base + reg) elif 8 <= reg <= 15: code.extend(b'\x41') code.append(base + (reg - 8)) else: raise ValueError("Недопустимый регистр для " + itype) elif itype == "incdec": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] subop = instr["subop"] if size == 64: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xFF) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 32: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xFF) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 16: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xFF) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 8: # Для 8-битных регистров — всегда регистровый режим (Mod=11) if reg_info.get("high8"): if reg < 4: code.append(0xFE) modrm = 0xC0 | (subop << 3) | reg code.append(modrm) else: raise ValueError("Старшие байты доступны только для первых 4 регистров") else: if reg < 4: code.append(0xFE) modrm = 0xC0 | (subop << 3) | reg code.append(modrm) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xFE) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) else: raise ValueError("Неподдерживаемый размер регистра") elif itype in ("unary", "muldiv"): if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] subop = instr["subop"] if size == 64: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xF7) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 32: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xF7) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 16: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xF7) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) elif size == 8: if reg_info.get("high8"): if reg < 4: code.append(0xF6) modrm = 0xC0 | (subop << 3) | reg code.append(modrm) else: raise ValueError("Старшие байты только для первых 4 регистров") else: if reg < 4: code.append(0xF6) modrm = 0xC0 | (subop << 3) | reg code.append(modrm) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xF6) modrm = 0xC0 | (subop << 3) | (reg & 7) code.append(modrm) else: raise ValueError("Неподдерживаемый размер регистра") elif itype == "test": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) dst = dst_info["index"] src = src_info["index"] size = dst_info["size"] if size != src_info["size"]: raise ValueError("Размеры операндов не совпадают в '" + mnemonic + "'") if size == 64: rex = 0x48 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 code.append(rex) code.append(0x85) elif size == 32: rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x85) elif size == 16: code.append(0x66) rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x85) elif size == 8: if dst_info.get("high8") or src_info.get("high8"): if rex != 0x40: raise ValueError("Старшие байты несовместимы с REX") code.append(0x84) else: rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x84) else: raise ValueError("Неподдерживаемый размер регистра") modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype == "lea": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) label = operands[1] if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if label not in labels: raise ValueError(error_missing_label(label)) dst = dst_info["index"] if dst_info["size"] != 64: raise ValueError("LEA поддерживает только 64-битные регистры назначения") target_addr = parse_operand(label) current_addr = vaddr_text + position[".text"] rip_after = current_addr + 7 disp = target_addr - rip_after if disp < -0x80000000 or disp > 0x7FFFFFFF: raise ValueError("Метка слишком далеко для RIP-relative адресации: " + label) code.extend(b"\x48\x8D") modrm = 0x05 | ((dst & 7) << 3) if dst >= 8: code[0] |= 0x01 code.append(modrm) code.extend(pack_i32_le(disp)) elif itype in ("shift_imm", "rotate_imm"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) imm = parse_operand(operands[1]) if not (0 <= imm <= 255): raise ValueError("Сдвиг/вращение должно быть в диапазоне 0–255, получено: " + str(imm)) reg = reg_info["index"] size = reg_info["size"] base_op = instr["code"][-1] # последний байт — базовый опкод без регистра if size == 64: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xC1) modrm = 0xC0 | (base_op << 3) | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif size == 32: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xC1) modrm = 0xC0 | (base_op << 3) | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif size == 16: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0xC1) modrm = 0xC0 | (base_op << 3) | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif size == 8: if reg_info.get("high8"): if reg < 4: code.append(0xC0) modrm = 0xC0 | (base_op << 3) | reg code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Старшие байты только для первых 4 регистров") else: if reg < 4: code.append(0xC0) modrm = 0xC0 | (base_op << 3) | reg code.append(modrm) code.append(imm & 0xFF) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0xC0) modrm = 0xC0 | (base_op << 3) | (reg & 7) code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Неподдерживаемый размер регистра") elif itype == "int": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) imm = parse_operand(operands[0]) if not (0 <= imm <= 255): raise ValueError("Номер прерывания должен быть 0–255, получено: " + str(imm)) code.extend(instr["code"]) code.append(imm & 0xFF) elif itype in ("movzx", "movsx"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) dst = dst_info["index"] src = src_info["index"] if dst_info["size"] != 64: raise ValueError("Назначение в '" + mnemonic + "' должно быть 64-битным") if src_info["size"] not in (8, 16): raise ValueError("Источник в '" + mnemonic + "' должен быть 8- или 16-битным") rex = 0x48 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 code.append(rex) code.extend(instr["code"][1:]) # пропускаем 0x48 из оригинального опкода modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype == "xchg": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) dst = dst_info["index"] src = src_info["index"] size = dst_info["size"] if size != src_info["size"]: raise ValueError("Размеры операндов не совпадают в '" + mnemonic + "'") if size == 64: rex = 0x48 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 code.append(rex) code.append(0x87) elif size == 32: rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x87) elif size == 16: code.append(0x66) rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x87) elif size == 8: if dst_info.get("high8") or src_info.get("high8"): code.append(0x86) else: rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x86) else: raise ValueError("Неподдерживаемый размер регистра") modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype == "loop": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) code.extend(instr["code"]) target = parse_operand(operands[0]) current_addr = vaddr_text + position[".text"] offset = (target - (current_addr + 2)) & 0xFF if not (0 <= offset <= 255): raise ValueError("Цель цикла слишком далеко") code.append(offset & 0xFF) elif itype == "mov_reg8_imm8": # Обрабатывается в reg_imm, но оставлено для совместимости raise ValueError("Используйте 'переместить_имм' вместо 'загрузить_байт'") elif itype in ("add_reg8_reg8", "sub_reg8_reg8", "cmp_reg8_reg8"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) if dst_info["size"] != 8 or src_info["size"] != 8: raise ValueError("Операнды должны быть 8-битными в '" + mnemonic + "'") dst = dst_info["index"] src = src_info["index"] op_map = { "add_reg8_reg8": 0x00, "sub_reg8_reg8": 0x28, "cmp_reg8_reg8": 0x38, } op = op_map[itype] if dst_info.get("high8") or src_info.get("high8"): if dst >= 4 or src >= 4: raise ValueError("Старшие байты доступны только для первых 4 регистров") code.append(op) modrm = 0xC0 | (src << 3) | dst code.append(modrm) else: rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(op) modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype in ("mov_mem8_reg8", "store_reg8_to_reg64"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) addr_op = operands[0] src_info = get_reg_info(operands[1]) if not (addr_op.startswith('[') and addr_op.endswith(']')): raise ValueError(error_invalid_address_operand(addr_op)) base_name = addr_op[1:-1] base_info = get_reg_info(base_name) if base_info is None: raise ValueError(error_invalid_register("адресного регистра", base_name, mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) if base_info["size"] != 64: raise ValueError("Адресный регистр должен быть 64-битным") if src_info["size"] != 8: raise ValueError("Источник должен быть 8-битным") base = base_info["index"] src = src_info["index"] if src_info.get("high8"): if src >= 4: raise ValueError("Старшие байты только для первых 4 регистров") code.append(0x88) modrm = 0x00 | (src << 3) | base code.append(modrm) else: rex = 0x40 if src >= 8: rex |= 0x04 if base >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x88) modrm = 0x00 | ((src & 7) << 3) | (base & 7) code.append(modrm) elif itype == "cmp_reg_imm": if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] imm = parse_operand(operands[1]) if size == 64: if -128 <= imm <= 127: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x83) modrm = 0xF8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif -0x80000000 <= imm <= 0x7FFFFFFF: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x81) modrm = 0xF8 | (reg & 7) code.append(modrm) code.extend(pack_u32_le(imm & 0xFFFFFFFF)) else: raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm)) elif size == 32: if -128 <= imm <= 127: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x83) modrm = 0xF8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif -0x80000000 <= imm <= 0x7FFFFFFF: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x81) modrm = 0xF8 | (reg & 7) code.append(modrm) code.extend(pack_u32_le(imm & 0xFFFFFFFF)) else: raise ValueError("32-битное непосредственное значение вне диапазона") elif size == 16: if -128 <= imm <= 127: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x83) modrm = 0xF8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif 0 <= imm <= 0xFFFF: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x81) modrm = 0xF8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) code.append((imm >> 8) & 0xFF) else: raise ValueError("16-битное непосредственное значение вне диапазона") elif size == 8: if not (0 <= imm <= 255): raise ValueError("8-битное значение вне диапазона 0–255: " + str(imm)) if reg_info.get("high8"): if reg < 4: code.append(0x80) modrm = 0xF8 | reg code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Старшие байты только для первых 4 регистров") else: if reg < 4: code.append(0x80) modrm = 0xF8 | reg code.append(modrm) code.append(imm & 0xFF) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x80) modrm = 0xF8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Неподдерживаемый размер регистра") elif itype in ("mov_reg_mem", "mov_mem_reg", "xchg_mem_reg"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) if itype in ("mov_reg_mem", "xchg_mem_reg"): dst_info = get_reg_info(operands[0]) src_op = operands[1] is_load = True else: # mov_mem_reg dst_op = operands[0] src_info = get_reg_info(operands[1]) is_load = False if is_load: if not (src_op.startswith('[') and src_op.endswith(']')): raise ValueError(error_invalid_address_operand(src_op)) base_name = src_op[1:-1] base_info = get_reg_info(base_name) if base_info is None: raise ValueError(error_invalid_register("базового адреса", base_name, mnemonic)) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if base_info["size"] != 64: raise ValueError("Адресный регистр должен быть 64-битным") dst = dst_info["index"] base = base_info["index"] size = dst_info["size"] else: if not (dst_op.startswith('[') and dst_op.endswith(']')): raise ValueError(error_invalid_address_operand(dst_op)) base_name = dst_op[1:-1] base_info = get_reg_info(base_name) if base_info is None: raise ValueError(error_invalid_register("адресного регистра", base_name, mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) if base_info["size"] != 64: raise ValueError("Адресный регистр должен быть 64-битным") src = src_info["index"] base = base_info["index"] size = src_info["size"] op_map = { "mov_reg_mem": (0x8B, True), "mov_mem_reg": (0x89, False), "xchg_mem_reg": (0x87, False), } op, is_load_op = op_map[itype] if size == 64: rex = 0x48 if base >= 8: rex |= 0x01 if (is_load and dst >= 8) or (not is_load and src >= 8): rex |= 0x04 code.append(rex) code.append(op) elif size == 32: rex = 0x40 if base >= 8: rex |= 0x01 if (is_load and dst >= 8) or (not is_load and src >= 8): rex |= 0x04 if rex != 0x40: code.append(rex) code.append(op) elif size == 16: code.append(0x66) rex = 0x40 if base >= 8: rex |= 0x01 if (is_load and dst >= 8) or (not is_load and src >= 8): rex |= 0x04 if rex != 0x40: code.append(rex) code.append(op) elif size == 8: if is_load: raise ValueError("Загрузка 8-битного регистра из памяти не поддерживается напрямую в этом ассемблере") else: if src_info.get("high8"): if src >= 4: raise ValueError("Старшие байты только для первых 4 регистров") code.append(0x88) modrm = 0x00 | (src << 3) | base code.append(modrm) return bytes(code) else: rex = 0x40 if src >= 8: rex |= 0x04 if base >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x88) modrm = 0x00 | ((src & 7) << 3) | (base & 7) code.append(modrm) return bytes(code) else: raise ValueError("Неподдерживаемый размер регистра") modrm = 0x00 | (((dst if is_load else src) & 7) << 3) | (base & 7) code.append(modrm) elif itype in ("mov32_reg_reg", "add32_reg_reg", "sub32_reg_reg", "cmp32_reg_reg"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) dst_info = get_reg_info(operands[0]) src_info = get_reg_info(operands[1]) if dst_info is None: raise ValueError(error_invalid_register("назначения", operands[0], mnemonic)) if src_info is None: raise ValueError(error_invalid_register("источника", operands[1], mnemonic)) if dst_info["size"] != 32 or src_info["size"] != 32: raise ValueError("Операнды должны быть 32-битными в '" + mnemonic + "'") dst = dst_info["index"] src = src_info["index"] op_map = { "mov32_reg_reg": 0x89, "add32_reg_reg": 0x01, "sub32_reg_reg": 0x29, "cmp32_reg_reg": 0x39, } op = op_map[itype] rex = 0x40 if src >= 8: rex |= 0x04 if dst >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(op) modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) code.append(modrm) elif itype in ("add_reg_imm", "sub_reg_imm"): if len(operands) != 2: raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] imm = parse_operand(operands[1]) is_add = (itype == "add_reg_imm") if size == 64: if -128 <= imm <= 127: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x83) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif -0x80000000 <= imm <= 0x7FFFFFFF: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x81) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.extend(pack_u32_le(imm & 0xFFFFFFFF)) else: raise ValueError(("Непосредственное значение слишком велико для " + ("сложения" if is_add else "вычитания")) + ": " + str(imm)) elif size == 32: if -128 <= imm <= 127: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x83) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif -0x80000000 <= imm <= 0x7FFFFFFF: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x81) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.extend(pack_u32_le(imm & 0xFFFFFFFF)) else: raise ValueError("32-битное непосредственное значение вне диапазона") elif size == 16: if -128 <= imm <= 127: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x83) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) elif 0 <= imm <= 0xFFFF: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x81) modrm = 0xC0 | (reg & 7) if is_add else 0xE8 | (reg & 7) code.append(modrm) code.append(imm & 0xFF) code.append((imm >> 8) & 0xFF) else: raise ValueError("16-битное непосредственное значение вне диапазона") elif size == 8: if not (0 <= imm <= 255): raise ValueError("8-битное значение вне диапазона 0–255: " + str(imm)) if reg_info.get("high8"): if reg < 4: code.append(0x80) modrm = (0xC0 if is_add else 0xE8) | reg code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Старшие байты только для первых 4 регистров") else: if reg < 4: code.append(0x80) modrm = (0xC0 if is_add else 0xE8) | reg code.append(modrm) code.append(imm & 0xFF) else: rex = 0x40 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x80) modrm = (0xC0 if is_add else 0xE8) | (reg & 7) code.append(modrm) code.append(imm & 0xFF) else: raise ValueError("Неподдерживаемый размер регистра") elif itype == "xor_self": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) reg_info = get_reg_info(operands[0]) if reg_info is None: raise ValueError(error_invalid_register("регистра", operands[0], mnemonic)) reg = reg_info["index"] size = reg_info["size"] if size == 64: rex = 0x48 if reg >= 8: rex |= 0x01 code.append(rex) code.append(0x31) elif size == 32: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x31) elif size == 16: code.append(0x66) rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x31) elif size == 8: if reg_info.get("high8"): code.append(0x30) else: rex = 0x40 if reg >= 8: rex |= 0x01 if rex != 0x40: code.append(rex) code.append(0x30) else: raise ValueError("Неподдерживаемый размер регистра") modrm = 0xC0 | ((reg & 7) << 3) | (reg & 7) code.append(modrm) elif itype in ("push_mem", "pop_mem"): if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) addr_op = operands[0] if not (addr_op.startswith('[') and addr_op.endswith(']')): raise ValueError(error_invalid_address_operand(addr_op)) base_name = addr_op[1:-1] base_info = get_reg_info(base_name) if base_info is None: raise ValueError(error_invalid_register("адресного регистра", base_name, mnemonic)) if base_info["size"] != 64: raise ValueError("Адресный регистр должен быть 64-битным") base = base_info["index"] # REX префикс - только если нужен B бит для расширенных регистров rex = 0x40 if base >= 8: rex |= 0x01 # НЕ устанавливаем REX.W бит (0x08) - он не нужен для push/pop с памятью op = 0xFF if itype == "push_mem" else 0x8F subop = 6 if itype == "push_mem" else 0 # Специальные случаи: if base == 4 or base == 12: # rsp или r12 - требуют SIB if rex != 0x40: code.append(rex) code.append(op) code.append(0x24) # ModR/M: mod=00, reg=subop, r/m=100 (SIB required) code.append(0x24) # SIB: scale=00, index=100, base=100 ([rsp]/[r12]) elif base == 5 or base == 13: # rbp или r13 - требуют 8-битного нулевого смещения if rex != 0x40: code.append(rex) code.append(op) code.append(0x40 | (subop << 3) | (base & 7)) # ModR/M: mod=01, reg=subop, r/m=base code.append(0x00) # disp8 = 0 else: # Обычные регистры if rex != 0x40: code.append(rex) code.append(op) modrm = (subop << 3) | (base & 7) code.append(modrm) elif itype in ("in_imm", "out_imm"): if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) imm = parse_operand(operands[0]) if not (0 <= imm <= 255): raise ValueError(("Порт " + ("ввода" if itype == "in_imm" else "вывода") + " должен быть 0–255: ") + str(imm)) code.append(instr["code"][0]) code.append(imm & 0xFF) elif itype == "short_jmp": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) target = parse_operand(operands[0]) current_addr = vaddr_text + position[".text"] offset = target - (current_addr + 2) if not (-128 <= offset <= 127): raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset)) code.append(0xEB) code.append(offset & 0xFF) elif itype == "short_jcc": if len(operands) != 1: raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands))) code.extend(instr["code"]) target = parse_operand(operands[0]) current_addr = vaddr_text + position[".text"] offset = target - (current_addr + 2) # 1 байт опкода + 1 байт disp8 if not (-128 <= offset <= 127): raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset)) code.append(offset & 0xFF) else: raise ValueError("Неизвестный тип инструкции: " + itype) return bytes(code) def parse_instruction_or_directive(tokens, line_num, line_text): global current_section, entry_point first = tokens[0] if first[0] != 'word': raise ValueError("Ожидалось слово, получено: " + str(first)) word = first[1] if word.startswith('.'): if word == '.текст': current_section = ".text" elif word == '.данные': current_section = ".data" elif word == '.глобал': if len(tokens) < 2 or tokens[1][0] != 'word': raise ValueError(".глобал требует имя метки") entry_point = tokens[1][1] elif word == '.константа': if len(tokens) < 3: raise ValueError(".константа требует имя и значение") name = tokens[1][1] value = parse_number_token(tokens[2]) symbols[name] = value elif word == '.строка_нуль' or word == '.строка': if len(tokens) < 2 or tokens[1][0] != 'string': raise ValueError(word + " требует строку в кавычках") if current_section != ".data": raise ValueError("Строки разрешены только в секции .data") s = tokens[1][1] bstring = s.encode('utf-8') add_null = (word == '.строка_нуль') size = len(bstring) + (1 if add_null else 0) if pass_num == 1: position[".data"] += size elif pass_num == 2: sections[".data"] += bstring if add_null: sections[".data"] += b'\x00' position[".data"] += size elif word == '.байт': if len(tokens) < 2: raise ValueError(".байт требует хотя бы один байт") for i in range(1, len(tokens)): tok = tokens[i] if tok[0] != 'word': raise ValueError(error_unexpected_string_in_byte()) s = tok[1] if s.startswith("0x"): try: val = int(s, 16) except: raise ValueError(error_invalid_number_format(s)) elif s.isdigit(): try: val = int(s) except: raise ValueError(error_invalid_number_format(s)) else: raise ValueError(error_invalid_number_format(s)) if val < 0 or val > 255: raise ValueError(error_byte_out_of_range(s, val)) if pass_num == 2: old_pos = position[current_section] sections[current_section].append(val) position[current_section] += 1 new_pos = position[current_section] log("[ПРОХОД 2] Директива .байт: 0x{:02x}".format(val)) log(" Адрес: {} (0x{:x})".format(old_pos, old_pos)) else: position[current_section] += 1 return else: raise ValueError(error_unknown_directive(word)) return mnemonic = word if mnemonic not in INSTRUCTIONS: raise ValueError(error_unknown_mnemonic(mnemonic)) operands = [] for tok in tokens[1:]: if tok[0] == 'word': operands.append(tok[1]) elif tok[0] == 'comma': continue else: raise ValueError(error_unexpected_token_in_operands(tok)) if pass_num == 2: code = encode_instruction(mnemonic, operands, line_num, line_text) addr_before = position[current_section] sections[current_section] += code position[current_section] += len(code) addr_after = position[current_section] # Логируем детали генерации hex_bytes = ''.join('\\x{:02x}'.format(b) for b in code) log("[ПРОХОД 2] Инструкция: '" + mnemonic + "'") log(" Адрес до: " + str(addr_before) + " (0x" + hex(addr_before)[2:] + ")") log(" Байты: " + hex_bytes) log(" Длина: " + str(len(code))) log(" Адрес после: " + str(addr_after) + " (0x" + hex(addr_after)[2:] + ")") else: instr_info = INSTRUCTIONS[mnemonic] itype = instr_info["type"] size_map = { "reg_imm": 10, "reg_reg": 3, "test": 3, "call": 5, "jmp": 5, "jcc": 6, "push": 1, "pop": 1, "none": lambda: len(instr_info["code"]), "incdec": 3, "unary": 3, "muldiv": 3, "shift_imm": 4, "rotate_imm": 4, "int": 2, "lea": 7, "movzx": 4, "movsx": 4, "xchg": 3, "loop": 2, "mov_reg8_imm8": 3, "add_reg8_reg8": 3, "mov_mem8_reg8": 3, "store_reg8_to_reg64": 3, "cmp_reg_imm": 7, # REX + 0x81 + ModR/M + imm32 "mov_reg_mem": 3, # REX + 0x8B + ModR/M "mov_mem_reg": 3, # REX + 0x89 + ModR/M "xchg_mem_reg": 3, # REX + 0x87 + ModR/M "mov32_reg_reg": 3, # REX + 0x89 + ModR/M "add32_reg_reg": 3, # REX + 0x01 + ModR/M "sub32_reg_reg": 3, # REX + 0x29 + ModR/M "cmp32_reg_reg": 3, # REX + 0x39 + ModR/M "sub_reg8_reg8": 3, # REX + 0x28 + ModR/M "cmp_reg8_reg8": 3, # REX + 0x38 + ModR/M "add_reg_imm": 7, # REX + 0x81 + ModR/M + imm32 "sub_reg_imm": 7, # REX + 0x81 + ModR/M + imm32 "xor_self": 3, # REX + 0x31 + ModR/M "push_mem": 4, # REX + 0xFF + ModR/M (/6) "pop_mem": 4, # REX + 0x8F + ModR/M (/0) "in_imm": 2, # 0xE4 + imm8 "out_imm": 2, # 0xE6 + imm8 "short_jmp": 2, # 0xEB + disp8 "short_jcc": 2, } if itype in size_map: sz = size_map[itype] position[current_section] += sz() if callable(sz) else sz else: raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype) def parse_tokens(tokens, line_num, line_text): if not tokens: return if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon': label = tokens[0][1] labels[label] = position[current_section] label_sections[label] = current_section rest = tokens[2:] if rest: parse_instruction_or_directive(rest, line_num, line_text) return parse_instruction_or_directive(tokens, line_num, line_text) def parse(source): global pass_num, text_size, data_size, vaddr_text, vaddr_data global offset_text, offset_data, memsz_text, memsz_data global sections, position lines = source.split('\n') # === ПРОХОД 1: анализ === pass_num = 1 position[".text"] = 0 position[".data"] = 0 sections[".text"] = bytearray() sections[".data"] = bytearray() log("=== ПРОХОД 1: анализ ===") for line_num, line in enumerate(lines, start=1): original_line = line.rstrip() log("[ПРОХОД 1] Line " + str(line_num) + ": " + original_line) try: tokens = tokenize_line(line) log_tokens(line_num, tokens) parse_tokens(tokens, line_num, original_line) except Exception as e: error_full = make_error_msg(line_num, original_line, str(e)) log("ОШИБКА на проходе 1:\n" + error_full) print(error_full, file=sys.stderr) close_log() sys.exit(1) text_size = position[".text"] data_size = position[".data"] PAGE_SIZE = 0x1000 elf_header_size = 64 program_header_size = 56 ph_num = 2 ph_table_size = program_header_size * ph_num offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE) offset_data = align_up(offset_text + text_size, PAGE_SIZE) vaddr_text = code_start memsz_text = align_up(text_size, PAGE_SIZE) vaddr_data = align_up(vaddr_text + memsz_text, PAGE_SIZE) memsz_data = align_up(data_size, PAGE_SIZE) log("Вычисленные адреса:") log(".text offset = " + hex(offset_text) + ", vaddr = " + hex(vaddr_text) + ", size = " + str(text_size) + ", memsz = " + str(memsz_text)) log(".data offset = " + hex(offset_data) + ", vaddr = " + hex(vaddr_data) + ", size = " + str(data_size) + ", memsz = " + str(memsz_data)) # === ПРОХОД 2: генерация === pass_num = 2 position[".text"] = 0 position[".data"] = 0 sections[".text"] = bytearray() sections[".data"] = bytearray() log("=== ПРОХОД 2: генерация ===") for line_num, line in enumerate(lines, start=1): original_line = line.rstrip() log("[ПРОХОД 2] Line " + str(line_num) + ": " + original_line) try: tokens = tokenize_line(line) log_tokens(line_num, tokens) parse_tokens(tokens, line_num, original_line) except Exception as e: error_full = make_error_msg(line_num, original_line, str(e)) log("ОШИБКА на проходе 2:\n" + error_full) print(error_full, file=sys.stderr) close_log() sys.exit(1) log_labels() # === ELF === def create_elf(filename): global sections, entry_point, labels, vaddr_text global offset_text, offset_data, vaddr_data, memsz_text, memsz_data text = sections[".text"] data = sections[".data"] entry_addr = vaddr_text + labels.get(entry_point, 0) e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8 e_type = pack_u16_le(2) e_machine = pack_u16_le(0x3E) e_version = pack_u32_le(1) e_entry = pack_u64_le(entry_addr) e_phoff = pack_u64_le(64) e_shoff = pack_u64_le(0) e_flags = pack_u32_le(0) e_ehsize = pack_u16_le(64) e_phentsize = pack_u16_le(56) e_phnum = pack_u16_le(2) e_shentsize = pack_u16_le(0) e_shnum = pack_u16_le(0) e_shstrndx = pack_u16_le(0) elf_header = ( e_ident + e_type + e_machine + e_version + e_entry + e_phoff + e_shoff + e_flags + e_ehsize + e_phentsize + e_phnum + e_shentsize + e_shnum + e_shstrndx ) p_type = pack_u32_le(1) p_flags = pack_u32_le(5) p_offset = pack_u64_le(offset_text) p_vaddr = pack_u64_le(vaddr_text) p_paddr = pack_u64_le(vaddr_text) p_filesz = pack_u64_le(len(text)) p_memsz = pack_u64_le(memsz_text) p_align = pack_u64_le(0x1000) text_header = ( p_type + p_flags + p_offset + p_vaddr + p_paddr + p_filesz + p_memsz + p_align ) p_flags_data = pack_u32_le(6) p_offset_data = pack_u64_le(offset_data) p_vaddr_data = pack_u64_le(vaddr_data) p_paddr_data = pack_u64_le(vaddr_data) p_filesz_data = pack_u64_le(len(data)) p_memsz_data = pack_u64_le(memsz_data) data_header = ( p_type + p_flags_data + p_offset_data + p_vaddr_data + p_paddr_data + p_filesz_data + p_memsz_data + p_align ) # === ЛОГГИРОВАНИЕ ELF-ЗАГОЛОВКА === log("=== Сформированный ELF-заголовок ===") log("e_ident = " + str(e_ident)) log("e_type = " + hex(int.from_bytes(e_type, 'little'))) log("e_machine = " + hex(int.from_bytes(e_machine, 'little'))) log("e_version = " + hex(int.from_bytes(e_version, 'little'))) log("e_entry = " + hex(entry_addr)) log("e_phoff = " + hex(int.from_bytes(e_phoff, 'little'))) log("e_shoff = " + hex(int.from_bytes(e_shoff, 'little'))) log("e_flags = " + hex(int.from_bytes(e_flags, 'little'))) log("e_ehsize = " + hex(int.from_bytes(e_ehsize, 'little'))) log("e_phentsize = " + hex(int.from_bytes(e_phentsize, 'little'))) log("e_phnum = " + hex(int.from_bytes(e_phnum, 'little'))) log("e_shentsize = " + hex(int.from_bytes(e_shentsize, 'little'))) log("e_shnum = " + hex(int.from_bytes(e_shnum, 'little'))) log("e_shstrndx = " + hex(int.from_bytes(e_shstrndx, 'little'))) # === ЛОГГИРОВАНИЕ PROGRAM HEADER (.text) === log("=== Program Header: .text ===") log("p_type = " + hex(int.from_bytes(p_type, 'little'))) log("p_flags = " + hex(int.from_bytes(p_flags, 'little'))) log("p_offset = " + hex(int.from_bytes(p_offset, 'little'))) log("p_vaddr = " + hex(int.from_bytes(p_vaddr, 'little'))) log("p_paddr = " + hex(int.from_bytes(p_paddr, 'little'))) log("p_filesz = " + hex(int.from_bytes(p_filesz, 'little'))) log("p_memsz = " + hex(int.from_bytes(p_memsz, 'little'))) log("p_align = " + hex(int.from_bytes(p_align, 'little'))) # === ЛОГГИРОВАНИЕ PROGRAM HEADER (.data) === log("=== Program Header: .data ===") log("p_type = " + hex(int.from_bytes(p_type, 'little'))) log("p_flags = " + hex(int.from_bytes(p_flags_data, 'little'))) log("p_offset = " + hex(int.from_bytes(p_offset_data, 'little'))) log("p_vaddr = " + hex(int.from_bytes(p_vaddr_data, 'little'))) log("p_paddr = " + hex(int.from_bytes(p_paddr_data, 'little'))) log("p_filesz = " + hex(int.from_bytes(p_filesz_data, 'little'))) log("p_memsz = " + hex(int.from_bytes(p_memsz_data, 'little'))) log("p_align = " + hex(int.from_bytes(p_align, 'little'))) # === Опционально: дамп байтов заголовков (для бинарной верификации) === def bytes_to_hex_str(b): s = "" for byte in b: s += "\\x" + hex(byte)[2:].zfill(2) return s log("Байты ELF-заголовка: " + bytes_to_hex_str(elf_header)) log("Байты program header .text: " + bytes_to_hex_str(text_header)) log("Байты program header .data: " + bytes_to_hex_str(data_header)) file_content = bytearray() file_content.extend(elf_header) file_content.extend(text_header) file_content.extend(data_header) while len(file_content) < offset_text: file_content.append(0) file_content.extend(text) while len(file_content) < offset_data: file_content.append(0) file_content.extend(data) f_out = open(filename, "wb") f_out.write(file_content) f_out.close() import os os.chmod(filename, 0o755) # === Основной запуск === if len(sys.argv) != 2: print("Использование: python asm3_full_two_pass.py <файл.квс>") sys.exit(1) source_file = sys.argv[1] source_filename = source_file if not source_file.endswith('.квс'): print("Ошибка: файл должен иметь расширение .квс") sys.exit(1) f_in = None try: f_in = open(source_file, "r", encoding="utf-8") source = f_in.read() f_in.close() except FileNotFoundError: safe_close(f_in) print("Ошибка: файл '" + source_file + "' не найден.") sys.exit(1) except UnicodeDecodeError as e: safe_close(f_in) print("Ошибка кодировки в файле '" + source_file + "': " + str(e)) sys.exit(1) log_file = open("asm_log.txt", "w", encoding="utf-8") tokens_log = open("tokens.log", "w", encoding="utf-8") try: parse(source) elf_file = source_file[:-4] + ".elf" create_elf(elf_file) close_log() print("ELF-файл успешно создан: " + elf_file) print("Токены сохранены в tokens.log") print("Запустите: ./" + elf_file) except SystemExit: raise except Exception as e: close_log() print("Внутренняя ошибка ассемблера (сообщите разработчику, ну или сами как-нить ...): " + str(e), file=sys.stderr) sys.exit(1)