From e1259f0b88604cabb2e08776b2ddb8eb0d4caf99 Mon Sep 17 00:00:00 2001 From: mzhoot Date: Sun, 17 May 2026 12:21:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BA=D0=BE=D0=B4=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D1=89=D0=B8=D0=BA=20=D0=B8=D0=BD=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=86=D0=B8=D0=B9=20=D1=84=D0=B8=D0=BA=D1=81=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=80=D0=B0=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvs_data.py | 71 +++++ kvs_lexer.py | 42 +-- kvs_pass1.py | 184 +++++++------ kvs_pass2.py | 37 +-- kvs_pass2_encoder.py | 495 +++-------------------------------- kvs_pass2_encoder_fixsize.py | 474 +++++++++++++++++++++++++++++++++ todo.txt | 3 +- 7 files changed, 704 insertions(+), 602 deletions(-) create mode 100644 kvs_pass2_encoder_fixsize.py diff --git a/kvs_data.py b/kvs_data.py index b6f760a..6b72074 100644 --- a/kvs_data.py +++ b/kvs_data.py @@ -238,6 +238,77 @@ INSTRUCTIONS = { } +# Размеры инструкций в байтах (для первого прохода) +INSTRUCTION_SIZES = { + "переместить_имм": 10, + "сравнить_с": 7, + "переход": 5, + "переход_если_равно": 6, + "переход_если_неравно": 6, + "переход_если_ноль": 6, + "переход_если_не_ноль": 6, + "переход_если_меньше": 6, + "переход_если_больше": 6, + "переход_если_меньше_или_равно": 6, + "переход_если_больше_или_равно": 6, + "переход_если_перенос": 6, + "переход_если_нет_переноса": 6, + "вызов_системы": 2, + "нет_операции": 1, + "короткий_переход": 2, + "короткий_переход_если_равно": 2, + "короткий_переход_если_неравно": 2, + "короткий_переход_если_меньше": 2, + "короткий_переход_если_больше": 2, + "короткий_переход_если_меньше_или_равно": 2, + "короткий_переход_если_больше_или_равно": 2, + "короткий_переход_если_перенос": 2, + "короткий_переход_если_нет_переноса": 2, + "короткий_переход_если_ноль": 2, + "короткий_переход_если_не_ноль": 2, + "сравнить": 3, + "проверить": 3, + "вычесть": 3, + "прибавить": 3, + "увеличить": 3, + "уменьшить": 3, + "и": 3, + "или": 3, + "исключающее_или": 3, + "инвертировать": 3, + "отрицать": 3, + "втолкнуть": 2, + "вытолкнуть": 2, + "втолкнуть_непосредственно": 5, + "умножить": 3, + "умножить_знаковое": 3, + "разделить": 3, + "разделить_знаковое": 3, + "сдвиг_влево": 4, + "сдвиг_вправо": 4, + "сдвиг_арифметический_вправо": 4, + "вращать_влево": 4, + "вращать_вправо": 4, + "цикл": 2, + "обменять": 4, + "прервать": 2, + "ввод_байта": 2, + "вывод_байта": 2, + "установить_перенос": 1, + "сбросить_перенос": 1, + "установить_направление": 1, + "сбросить_направление": 1, + "втолкнуть_флаги": 1, + "вытолкнуть_флаги": 1, + "переместить_байт": 1, + "переместить_слово": 1, + "сравнить_байты": 1, + "сканировать_байт": 1, + "идентифицировать_процессор": 2, + "прочитать_счётчик": 2, +} + + # === ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ === def get_reg_info(reg_name): diff --git a/kvs_lexer.py b/kvs_lexer.py index d48c487..e235b8b 100644 --- a/kvs_lexer.py +++ b/kvs_lexer.py @@ -16,25 +16,43 @@ """ import sys -import re def error_unterminated_string(line_num): return f"Незакрытая кавычка в строке на строке {line_num}" def is_hex_number(s): """Проверяет, является ли строка шестнадцатеричным числом (0x... или 0X...)""" - return re.match(r'^0[xX][0-9a-fA-F]+$', s) is not None + if len(s) < 3: + return False + if not (s[0] == '0' and (s[1] == 'x' or s[1] == 'X')): + return False + for ch in s[2:]: + if not (('0' <= ch <= '9') or ('a' <= ch <= 'f') or ('A' <= ch <= 'F')): + return False + return True def is_dec_number(s): """Проверяет, является ли строка десятичным числом (возможно с минусом)""" - if s.startswith('-'): - s = s[1:] - return s.isdigit() + if not s: + return False + start = 0 + if s[0] == '-': + if len(s) == 1: + return False + start = 1 + for ch in s[start:]: + if ch < '0' or ch > '9': + return False + return True def tokenize_line(line, line_num): """Разбирает строку в токены""" # Удаляем комментарии - semi = line.find(';') + semi = -1 + for idx in range(len(line)): + if line[idx] == ';': + semi = idx + break if semi != -1: line = line[:semi] line = line.rstrip() @@ -49,7 +67,7 @@ def tokenize_line(line, line_num): ch = line[i] # Пропускаем пробелы - if ch.isspace(): + if ch == ' ' or ch == '\t': i += 1 continue @@ -81,7 +99,7 @@ def tokenize_line(line, line_num): i += 1 continue - # НОВЫЕ ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ + # ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ if ch == '[': tokens.append(('LBRACKET', '[')) i += 1 @@ -98,12 +116,6 @@ def tokenize_line(line, line_num): continue if ch == '-': - # Проверяем, не является ли это началом отрицательного числа - # Смотрим следующий символ - if i + 1 < n and (line[i + 1].isdigit() or line[i + 1] == '0'): - # Возможно отрицательное число, дадим разобраться дальше - # Но для простоты сохраняем как MINUS, а число будет отдельным токеном - pass tokens.append(('MINUS', '-')) i += 1 continue @@ -115,7 +127,7 @@ def tokenize_line(line, line_num): # Слова, числа, метки j = i - while j < n and not (line[j].isspace() or line[j] in ',:;[]+*-'): + while j < n and not (line[j] in ' \t,;:[]+*-'): j += 1 word = line[i:j] diff --git a/kvs_pass1.py b/kvs_pass1.py index 268e444..7b1841c 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.py @@ -7,9 +7,8 @@ """ import sys -import re sys.path.insert(0, '.') -from kvs_data import PAGE_SIZE, text_vaddr_base, data_vaddr_base, align_up, INSTRUCTIONS +from kvs_data import PAGE_SIZE, text_vaddr_base, align_up, INSTRUCTIONS, INSTRUCTION_SIZES def unescape_string(s): """Преобразует escape-последовательности в реальные символы""" @@ -39,6 +38,56 @@ def read_ast(input_file): ast_lines.append(line.strip()) return ast_lines +def is_hex_number(s): + """Проверяет, является ли строка шестнадцатеричным числом""" + if len(s) < 3: + return False + if not (s[0] == '0' and (s[1] == 'x' or s[1] == 'X')): + return False + for ch in s[2:]: + if not (('0' <= ch <= '9') or ('a' <= ch <= 'f') or ('A' <= ch <= 'F')): + return False + return True + +def is_dec_number(s): + """Проверяет, является ли строка десятичным числом (возможно с минусом)""" + if not s: + return False + start = 0 + if s[0] == '-': + if len(s) == 1: + return False + start = 1 + for ch in s[start:]: + if ch < '0' or ch > '9': + return False + return True + +def is_register(s): + """Проверяет, является ли строка именем регистра""" + return s in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', + 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15'] + +def split_by_operators(content): + """ + Разбивает строку по операторам + и - с сохранением операторов. + Аналог re.split(r'([+\-])', content) + Пример: "раикс + рбикс*4 - 8" -> ["раикс ", "+", " рбикс*4 ", "-", " 8"] + """ + parts = [] + current = '' + for ch in content: + if ch == '+' or ch == '-': + if current: + parts.append(current) + parts.append(ch) + current = '' + else: + current += ch + if current: + parts.append(current) + return parts + def parse_memory_operand(operand_str): """ Анализирует операнд памяти вида [reg + reg*scale + disp] @@ -71,9 +120,9 @@ def parse_memory_operand(operand_str): } # Проверяем, не является ли содержимое просто числом (абсолютный адрес) - if content.isdigit() or (content.startswith('0x') and len(content) > 2 and content[2:].replace('0','').replace('1','').replace('2','').replace('3','').replace('4','').replace('5','').replace('6','').replace('7','').replace('8','').replace('9','').replace('a','').replace('b','').replace('c','').replace('d','').replace('e','').replace('f','').replace('A','').replace('B','').replace('C','').replace('D','').replace('E','').replace('F','') == ''): + if is_dec_number(content) or is_hex_number(content): result['has_disp'] = True - if content.startswith('0x'): + if content.startswith('0x') or content.startswith('0X'): result['disp_value'] = int(content, 16) else: result['disp_value'] = int(content) @@ -81,43 +130,41 @@ def parse_memory_operand(operand_str): return result # Простой регистр без смещения - if content in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', - 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']: + if is_register(content): result['has_base'] = True result['base_reg'] = content return result # Разбираем выражение: регистр [+- регистр[*масштаб] [+- смещение]] - parts = re.split(r'([+\-])', content) + parts = split_by_operators(content) for part in parts: part = part.strip() - if not part or part in '+-': + if not part or part == '+' or part == '-': continue - if part in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', - 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']: + if is_register(part): if not result['has_base']: result['has_base'] = True result['base_reg'] = part elif not result['has_index']: result['has_index'] = True result['index_reg'] = part - elif part.isdigit() or (part.startswith('0x') and len(part) > 2): + elif is_dec_number(part) or is_hex_number(part): result['has_disp'] = True - if part.startswith('0x'): + if part.startswith('0x') or part.startswith('0X'): result['disp_value'] = int(part, 16) else: result['disp_value'] = int(part) elif '*' in part: - reg_part, scale_part = part.split('*') - reg_part = reg_part.strip() - scale_part = scale_part.strip() - if reg_part in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', - 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']: - result['has_index'] = True - result['index_reg'] = reg_part - result['scale'] = int(scale_part) + star_pos = part.find('*') + if star_pos != -1: + reg_part = part[:star_pos].strip() + scale_part = part[star_pos + 1:].strip() + if is_register(reg_part): + result['has_index'] = True + result['index_reg'] = reg_part + result['scale'] = int(scale_part) if result['has_disp']: if -128 <= result['disp_value'] <= 127: @@ -179,16 +226,20 @@ class Pass1: elif directive == '.константа': name = parts[2] value_str = parts[3] - if value_str.isdigit(): + if is_dec_number(value_str): self.symbols[name] = int(value_str) - elif value_str.startswith('0x'): + elif is_hex_number(value_str): self.symbols[name] = int(value_str, 16) else: self.symbols[name] = 0 elif directive == '.байт': sec = parts[2] if len(parts) > 3 and parts[3]: - num_bytes = len(parts[3].split(',')) + # Считаем количество значений, разделённых запятыми + num_bytes = 1 + for ch in parts[3]: + if ch == ',': + num_bytes += 1 self.position[sec] += num_bytes # Директивы резервирования в .бнд @@ -218,7 +269,18 @@ class Pass1: mnemonic = parts[1] sec = parts[2] operands_str = parts[3] if len(parts) > 3 else "" - operands = operands_str.split(',') if operands_str else [] + operands = [] + if operands_str: + # Разбиваем по запятым + current = '' + for ch in operands_str: + if ch == ',': + operands.append(current) + current = '' + else: + current += ch + if current: + operands.append(current) size = self.estimate_size(mnemonic, operands) self.position[sec] += size @@ -230,83 +292,15 @@ class Pass1: # RIP-relative: REX (1) + opcode (1) + ModR/M (1) + disp32 (4) = 7 return 7 - # Инструкции без операндов + # Инструкции без операндов — размер из INSTRUCTIONS if mnemonic in ("вызов_системы", "нет_операции", "вернуться", "остановить", "отладка"): instr = INSTRUCTIONS.get(mnemonic) if instr and "opcode" in instr: return len(instr["opcode"]) return 2 - # Таблица фиксированных размеров - size_map = { - "переместить_имм": 10, - "сравнить_с": 7, - "переход": 5, - "переход_если_равно": 6, - "переход_если_неравно": 6, - "переход_если_ноль": 6, - "переход_если_не_ноль": 6, - "переход_если_меньше": 6, - "переход_если_больше": 6, - "переход_если_меньше_или_равно": 6, - "переход_если_больше_или_равно": 6, - "переход_если_перенос": 6, - "переход_если_нет_переноса": 6, - "вызов_системы": 2, - "нет_операции": 1, - "короткий_переход": 2, - "короткий_переход_если_равно": 2, - "короткий_переход_если_неравно": 2, - "короткий_переход_если_меньше": 2, - "короткий_переход_если_больше": 2, - "короткий_переход_если_меньше_или_равно": 2, - "короткий_переход_если_больше_или_равно": 2, - "короткий_переход_если_перенос": 2, - "короткий_переход_если_нет_переноса": 2, - "короткий_переход_если_ноль": 2, - "короткий_переход_если_не_ноль": 2, - "сравнить": 3, - "проверить": 3, - "вычесть": 3, - "прибавить": 3, - "увеличить": 3, - "уменьшить": 3, - "и": 3, - "или": 3, - "исключающее_или": 3, - "инвертировать": 3, - "отрицать": 3, - "втолкнуть": 2, - "вытолкнуть": 2, - "втолкнуть_непосредственно": 5, - "умножить": 3, - "умножить_знаковое": 3, - "разделить": 3, - "разделить_знаковое": 3, - "сдвиг_влево": 4, - "сдвиг_вправо": 4, - "сдвиг_арифметический_вправо": 4, - "вращать_влево": 4, - "вращать_вправо": 4, - "цикл": 2, - "обменять": 4, - "прервать": 2, - "ввод_байта": 2, - "вывод_байта": 2, - "установить_перенос": 1, - "сбросить_перенос": 1, - "установить_направление": 1, - "сбросить_направление": 1, - "втолкнуть_флаги": 1, - "вытолкнуть_флаги": 1, - "переместить_байт": 1, - "переместить_слово": 1, - "сравнить_байты": 1, - "сканировать_байт": 1, - "идентифицировать_процессор": 2, - "прочитать_счётчик": 2, - } - return size_map.get(mnemonic, 3) + # Фиксированные размеры из общей таблицы + return INSTRUCTION_SIZES.get(mnemonic, 3) def calculate_layout(self): text_size = self.position[".text"] diff --git a/kvs_pass2.py b/kvs_pass2.py index 26c5d4b..c5b832f 100644 --- a/kvs_pass2.py +++ b/kvs_pass2.py @@ -91,11 +91,8 @@ class Pass2: self.symbols = symbols self.vaddr_text = pass1_data["vaddr_text"] text_size = pass1_data["text_size"] - data_size = pass1_data["data_size"] self.vaddr_data = align_up(self.vaddr_text + text_size, PAGE_SIZE) - self.vaddr_bnd = pass1_data.get("vaddr_bnd", align_up(self.vaddr_data + data_size, PAGE_SIZE)) - self.bnd_size = pass1_data.get("bnd_size", 0) - self.position = {".text": 0, ".data": 0, ".бнд": 0} + self.position = {".text": 0, ".data": 0} self.current_section = ".text" self.csv_lines = [] self.data_bytes = {".text": bytearray(), ".data": bytearray()} @@ -122,14 +119,7 @@ class Pass2: op_str = operands[1] if op_str in self.labels: sec = self.label_sections[op_str] - if sec == ".text": - addr = self.labels[op_str] + self.vaddr_text - elif sec == ".data": - addr = self.labels[op_str] + self.vaddr_data - elif sec == ".бнд": - addr = self.labels[op_str] + self.vaddr_bnd - else: - addr = self.labels[op_str] + self.vaddr_text + addr = self.labels[op_str] + (self.vaddr_text if sec == ".text" else self.vaddr_data) return hex(addr) elif op_str in self.symbols: return hex(self.symbols[op_str]) @@ -149,8 +139,6 @@ class Pass2: self.current_section = ".text" elif directive == '.данные': self.current_section = ".data" - elif directive == '.бнд': - self.current_section = ".бнд" elif directive in ('.строка_нуль', '.строка'): s = parts[3] if len(parts) > 3 else "" real_s = unescape_string(s) @@ -192,23 +180,6 @@ class Pass2: self.data_bytes[".data"].append(val & 0xFF) self.position[".data"] += len(byte_values) - - # Директивы резервирования в .бнд — не генерируют байты - elif directive == '.резб': - count = int(parts[3]) - self.position['.бнд'] += count * 1 - - elif directive == '.резс': - count = int(parts[3]) - self.position['.бнд'] += count * 2 - - elif directive == '.рездс': - count = int(parts[3]) - self.position['.бнд'] += count * 4 - - elif directive == '.резкс': - count = int(parts[3]) - self.position['.бнд'] += count * 8 elif line_type == "LABEL": label_name = parts[1] @@ -278,6 +249,4 @@ if __name__ == "__main__": print(f"Проход 2: {len(csv_lines)} строк CSV записано в {sys.argv[3]}") print(f" .text: {pass2.position['.text']} байт, виртуальный адрес: 0x{pass2.vaddr_text:x}") - print(f" .data: {pass2.position['.data']} байт, виртуальный адрес: 0x{pass2.vaddr_data:x}") - if pass2.bnd_size > 0: - print(f" .бнд: {pass2.bnd_size} байт, виртуальный адрес: 0x{pass2.vaddr_bnd:x}") \ No newline at end of file + print(f" .data: {pass2.position['.data']} байт, виртуальный адрес: 0x{pass2.vaddr_data:x}") \ No newline at end of file diff --git a/kvs_pass2_encoder.py b/kvs_pass2_encoder.py index e2855de..da65f13 100644 --- a/kvs_pass2_encoder.py +++ b/kvs_pass2_encoder.py @@ -3,14 +3,44 @@ """ Кодировщик инструкций для КВС -Содержит все функции генерации машинного кода +Содержит все функции генерации машинного кода. +Фиксированные инструкции вынесены в kvs_pass2_encoder_fixsize.py. +Здесь остаются: parse_operand, parse_memory_operand, инструкции с адресацией, диспетчер. """ import struct import sys from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info +from kvs_pass2_encoder_fixsize import ( + encode_mov_reg_reg, + encode_mov_reg8_imm8, + encode_cmp_reg_reg, + encode_add_sub_reg_reg, + encode_muldiv, + encode_and_or_xor_reg_reg, + encode_not_neg, + encode_incdec, + encode_push_reg, + encode_pop_reg, + encode_push_imm, + encode_shift_rotate, + encode_syscall, + encode_int, + encode_hlt, + encode_int3, + encode_nop, + encode_flag_instruction, + encode_in, + encode_out, + encode_string_instruction, + encode_cpuid, + encode_rdtsc, + encode_xchg, +) +# ========== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ========== + def parse_operand(operand, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None): """ Вычисляет значение операнда. @@ -96,6 +126,8 @@ def parse_memory_operand(operand_str, labels, label_sections, vaddr_text, vaddr_ return None +# ========== MOV С ПАМЯТЬЮ ========== + def encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text): """ Кодирует MOV reg, [addr] @@ -169,12 +201,10 @@ def encode_mov_reg_mem(operands, labels, label_sections, symbols, vaddr_text, va reg_info = get_reg_info(operands[0]) mem_operand = operands[1] - # Пытаемся разобрать операнд памяти mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd) if mem_info and mem_info['type'] == 'absolute': return encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text) - # TODO: другие типы адресации print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr) return b'\x90' * 3 @@ -187,12 +217,10 @@ def encode_mov_mem_reg(operands, labels, label_sections, symbols, vaddr_text, va mem_operand = operands[0] reg_info = get_reg_info(operands[1]) - # Пытаемся разобрать операнд памяти mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd) if mem_info and mem_info['type'] == 'absolute': return encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text) - # TODO: другие типы адресации print(f"Предупреждение: сложная адресация '{mem_operand}' пока не поддерживается", file=sys.stderr) return b'\x90' * 3 @@ -205,7 +233,6 @@ def encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_ reg_info = get_reg_info(operands[0]) mem_operand = operands[1] - # Пытаемся разобрать операнд памяти mem_info = parse_memory_operand(mem_operand, labels, label_sections, vaddr_text, vaddr_data, vaddr_bnd) if mem_info and mem_info['type'] == 'absolute': target_addr = mem_info['address'] @@ -231,7 +258,7 @@ def encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_ return b'\x90' * 3 -# ========== 1. Инструкции перемещения данных (MOV, LEA, MOVZX, MOVSX) ========== +# ========== MOV reg, imm (зависит от parse_operand) ========== def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None): """ @@ -291,65 +318,7 @@ def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, va return code -def encode_mov_reg_reg(operands): - """ - MOV reg, reg - переместить данные между регистрами - Формат: переместить <регистр_назначения>, <регистр_источник> - """ - code = bytearray() - instr = INSTRUCTIONS["переместить"] - dst_info = get_reg_info(operands[0]) - src_info = get_reg_info(operands[1]) - dst = dst_info["index"] - src = src_info["index"] - size = dst_info["size"] - - use_66 = (size == 16) - use_rex_w = (size == 64) - rex = 0x40 - if use_rex_w: - rex |= 0x08 - if src >= 8: - rex |= 0x04 - if dst >= 8: - rex |= 0x01 - if dst_info.get("high8") or src_info.get("high8"): - rex = 0 - - if use_66: - code.append(0x66) - if rex != 0x40 or use_rex_w: - code.append(rex) - code.extend(instr["opcode"][-1:]) - modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) - code.append(modrm) - return code - - -def encode_mov_reg8_imm8(operands): - """ - MOV reg8, imm8 - загрузить непосредственный байт в 8-битный регистр - Формат: загрузить_байт <регистр8>, <байт> - """ - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - imm = int(operands[1]) if operands[1].isdigit() else int(operands[1], 16) - - if reg_info.get("high8"): - code.append(0xB0 + reg + 4) - else: - if reg < 4: - code.append(0xB0 + reg) - else: - rex = 0x40 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.append(0xB0 + (reg & 7)) - code.append(imm & 0xFF) - return code - +# ========== MOVZX/MOVSX (зависят от parse_operand) ========== def encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None): """ @@ -395,7 +364,7 @@ def encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_da return code -# ========== 2. Инструкции сравнения (CMP, TEST) ========== +# ========== CMP reg, imm (зависит от parse_operand) ========== def encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None): """ @@ -472,42 +441,7 @@ def encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, va return code -def encode_cmp_reg_reg(mnemonic, operands): - """ - CMP reg, reg - сравнить регистры - Формат: сравнить <регистр1>, <регистр2> - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - dst_info = get_reg_info(operands[0]) - src_info = get_reg_info(operands[1]) - dst = dst_info["index"] - src = src_info["index"] - size = dst_info["size"] - - use_66 = (size == 16) - use_rex_w = (size == 64) - rex = 0x40 - if use_rex_w: - rex |= 0x08 - if src >= 8: - rex |= 0x04 - if dst >= 8: - rex |= 0x01 - if dst_info.get("high8") or src_info.get("high8"): - rex = 0 - - if use_66: - code.append(0x66) - if rex != 0x40 or use_rex_w: - code.append(rex) - code.extend(instr["opcode"][-1:]) - modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) - code.append(modrm) - return code - - -# ========== 3. Арифметические инструкции (ADD, SUB, MUL, DIV) ========== +# ========== ADD/SUB reg, imm (зависит от parse_operand) ========== def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, vaddr_bnd=None): """ @@ -573,119 +507,7 @@ def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, return code -def encode_add_sub_reg_reg(mnemonic, operands): - """ - ADD/SUB reg, reg - сложить/вычесть регистры - Формат: прибавить / вычесть <регистр_назначения>, <регистр_источник> - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - dst_info = get_reg_info(operands[0]) - src_info = get_reg_info(operands[1]) - dst = dst_info["index"] - src = src_info["index"] - size = dst_info["size"] - - use_66 = (size == 16) - use_rex_w = (size == 64) - rex = 0x40 - if use_rex_w: - rex |= 0x08 - if src >= 8: - rex |= 0x04 - if dst >= 8: - rex |= 0x01 - if dst_info.get("high8") or src_info.get("high8"): - rex = 0 - - if use_66: - code.append(0x66) - if rex != 0x40 or use_rex_w: - code.append(rex) - code.extend(instr["opcode"][-1:]) - modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) - code.append(modrm) - return code - - -def encode_muldiv(mnemonic): - """ - MUL/IMUL/DIV/IDIV - умножение и деление (работают с RAX/RDX) - Формат: умножить / умножить_знаковое / разделить / разделить_знаковое - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - subop = instr["subop"] - - code.append(0x48) - code.extend(instr["opcode"]) - modrm = 0xC0 | (subop << 3) | 0 - code.append(modrm) - return code - - -# ========== 4. Логические инструкции (AND, OR, XOR, NOT, NEG) ========== - -def encode_and_or_xor_reg_reg(mnemonic, operands): - """ - AND/OR/XOR reg, reg - логические операции над регистрами - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - dst_info = get_reg_info(operands[0]) - src_info = get_reg_info(operands[1]) - dst = dst_info["index"] - src = src_info["index"] - size = dst_info["size"] - - use_66 = (size == 16) - use_rex_w = (size == 64) - rex = 0x40 - if use_rex_w: - rex |= 0x08 - if src >= 8: - rex |= 0x04 - if dst >= 8: - rex |= 0x01 - if dst_info.get("high8") or src_info.get("high8"): - rex = 0 - - if use_66: - code.append(0x66) - if rex != 0x40 or use_rex_w: - code.append(rex) - code.extend(instr["opcode"][-1:]) - modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) - code.append(modrm) - return code - - -def encode_not_neg(mnemonic, operands): - """ - NOT/NEG - инвертировать/отрицать регистр - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - subop = instr["subop"] - - if size == 64: - code.append(0x48) - elif size == 16: - code.append(0x66) - - if reg >= 8: - code.append(0x41) - - code.extend(instr["opcode"]) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - return code - - -# ========== 5. Инструкции переходов и вызовов ========== +# ========== ПЕРЕХОДЫ И ВЫЗОВЫ (зависят от parse_operand) ========== def encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos, vaddr_bnd=None): """ @@ -775,247 +597,6 @@ def encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_dat return code -# ========== 6. Стековые инструкции ========== - -def encode_push_reg(operands): - """PUSH reg""" - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - - if size == 64: - if reg < 8: - code.append(0x50 + reg) - else: - code.append(0x41) - code.append(0x50 + (reg - 8)) - elif size == 16: - code.append(0x66) - if reg < 8: - code.append(0x50 + reg) - else: - code.append(0x41) - code.append(0x50 + (reg - 8)) - else: - raise ValueError(f"PUSH не поддерживает {size}-битные регистры") - - return code - - -def encode_pop_reg(operands): - """POP reg""" - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - - if size == 64: - if reg < 8: - code.append(0x58 + reg) - else: - code.append(0x41) - code.append(0x58 + (reg - 8)) - elif size == 16: - code.append(0x66) - if reg < 8: - code.append(0x58 + reg) - else: - code.append(0x41) - code.append(0x58 + (reg - 8)) - else: - raise ValueError(f"POP не поддерживает {size}-битные регистры") - - return code - - -def encode_push_imm(operands): - """PUSH imm""" - code = bytearray() - imm = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16) - - if -128 <= imm <= 127: - code.append(0x6A) - code.append(imm & 0xFF) - else: - code.append(0x68) - code.extend(struct.pack('= 8: - rex |= 0x01 - code.append(rex) - code.append(0xFF) - elif size == 32: - rex = 0x40 - if reg >= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(0xFF) - elif size == 16: - code.append(0x66) - rex = 0x40 - if reg >= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(0xFF) - elif size == 8: - if reg_info.get("high8"): - code.append(0xFE) - else: - if reg < 4: - code.append(0xFE) - else: - rex = 0x40 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.append(0xFE) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - return bytes(code) - - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - return bytes(code) - - -# ========== 8. Системные и отладочные инструкции ========== - -def encode_syscall(): - return INSTRUCTIONS["вызов_системы"]["opcode"] - -def encode_int(operands): - code = bytearray() - instr = INSTRUCTIONS["прервать"] - code.extend(instr["opcode"]) - imm = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16) - code.append(imm & 0xFF) - return code - -def encode_hlt(): - return INSTRUCTIONS["остановить"]["opcode"] - -def encode_int3(): - return INSTRUCTIONS["отладка"]["opcode"] - -def encode_nop(): - return INSTRUCTIONS["нет_операции"]["opcode"] - - -# ========== 9. Инструкции работы с флагами ========== - -def encode_flag_instruction(mnemonic): - return INSTRUCTIONS[mnemonic]["opcode"] - - -# ========== 10. Ввод-вывод ========== - -def encode_in(operands): - code = bytearray() - instr = INSTRUCTIONS["ввод_байта"] - code.extend(instr["opcode"]) - port = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16) - code.append(port & 0xFF) - return code - -def encode_out(operands): - code = bytearray() - instr = INSTRUCTIONS["вывод_байта"] - code.extend(instr["opcode"]) - port = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16) - code.append(port & 0xFF) - return code - - -# ========== 11. Строковые инструкции ========== - -def encode_string_instruction(mnemonic): - return INSTRUCTIONS[mnemonic]["opcode"] - - -# ========== 12. Инструкции идентификации процессора ========== - -def encode_cpuid(): - return INSTRUCTIONS["идентифицировать_процессор"]["opcode"] - -def encode_rdtsc(): - return INSTRUCTIONS["прочитать_счётчик"]["opcode"] - - -# ========== 13. Инструкции с памятью (регистр-регистр) ========== - -def encode_xchg(operands): - """XCHG reg, reg - обменять регистры""" - code = bytearray() - instr = INSTRUCTIONS["обменять"] - reg1_info = get_reg_info(operands[0]) - reg2_info = get_reg_info(operands[1]) - reg1 = reg1_info["index"] - reg2 = reg2_info["index"] - - rex = 0x48 if reg1_info["size"] == 64 else 0x40 - if reg1 >= 8 or reg2 >= 8: - rex |= 0x01 - if reg2 >= 8: - rex |= 0x04 - code.append(rex) - code.extend(instr["opcode"]) - modrm = 0xC0 | ((reg2 & 7) << 3) | (reg1 & 7) - code.append(modrm) - return code - - # ========== ГЛАВНЫЙ ДИСПЕТЧЕР ========== def encode_instruction(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos, vaddr_bnd=None): diff --git a/kvs_pass2_encoder_fixsize.py b/kvs_pass2_encoder_fixsize.py new file mode 100644 index 0000000..3df1e45 --- /dev/null +++ b/kvs_pass2_encoder_fixsize.py @@ -0,0 +1,474 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Кодировщик инструкций фиксированного размера для КВС +Инструкции, не зависящие от сложной адресации памяти. +Вынесены из kvs_pass2_encoder.py для упрощения поддержки. +""" + +import struct +from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info + + +# ========== MOV регистр-регистр ========== + +def encode_mov_reg_reg(operands): + """ + MOV reg, reg - переместить данные между регистрами + Формат: переместить <регистр_назначения>, <регистр_источник> + """ + code = bytearray() + instr = INSTRUCTIONS["переместить"] + dst_info = get_reg_info(operands[0]) + src_info = get_reg_info(operands[1]) + dst = dst_info["index"] + src = src_info["index"] + size = dst_info["size"] + + use_66 = (size == 16) + use_rex_w = (size == 64) + rex = 0x40 + if use_rex_w: + rex |= 0x08 + if src >= 8: + rex |= 0x04 + if dst >= 8: + rex |= 0x01 + if dst_info.get("high8") or src_info.get("high8"): + rex = 0 + + if use_66: + code.append(0x66) + if rex != 0x40 or use_rex_w: + code.append(rex) + code.extend(instr["opcode"][-1:]) + modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) + code.append(modrm) + return code + + +# ========== MOV reg8, imm8 ========== + +def encode_mov_reg8_imm8(operands): + """ + MOV reg8, imm8 - загрузить непосредственный байт в 8-битный регистр + Формат: загрузить_байт <регистр8>, <байт> + """ + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + imm = int(operands[1]) if operands[1].isdigit() else int(operands[1], 16) + + if reg_info.get("high8"): + code.append(0xB0 + reg + 4) + else: + if reg < 4: + code.append(0xB0 + reg) + else: + rex = 0x40 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.append(0xB0 + (reg & 7)) + code.append(imm & 0xFF) + return code + + +# ========== CMP/TEST регистр-регистр ========== + +def encode_cmp_reg_reg(mnemonic, operands): + """ + CMP reg, reg - сравнить регистры + Формат: сравнить <регистр1>, <регистр2> + Также TEST через проверить. + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + dst_info = get_reg_info(operands[0]) + src_info = get_reg_info(operands[1]) + dst = dst_info["index"] + src = src_info["index"] + size = dst_info["size"] + + use_66 = (size == 16) + use_rex_w = (size == 64) + rex = 0x40 + if use_rex_w: + rex |= 0x08 + if src >= 8: + rex |= 0x04 + if dst >= 8: + rex |= 0x01 + if dst_info.get("high8") or src_info.get("high8"): + rex = 0 + + if use_66: + code.append(0x66) + if rex != 0x40 or use_rex_w: + code.append(rex) + code.extend(instr["opcode"][-1:]) + modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) + code.append(modrm) + return code + + +# ========== ADD/SUB регистр-регистр ========== + +def encode_add_sub_reg_reg(mnemonic, operands): + """ + ADD/SUB reg, reg - сложить/вычесть регистры + Формат: прибавить / вычесть <регистр_назначения>, <регистр_источник> + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + dst_info = get_reg_info(operands[0]) + src_info = get_reg_info(operands[1]) + dst = dst_info["index"] + src = src_info["index"] + size = dst_info["size"] + + use_66 = (size == 16) + use_rex_w = (size == 64) + rex = 0x40 + if use_rex_w: + rex |= 0x08 + if src >= 8: + rex |= 0x04 + if dst >= 8: + rex |= 0x01 + if dst_info.get("high8") or src_info.get("high8"): + rex = 0 + + if use_66: + code.append(0x66) + if rex != 0x40 or use_rex_w: + code.append(rex) + code.extend(instr["opcode"][-1:]) + modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) + code.append(modrm) + return code + + +# ========== MUL/IMUL/DIV/IDIV ========== + +def encode_muldiv(mnemonic): + """ + MUL/IMUL/DIV/IDIV - умножение и деление (работают с RAX/RDX) + Формат: умножить / умножить_знаковое / разделить / разделить_знаковое + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + subop = instr["subop"] + + code.append(0x48) + code.extend(instr["opcode"]) + modrm = 0xC0 | (subop << 3) | 0 + code.append(modrm) + return code + + +# ========== AND/OR/XOR регистр-регистр ========== + +def encode_and_or_xor_reg_reg(mnemonic, operands): + """ + AND/OR/XOR reg, reg - логические операции над регистрами + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + dst_info = get_reg_info(operands[0]) + src_info = get_reg_info(operands[1]) + dst = dst_info["index"] + src = src_info["index"] + size = dst_info["size"] + + use_66 = (size == 16) + use_rex_w = (size == 64) + rex = 0x40 + if use_rex_w: + rex |= 0x08 + if src >= 8: + rex |= 0x04 + if dst >= 8: + rex |= 0x01 + if dst_info.get("high8") or src_info.get("high8"): + rex = 0 + + if use_66: + code.append(0x66) + if rex != 0x40 or use_rex_w: + code.append(rex) + code.extend(instr["opcode"][-1:]) + modrm = 0xC0 | ((src & 7) << 3) | (dst & 7) + code.append(modrm) + return code + + +# ========== NOT/NEG ========== + +def encode_not_neg(mnemonic, operands): + """ + NOT/NEG - инвертировать/отрицать регистр + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + subop = instr["subop"] + + if size == 64: + code.append(0x48) + elif size == 16: + code.append(0x66) + + if reg >= 8: + code.append(0x41) + + code.extend(instr["opcode"]) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + return code + + +# ========== INC/DEC ========== + +def encode_incdec(mnemonic, operands): + """ + INC / DEC - инкремент и декремент регистра + """ + code = bytearray() + instr = INSTRUCTIONS[mnemonic] + reg_info = get_reg_info(operands[0]) + 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) + elif size == 32: + rex = 0x40 + if reg >= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(0xFF) + elif size == 16: + code.append(0x66) + rex = 0x40 + if reg >= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(0xFF) + elif size == 8: + if reg_info.get("high8"): + code.append(0xFE) + else: + if reg < 4: + code.append(0xFE) + else: + rex = 0x40 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.append(0xFE) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + return bytes(code) + + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + return bytes(code) + + +# ========== Стек (PUSH/POP) ========== + +def encode_push_reg(operands): + """PUSH reg""" + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + + if size == 64: + if reg < 8: + code.append(0x50 + reg) + else: + code.append(0x41) + code.append(0x50 + (reg - 8)) + elif size == 16: + code.append(0x66) + if reg < 8: + code.append(0x50 + reg) + else: + code.append(0x41) + code.append(0x50 + (reg - 8)) + else: + raise ValueError(f"PUSH не поддерживает {size}-битные регистры") + + return code + + +def encode_pop_reg(operands): + """POP reg""" + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + + if size == 64: + if reg < 8: + code.append(0x58 + reg) + else: + code.append(0x41) + code.append(0x58 + (reg - 8)) + elif size == 16: + code.append(0x66) + if reg < 8: + code.append(0x58 + reg) + else: + code.append(0x41) + code.append(0x58 + (reg - 8)) + else: + raise ValueError(f"POP не поддерживает {size}-битные регистры") + + return code + + +def encode_push_imm(operands): + """PUSH imm""" + code = bytearray() + imm = int(operands[0]) if operands[0].isdigit() else int(operands[0], 16) + + if -128 <= imm <= 127: + code.append(0x6A) + code.append(imm & 0xFF) + else: + code.append(0x68) + code.extend(struct.pack('= 8 or reg2 >= 8: + rex |= 0x01 + if reg2 >= 8: + rex |= 0x04 + code.append(rex) + code.extend(instr["opcode"]) + modrm = 0xC0 | ((reg2 & 7) << 3) | (reg1 & 7) + code.append(modrm) + return code \ No newline at end of file diff --git a/todo.txt b/todo.txt index bda6bd9..0dc7766 100644 --- a/todo.txt +++ b/todo.txt @@ -248,4 +248,5 @@ Pass2 (шаг 5): избавиться от ре и перенести таблицу фиксированных размеров -вынести работу с памятью из второго прохода в отдельный энкодер \ No newline at end of file +вынести работу с памятью из второго прохода в отдельный энкодер +изучить работу с кучей \ No newline at end of file