From ce1800f2363c3a8982833b67c7efd17b8ce1093f Mon Sep 17 00:00:00 2001 From: mzhoot Date: Sat, 20 Jun 2026 10:34:59 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D0=BC=D0=BE=D0=BD=D1=82=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=BE=D0=B2=20=D1=809=20-?= =?UTF-8?q?=20=D1=8011?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + kvs_encoder_mutsize.py | 108 +++++++++---- тесты/kvs_full_test.csv | 5 +- тесты/прогон_тестов.py | 2 +- .../тест_адресации_р10.py | 140 +++++++++++++++++ .../тест_адресации_р10.квс | 29 ++++ .../тест_адресации_р11.py | 142 ++++++++++++++++++ .../тест_адресации_р11.квс | 29 ++++ .../тест_адресации_р9.py | 138 +++++++++++++++++ .../тест_адресации_р9.квс | 29 ++++ 10 files changed, 591 insertions(+), 32 deletions(-) create mode 100644 тесты/тест_адресации_р10/тест_адресации_р10.py create mode 100644 тесты/тест_адресации_р10/тест_адресации_р10.квс create mode 100644 тесты/тест_адресации_р11/тест_адресации_р11.py create mode 100644 тесты/тест_адресации_р11/тест_адресации_р11.квс create mode 100644 тесты/тест_адресации_р9/тест_адресации_р9.py create mode 100644 тесты/тест_адресации_р9/тест_адресации_р9.квс diff --git a/.gitignore b/.gitignore index f6a0aaa..4e917fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__/ +.gigacode *.pyc *.pyo \ No newline at end of file diff --git a/kvs_encoder_mutsize.py b/kvs_encoder_mutsize.py index db4ea6f..65f6f35 100644 --- a/kvs_encoder_mutsize.py +++ b/kvs_encoder_mutsize.py @@ -186,44 +186,92 @@ def encode_mov_mem_reg_absolute(reg_info, mem_info, current_pos, vaddr_text): return code -def encode_mov_reg_mem_indirect(reg_info, base_reg): +def encode_mov_reg_mem_indirect(reg_info: dict, base_reg: str) -> bytearray: """ - Кодирует MOV reg, [base_reg] — косвенная адресация. - Пример: mov rax, [rbx] → 48 8B 03 - - Аргументы: - - reg_info: словарь от get_reg_info() с ключами 'index' (int) и 'size' (int) - - base_reg: строка с именем регистра, например 'рбикс' - - REGISTERS[base_reg] возвращает число-индекс (int), НЕ словарь. + Кодирует MOV reg, [base_reg] — косвенная адресация через регистр. """ code = bytearray() - reg = reg_info['index'] - base = REGISTERS[base_reg] # REGISTERS возвращает int (индекс регистра) - rex = 0x48 if reg_info['size'] == 64 else 0x40 - if reg >= 8: - rex |= 0x04 # REX.R - if base >= 8: - rex |= 0x01 # REX.B - code.append(rex) - code.append(0x8B) # MOV r64, r/m64 + # === 1. Получаем индексы регистров === + reg_index = reg_info['index'] + reg_size = reg_info['size'] - # Особый случай: [rbp] или [r13] требует disp8=0 при mod=00 - # (mod=00 с r/m=101 означает RIP-relative, а не [rbp]) - if (base & 7) == 5: - # mod=01 (disp8), reg, r/m=5 - modrm = 0x40 | ((reg & 7) << 3) | 5 - code.append(modrm) - code.append(0x00) # disp8 = 0 + if base_reg not in REGISTERS: + raise ValueError(f"Неизвестный базовый регистр: {base_reg}") + base_index = REGISTERS[base_reg] + + # === 2. Выбираем opcode и префиксы === + # + # В x86-64 для MOV существуют разные opcodes: + # - 8A /r — MOV r8, r/m8 (только 8-бит!) + # - 8B /r — MOV r16/r32/r64, r/m16/r/m32/r/m64 + # + # Для 16-бит нужен префикс 0x66 + # Для 64-бит нужен REX.W (бит 3 в REX) + # + if reg_size == 8: + opcode = 0x8A # MOV r8, r/m8 + rex_base = 0x40 # Без REX.W + need_prefix_66 = False + elif reg_size == 16: + opcode = 0x8B # MOV r16, r/m16 + rex_base = 0x40 # Без REX.W + need_prefix_66 = True # ← ВАЖНО: префикс 0x66 для 16-бит! + elif reg_size == 32: + opcode = 0x8B # MOV r32, r/m32 + rex_base = 0x40 # Без REX.W + need_prefix_66 = False + elif reg_size == 64: + opcode = 0x8B # MOV r64, r/m64 + rex_base = 0x48 # С REX.W (бит 3 = 1) + need_prefix_66 = False else: - # mod=00, reg, r/m=base - modrm = ((reg & 7) << 3) | (base & 7) - code.append(modrm) + raise ValueError(f"Неподдерживаемый размер регистра: {reg_size} бит") + + # === 3. Формируем REX-префикс === + rex = rex_base + + # REX.R: для регистра-источника (reg) — нужен если индекс >= 8 + if reg_index >= 8: + rex |= 0x04 # REX.R = 1 + + # REX.B: для базового регистра (base) — нужен если индекс >= 8 + if base_index >= 8: + rex |= 0x01 # REX.B = 1 + + # === 4. Формируем ModR/M байт === + if (base_index & 7) == 5: + # [rbp] или [r13] требуют disp8=0 + modrm = 0x40 | ((reg_index & 7) << 3) | 5 + modrm_byte = modrm + disp8 = 0x00 + else: + modrm = ((reg_index & 7) << 3) | (base_index & 7) + modrm_byte = modrm + disp8 = None + + # === 5. Собираем инструкцию === + + # 5a. Префикс 0x66 для 16-битных операндов (ДО REX!) + if need_prefix_66: + code.append(0x66) + + # 5b. REX-префикс (если не равен 0x40) + if rex != 0x40: + code.append(rex) + + # 5c. Opcode + code.append(opcode) + + # 5d. ModR/M байт + code.append(modrm_byte) + + # 5e. Disp8 (если есть) + if disp8 is not None: + code.append(disp8) return code - - + def encode_mov_mem_reg_indirect(base_reg, reg_info): """ Кодирует MOV [base_reg], reg — косвенная адресация. diff --git a/тесты/kvs_full_test.csv b/тесты/kvs_full_test.csv index 5d47440..bb7d6aa 100644 --- a/тесты/kvs_full_test.csv +++ b/тесты/kvs_full_test.csv @@ -13,4 +13,7 @@ 1;тест_бнд_3;Проверка BSS: смешанная адресация (абсолютная запись + косвенное чтение) 1;сложение;Тест сложения 40+2=42 (проверка через код возврата) 0;сложение+;Тест сложения 40+2=42 (проверка через код возврата+) -1;вычитание;Тест вычитания 50-8=42 \ No newline at end of file +1;вычитание;Тест вычитания 50-8=42 +1;тест_адресации_р9;Проверка косвенной адресации через R9 (ASCII код первого символа аргумента) +1;тест_адресации_р10;Проверка косвенной адресации через R10 (ASCII код первого символа аргумента) +1;тест_адресации_р11;Проверка косвенной адресации через R11 (ASCII код первого символа аргумента) \ No newline at end of file diff --git a/тесты/прогон_тестов.py b/тесты/прогон_тестов.py index 74a584c..6c2ad7f 100644 --- a/тесты/прогон_тестов.py +++ b/тесты/прогон_тестов.py @@ -5,7 +5,7 @@ import csv from pathlib import Path def main(): - # Диспетчер лежит в тесты/kvs_full_test.py + # Диспетчер лежит в тесты/прогон_тестов.py # Значит, корень с табличкой — та же папка (тесты/) tests_dir = Path(__file__).parent csv_file = tests_dir / "kvs_full_test.csv" diff --git a/тесты/тест_адресации_р10/тест_адресации_р10.py b/тесты/тест_адресации_р10/тест_адресации_р10.py new file mode 100644 index 0000000..a3623c3 --- /dev/null +++ b/тесты/тест_адресации_р10/тест_адресации_р10.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Тест для проверки косвенной адресации через регистр R10 +Запускает скомпилированную программу с различными аргументами +и проверяет код возврата (должен быть равен ASCII коду первого символа) +""" + +import subprocess +import sys +import os +from pathlib import Path + +# Определяем пути +SCRIPT_DIR = Path(__file__).parent.absolute() +PROJECT_ROOT = SCRIPT_DIR.parent.parent +SOURCE_REL = SCRIPT_DIR / "тест_адресации_р10.квс" + +def clean(): + """Очищает артефакты сборки""" + files = [ + "тест_адресации_р10.elf", + "тест_адресации_р10.csv", + "тест_адресации_р10.аст", + "тест_адресации_р10.токены", + "тест_адресации_р10.константа", + "тест_адресации_р10_pass4.log" + ] + for f in files: + f_path = SCRIPT_DIR / f + if f_path.exists(): + f_path.unlink() + print(f"DEBUG: Удалён {f}", file=sys.stderr) + +def build() -> bool: + """ + Собирает тестовую программу с помощью КВС + + Returns: + bool: True если сборка успешна, иначе False + """ + print(f"DEBUG: Сборка {SOURCE_REL}", file=sys.stderr) + os.chdir(PROJECT_ROOT) + + build = subprocess.run( + ["python3", "квс_сборка.py", str(SOURCE_REL)], + capture_output=True, + text=True + ) + + if build.returncode != 0: + print(f"❌ Ошибка сборки: {build.returncode}", file=sys.stderr) + print(build.stderr, file=sys.stderr) + return False + + print("DEBUG: Сборка успешна", file=sys.stderr) + return True + +def test_with_char(char: str, expected_code: int) -> bool: + """ + Запускает скомпилированную программу с одним символом в качестве аргумента + и проверяет код возврата + + Args: + char: символ для передачи в программу + expected_code: ожидаемый ASCII код символа + + Returns: + bool: True если код возврата совпадает с ожидаемым, иначе False + """ + print(f"DEBUG: Тест с символом '{char}' (ожидается код {expected_code})", file=sys.stderr) + os.chdir(SCRIPT_DIR) + + elf_path = SCRIPT_DIR / "тест_адресации_р10.elf" + if not elf_path.exists(): + print("❌ ELF файл не найден", file=sys.stderr) + return False + + # Запускаем программу с аргументом + run = subprocess.run( + [str(elf_path), char], + capture_output=True, + text=True + ) + + # Проверяем код возврата (должен быть ASCII код символа) + if run.returncode != expected_code: + print(f"❌ Код возврата: ожидался {expected_code}, получен {run.returncode}", file=sys.stderr) + if run.stderr: + print(f" stderr: {run.stderr}", file=sys.stderr) + if run.stdout: + print(f" stdout: {run.stdout}", file=sys.stderr) + return False + + print(f"DEBUG: Тест с символом '{char}' пройден (код {run.returncode})", file=sys.stderr) + return True + +def main(): + """Главная функция теста""" + print("DEBUG: Начало теста тест_адресации_р10", file=sys.stderr) + + # Собираем программу + if not build(): + clean() + sys.exit(1) + + # Тестируем разные символы + tests = [ + ('A', 65), # Латинская A + ('B', 66), # Латинская B + ('0', 48), # Цифра 0 + ('z', 122), # Латинская z + ('!', 33), # Восклицательный знак + ('x', 120), # Латинская x + ('5', 53), # Цифра 5 + ('_', 95), # Подчёркивание + ('$', 36), # Знак доллара + ('@', 64), # Собака + ] + + all_passed = True + + for char, expected_code in tests: + if not test_with_char(char, expected_code): + all_passed = False + break + + # Очищаем артефакты + clean() + + if all_passed: + print("✅ Все тесты пройдены!", file=sys.stderr) + sys.exit(0) + else: + print("❌ Тест провален", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/тесты/тест_адресации_р10/тест_адресации_р10.квс b/тесты/тест_адресации_р10/тест_адресации_р10.квс new file mode 100644 index 0000000..6961840 --- /dev/null +++ b/тесты/тест_адресации_р10/тест_адресации_р10.квс @@ -0,0 +1,29 @@ +;./тесты/тест_адресации_р10.elf "A" +;echo $? # должен быть 65 + +; ============================================================ +; тест_адресации_r10.квс - тест косвенной адресации через R10 +; ============================================================ + +.текст +.глобал _start + +_start: + ; Получаем argv[1] + вытолкнуть рсикс ; argc + вытолкнуть рсиай ; argv[0] + вытолкнуть р10 ; argv[1] в R10 + + ; Загружаем первый байт из argv[1] через R10 + загрузить ал, [р10] + + ; Сохраняем в переменную (не обязательно, но для проверки) + сохранить [байт], ал + + ; Выход с кодом = загруженный байт + переместить_имм раикс, 60 + загрузить рдиай, [байт] + вызов_системы + +.данные +байт: .байт 0 \ No newline at end of file diff --git a/тесты/тест_адресации_р11/тест_адресации_р11.py b/тесты/тест_адресации_р11/тест_адресации_р11.py new file mode 100644 index 0000000..0c05719 --- /dev/null +++ b/тесты/тест_адресации_р11/тест_адресации_р11.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Тест для проверки косвенной адресации через регистр R11 +Запускает скомпилированную программу с различными аргументами +и проверяет код возврата (должен быть равен ASCII коду первого символа) +""" + +import subprocess +import sys +import os +from pathlib import Path + +# Определяем пути +SCRIPT_DIR = Path(__file__).parent.absolute() +PROJECT_ROOT = SCRIPT_DIR.parent.parent +SOURCE_REL = SCRIPT_DIR / "тест_адресации_р11.квс" + +def clean(): + """Очищает артефакты сборки""" + files = [ + "тест_адресации_р11.elf", + "тест_адресации_р11.csv", + "тест_адресации_р11.аст", + "тест_адресации_р11.токены", + "тест_адресации_р11.константа", + "тест_адресации_р11_pass4.log" + ] + for f in files: + f_path = SCRIPT_DIR / f + if f_path.exists(): + f_path.unlink() + print(f"DEBUG: Удалён {f}", file=sys.stderr) + +def build() -> bool: + """ + Собирает тестовую программу с помощью КВС + + Returns: + bool: True если сборка успешна, иначе False + """ + print(f"DEBUG: Сборка {SOURCE_REL}", file=sys.stderr) + os.chdir(PROJECT_ROOT) + + build = subprocess.run( + ["python3", "квс_сборка.py", str(SOURCE_REL)], + capture_output=True, + text=True + ) + + if build.returncode != 0: + print(f"❌ Ошибка сборки: {build.returncode}", file=sys.stderr) + print(build.stderr, file=sys.stderr) + return False + + print("DEBUG: Сборка успешна", file=sys.stderr) + return True + +def test_with_char(char: str, expected_code: int) -> bool: + """ + Запускает скомпилированную программу с одним символом в качестве аргумента + и проверяет код возврата + + Args: + char: символ для передачи в программу + expected_code: ожидаемый ASCII код символа + + Returns: + bool: True если код возврата совпадает с ожидаемым, иначе False + """ + print(f"DEBUG: Тест с символом '{char}' (ожидается код {expected_code})", file=sys.stderr) + os.chdir(SCRIPT_DIR) + + elf_path = SCRIPT_DIR / "тест_адресации_р11.elf" + if not elf_path.exists(): + print("❌ ELF файл не найден", file=sys.stderr) + return False + + # Запускаем программу с аргументом + run = subprocess.run( + [str(elf_path), char], + capture_output=True, + text=True + ) + + # Проверяем код возврата (должен быть ASCII код символа) + if run.returncode != expected_code: + print(f"❌ Код возврата: ожидался {expected_code}, получен {run.returncode}", file=sys.stderr) + if run.stderr: + print(f" stderr: {run.stderr}", file=sys.stderr) + if run.stdout: + print(f" stdout: {run.stdout}", file=sys.stderr) + return False + + print(f"DEBUG: Тест с символом '{char}' пройден (код {run.returncode})", file=sys.stderr) + return True + +def main(): + """Главная функция теста""" + print("DEBUG: Начало теста тест_адресации_р11", file=sys.stderr) + + # Собираем программу + if not build(): + clean() + sys.exit(1) + + # Тестируем разные символы + tests = [ + ('A', 65), # Латинская A + ('B', 66), # Латинская B + ('0', 48), # Цифра 0 + ('z', 122), # Латинская z + ('!', 33), # Восклицательный знак + ('x', 120), # Латинская x + ('5', 53), # Цифра 5 + ('_', 95), # Подчёркивание + ('$', 36), # Знак доллара + ('@', 64), # Собака + ('#', 35), # Решётка + ('*', 42), # Звёздочка + ] + + all_passed = True + + for char, expected_code in tests: + if not test_with_char(char, expected_code): + all_passed = False + break + + # Очищаем артефакты + clean() + + if all_passed: + print("✅ Все тесты пройдены!", file=sys.stderr) + sys.exit(0) + else: + print("❌ Тест провален", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/тесты/тест_адресации_р11/тест_адресации_р11.квс b/тесты/тест_адресации_р11/тест_адресации_р11.квс new file mode 100644 index 0000000..ef9f7da --- /dev/null +++ b/тесты/тест_адресации_р11/тест_адресации_р11.квс @@ -0,0 +1,29 @@ +;./тесты/тест_адресации_р11.elf "A" +;echo $? # должен быть 65 + +; ============================================================ +; тест_адресации_r11.квс - тест косвенной адресации через R11 +; ============================================================ + +.текст +.глобал _start + +_start: + ; Получаем argv[1] + вытолкнуть рсикс ; argc + вытолкнуть рсиай ; argv[0] + вытолкнуть р11 ; argv[1] в R11 + + ; Загружаем первый байт из argv[1] через R11 + загрузить ал, [р11] + + ; Сохраняем в переменную (не обязательно, но для проверки) + сохранить [байт], ал + + ; Выход с кодом = загруженный байт + переместить_имм раикс, 60 + загрузить рдиай, [байт] + вызов_системы + +.данные +байт: .байт 0 \ No newline at end of file diff --git a/тесты/тест_адресации_р9/тест_адресации_р9.py b/тесты/тест_адресации_р9/тест_адресации_р9.py new file mode 100644 index 0000000..e506e29 --- /dev/null +++ b/тесты/тест_адресации_р9/тест_адресации_р9.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Тест для проверки косвенной адресации через регистр R9 +Запускает скомпилированную программу с различными аргументами +и проверяет код возврата (должен быть равен ASCII коду первого символа) +""" + +import subprocess +import sys +import os +from pathlib import Path + +# Определяем пути +SCRIPT_DIR = Path(__file__).parent.absolute() +PROJECT_ROOT = SCRIPT_DIR.parent.parent +SOURCE_REL = SCRIPT_DIR / "тест_адресации_р9.квс" + +def clean(): + """Очищает артефакты сборки""" + files = [ + "тест_адресации_р9.elf", + "тест_адресации_р9.csv", + "тест_адресации_р9.аст", + "тест_адресации_р9.токены", + "тест_адресации_р9.константа", + "тест_адресации_р9_pass4.log" + ] + for f in files: + f_path = SCRIPT_DIR / f + if f_path.exists(): + f_path.unlink() + print(f"DEBUG: Удалён {f}", file=sys.stderr) + +def build() -> bool: + """ + Собирает тестовую программу с помощью КВС + + Returns: + bool: True если сборка успешна, иначе False + """ + print(f"DEBUG: Сборка {SOURCE_REL}", file=sys.stderr) + os.chdir(PROJECT_ROOT) + + build = subprocess.run( + ["python3", "квс_сборка.py", str(SOURCE_REL)], + capture_output=True, + text=True + ) + + if build.returncode != 0: + print(f"❌ Ошибка сборки: {build.returncode}", file=sys.stderr) + print(build.stderr, file=sys.stderr) + return False + + print("DEBUG: Сборка успешна", file=sys.stderr) + return True + +def test_with_char(char: str, expected_code: int) -> bool: + """ + Запускает скомпилированную программу с одним символом в качестве аргумента + и проверяет код возврата + + Args: + char: символ для передачи в программу + expected_code: ожидаемый ASCII код символа + + Returns: + bool: True если код возврата совпадает с ожидаемым, иначе False + """ + print(f"DEBUG: Тест с символом '{char}' (ожидается код {expected_code})", file=sys.stderr) + os.chdir(SCRIPT_DIR) + + elf_path = SCRIPT_DIR / "тест_адресации_р9.elf" + if not elf_path.exists(): + print("❌ ELF файл не найден", file=sys.stderr) + return False + + # Запускаем программу с аргументом + run = subprocess.run( + [str(elf_path), char], + capture_output=True, + text=True + ) + + # Проверяем код возврата (должен быть ASCII код символа) + if run.returncode != expected_code: + print(f"❌ Код возврата: ожидался {expected_code}, получен {run.returncode}", file=sys.stderr) + if run.stderr: + print(f" stderr: {run.stderr}", file=sys.stderr) + if run.stdout: + print(f" stdout: {run.stdout}", file=sys.stderr) + return False + + print(f"DEBUG: Тест с символом '{char}' пройден (код {run.returncode})", file=sys.stderr) + return True + +def main(): + """Главная функция теста""" + print("DEBUG: Начало теста тест_адресации_р9", file=sys.stderr) + + # Собираем программу + if not build(): + clean() + sys.exit(1) + + # Тестируем разные символы + tests = [ + ('A', 65), # Латинская A + ('B', 66), # Латинская B + ('0', 48), # Цифра 0 + ('z', 122), # Латинская z + ('!', 33), # Восклицательный знак + ('x', 120), # Латинская x + ('5', 53), # Цифра 5 + ('_', 95), # Подчёркивание + ] + + all_passed = True + + for char, expected_code in tests: + if not test_with_char(char, expected_code): + all_passed = False + break + + # Очищаем артефакты + clean() + + if all_passed: + print("✅ Все тесты пройдены!", file=sys.stderr) + sys.exit(0) + else: + print("❌ Тест провален", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/тесты/тест_адресации_р9/тест_адресации_р9.квс b/тесты/тест_адресации_р9/тест_адресации_р9.квс new file mode 100644 index 0000000..ed8d68c --- /dev/null +++ b/тесты/тест_адресации_р9/тест_адресации_р9.квс @@ -0,0 +1,29 @@ +;./тесты/тест_адресации_р9.elf "A" +;echo $? # должен быть 65 + +; ============================================================ +; тест_адресации_r9.квс - тест косвенной адресации через R8 +; ============================================================ + +.текст +.глобал _start + +_start: + ; Получаем argv[1] + вытолкнуть рсикс ; argc + вытолкнуть рсиай ; argv[0] + вытолкнуть р9 ; argv[1] в R9 + + ; Загружаем первый байт из argv[1] через R9 + загрузить ал, [р9] + + ; Сохраняем в переменную (не обязательно, но для проверки) + сохранить [байт], ал + + ; Выход с кодом = загруженный байт + переместить_имм раикс, 60 + загрузить рдиай, [байт] + вызов_системы + +.данные +байт: .байт 0 \ No newline at end of file