From eaef835f4d2f736b92739465336e5bdbb924d5e1 Mon Sep 17 00:00:00 2001 From: mzhoot Date: Mon, 11 May 2026 15:08:03 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B0=D0=B1=D1=81=D0=BE=D0=BB=D1=8E=D1=82?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D0=B0=D0=B4=D1=80=D0=B5=D1=81=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.html | 242 ++++++- README.md | 100 ++- kvs_lexer.py | 70 +- kvs_parser.py | 79 ++- kvs_pass1.py | 175 ++++- kvs_pass2.py | 1013 +--------------------------- kvs_pass2_encoder.py | 1128 ++++++++++++++++++++++++++++++++ todo.txt | 307 ++++----- тесты/cleaner.py | 9 + тесты/test_label.квс | 15 + тесты/test_minimal.квс | 15 + тесты/test_success.квс | 28 + тесты/т1.квс | 66 ++ 13 files changed, 2017 insertions(+), 1230 deletions(-) create mode 100644 kvs_pass2_encoder.py create mode 100644 тесты/cleaner.py create mode 100644 тесты/test_label.квс create mode 100644 тесты/test_minimal.квс create mode 100644 тесты/test_success.квс create mode 100644 тесты/т1.квс diff --git a/README.html b/README.html index 6aefb13..eecfc81 100644 --- a/README.html +++ b/README.html @@ -209,6 +209,14 @@ color: #ff8888; } + .success { + border-left: 3px solid #44ff44; + padding: 15px; + margin: 20px 0; + background: rgba(0, 40, 0, 0.2); + color: #88ff88; + } + .process-diagram { text-align: center; margin: 30px 0; @@ -221,6 +229,22 @@ overflow-x: auto; } + .status-table td:first-child { + font-weight: bold; + } + + .status-done { + color: #00ff00; + } + + .status-partial { + color: #ffff00; + } + + .status-pending { + color: #ff4444; + } + footer { margin-top: 40px; padding-top: 20px; @@ -266,6 +290,16 @@ margin: 0; } + .badge { + display: inline-block; + padding: 2px 6px; + margin-left: 8px; + font-size: 10px; + border-radius: 2px; + background: #005500; + color: #00ff00; + } + @media (max-width: 768px) { body { padding: 10px; @@ -330,6 +364,7 @@

Версия 2.0 | Экспериментальный проект | 2025

+

✓ Абсолютная адресация реализована | ⟳ Косвенная адресация в разработке

┌──[ ОГЛАВЛЕНИЕ ]────────────────────────────────────────────┐

@@ -340,10 +375,13 @@
  • Установка и запуск
  • Синтаксис исходных файлов (.квс)
  • Система команд
  • +
  • Работа с памятью
  • Регистры процессора
  • Директивы ассемблера
  • Примеры программ
  • Процесс трансляции
  • +
  • Статус реализации
  • +
  • План развития
  • Ограничения
  • └──────────────────────────────────────────────────────────────┘

    @@ -367,8 +405,10 @@
  • Генерация исполняемых ELF-файлов — 64-битные ELF для Linux, готовые к запуску
  • Двухпроходная сборка — традиционная схема с разрешением меток
  • Поддержка секций — разделение на .текст (код) и .данные (данные)
  • +
  • Абсолютная адресация памяти — чтение и запись по меткам [переменная] и абсолютным адресам [0x402000] с RIP-relative кодированием
  • CSV-логирование — детальный файл .csv с соответствием адресов, байтов и исходных команд
  • Более 50 инструкций — полный набор основных команд x86-64
  • +
  • Рефакторинг — кодогенерация вынесена в отдельный модуль kvs_pass2_encoder.py
  • АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ

    @@ -384,7 +424,7 @@ AST-представление (.аст) ▼ (kvs_pass1.py) Первый проход: размеры, метки (.проход1) │ - ▼ (kvs_pass2.py) + ▼ (kvs_pass2.py + kvs_pass2_encoder.py) Второй проход: генерация кода (.csv) │ ▼ (kvs_builder.py) @@ -393,6 +433,19 @@ ELF-компоновщик (.elf) Исполняемый файл! +

    Модули компилятора

    + + + + + + + + + + +
    ФайлНазначение
    kvs_build.pyГлавный сборочный скрипт
    kvs_lexer.pyЛексический анализ, разбор на токены
    kvs_parser.pyСинтаксический анализ, построение AST
    kvs_pass1.pyПервый проход: вычисление размеров и адресов меток
    kvs_pass2.pyВторой проход: обход AST, генерация CSV
    kvs_pass2_encoder.pyКодировщик инструкций (все encode_* функции)
    kvs_builder.pyСборка ELF-файла из CSV
    kvs_data.pyОбщие данные (регистры, инструкции)
    +

    УСТАНОВКА И ЗАПУСК

    # Клонирование репозитория
     git clone https://github.com/username/kvs.git
    @@ -411,39 +464,78 @@ python3 kvs_build.py программа.квс
         
    ; Комментарий начинается с точки с запятой
     
     .данные                     ; Начало секции данных
    +    переменная: .байт 0     ; Переменная в памяти
         message: .строка_нуль "Привет, мир!"
    -    counter: .байт 0x0A
     
     .текст                      ; Начало секции кода
     .глобал _start              ; Точка входа
     
     _start:
    -    ; Аргументы для write(1, message, 13)
    +    ; Работа с памятью
    +    переместить_имм раикс, 42
    +    сохранить [переменная], раикс    ; запись в память
    +    
    +    загрузить рбикс, [переменная]    ; чтение из памяти
    +
    +    ; Системный вызов write
         переместить_имм раикс, 1
         загрузить_адрес рсиай, message
         переместить_имм рдикс, 13
         вызов_системы
     
    -    ; Выход с кодом 0
    +    ; Выход с кодом из переменной
         переместить_имм раикс, 60
    -    переместить_имм рдикс, 0
    +    загрузить рдиай, [переменная]
         вызов_системы

    СИСТЕМА КОМАНД

    - + + - - + + - - - - +
    КатегорияПримеры инструкцийСоответствие NASM
    Перемещение данныхпереместить, загрузить, сохранить, переместить_иммmov, lea
    Перемещение данныхпереместить, переместить_иммmov, mov reg, imm
    Работа с памятьюзагрузить, сохранить, загрузить_адресmov reg, mem, mov mem, reg, lea
    Арифметикаприбавить, вычесть, умножить, разделить, увеличить, уменьшитьadd, sub, mul, div, inc, dec
    Логическиеи, или, исключающее_или, инвертировать, отрицатьand, or, xor, not, neg
    Сравнение и проверкасравнить, сравнить_с, проверитьcmp, test
    Условные переходыпереход_если_равно, переход_если_больше, короткий_переход_если_равноje, jg, je short
    Сравнениесравнить, сравнить_с, проверитьcmp, test
    Условные переходыпереход_если_равно, переход_если_больше, короткий_переход_если_равноje, jg, je short
    Безусловные переходыпереход, короткий_переход, вызвать, вернутьсяjmp, jmp short, call, ret
    Стеквтолкнуть, вытолкнуть, втолкнуть_непосредственноpush, pop, push imm
    Сдвиги и вращениясдвиг_влево, сдвиг_вправо, вращать_влево, вращать_вправоshl, shr, rol, ror
    Без операндовнет_операции, вызов_системы, остановить, отладкаnop, syscall, hlt, int3
    Строковыепереместить_байт, сравнить_байтыmovsb, cmpsb
    Системныевызов_системы, остановитьsyscall, hlt
    +

    РАБОТА С ПАМЯТЬЮ

    +
    + ✓ РЕАЛИЗОВАНО (v2.0): Абсолютная адресация через метки и числа +
    + +

    Поддерживаемые формы:

    + + + + +
    СинтаксисОписаниеПример
    [метка]Адрес метки (из .данные)загрузить раикс, [переменная]
    [число]Абсолютный адрессохранить [0x402000], раикс
    + +

    Пример использования:

    +
    .данные
    +переменная: .байт 0
    +результат: .байт 0
    +
    +.текст
    +_start:
    +    ; Запись значения
    +    переместить_имм раикс, 42
    +    сохранить [переменная], раикс
    +    
    +    ; Чтение значения
    +    загрузить рбикс, [переменная]
    +    
    +    ; Завершение с кодом 42
    +    переместить_имм раикс, 60
    +    загрузить рдиай, [переменная]
    +    вызов_системы
    + +
    + ⚠️ ВАЖНО: Порядок объявления в секции .данные имеет значение! Переменные, объявленные перед строковыми константами, могут затереть первые байты строк. Рекомендуется объявлять строки первыми, а изменяемые переменные — после. +
    +

    РЕГИСТРЫ ПРОЦЕССОРА

    @@ -460,10 +552,10 @@ _start:

    ДИРЕКТИВЫ АССЕМБЛЕРА

    64-битные32-битные16-битные8-битные (мл.)8-битные (ст.)
    - - - - + + + + @@ -479,19 +571,37 @@ _start: _start: ; write(1, msg, 13) переместить_имм раикс, 1 - загрузить_адрес рсиай, msg + переместить_имм рдиай, 1 + переместить_имм рсиай, msg переместить_имм рдикс, 13 вызов_системы ; exit(0) переместить_имм раикс, 60 - переместить_имм рдикс, 0 + переместить_имм рдиай, 0 вызов_системы .данные -msg: .строка_нуль "Привет, мир!" +msg: .строка_нуль "Hello, World!" -

    Пример 2: Цикл для вычисления суммы

    +

    Пример 2: Работа с переменными в памяти

    +
    .глобал _start
    +.текст
    +
    +_start:
    +    ; Запись 42 в переменную
    +    переместить_имм раикс, 42
    +    сохранить [переменная], раикс
    +    
    +    ; Завершение с кодом 42
    +    переместить_имм раикс, 60
    +    загрузить рдиай, [переменная]
    +    вызов_системы
    +
    +.данные
    +переменная: .байт 0
    + +

    Пример 3: Цикл для вычисления суммы

    .глобал _start
     .текст
     
    @@ -523,13 +633,99 @@ loop:
             
  • программа.elf — исполняемый ELF-файл
  • +

    СТАТУС РЕАЛИЗАЦИИ

    + +

    ✅ Полностью реализовано (v2.0)

    +
      +
    • Лексер с токенами [, ], NUMBER
    • +
    • Парсер с поддержкой операндов в квадратных скобках
    • +
    • Первый проход с корректным расчётом размера инструкций с памятью
    • +
    • Второй проход с кодированием абсолютной адресации через RIP-relative
    • +
    • Поддержка меток в операндах памяти [переменная]
    • +
    • Рефакторинг: выделение кодировщика в отдельный модуль kvs_pass2_encoder.py
    • +
    • Более 50 инструкций x86-64
    • +
    • CSV-логирование
    • +
    + +

    ⚠️ Частично реализовано

    +
      +
    • загрузить_адрес (LEA) — только для чисел, не для меток
    • +
    • Сложные выражения в операндах памяти парсятся, но не кодируются
    • +
    + +

    ❌ В разработке (v3.0)

    +
      +
    • Косвенная адресация [reg]
    • +
    • Адресация со смещением [reg + disp]
    • +
    • Сложная адресация с индексом [reg + reg*scale]
    • +
    • Полная SIB-адресация [reg + reg*scale + disp]
    • +
    • Директива .bss для неинициализированных данных
    • +
    • LEA для меток (загрузить_адрес reg, [метка])
    • +
    • Стандартная библиотека
    • +
    + +

    ПЛАН РАЗВИТИЯ

    + +

    v2.0 (текущая) — Абсолютная адресация

    +
      +
    • ✅ Лексер: [, ], NUMBER
    • +
    • ✅ Парсер: [метка] и [число]
    • +
    • ✅ Pass1: размер = 7 байт
    • +
    • ✅ Pass2: RIP-relative кодирование
    • +
    • ✅ Поддержка меток в памяти
    • +
    • ✅ Рефакторинг энкодера
    • +
    + +

    v3.0 (планируется) — Косвенная и относительная адресация

    +
      +
    • ❌ [reg] — косвенная адресация через регистр
    • +
    • ❌ [reg + disp] — со смещением (disp8/disp32)
    • +
    • ❌ [reg + reg*scale] — с индексным регистром
    • +
    • ❌ [reg + reg*scale + disp] — полная SIB-адресация
    • +
    • ❌ .bss — секция неинициализированных данных
    • +
    • ❌ LEA для меток
    • +
    + +

    v4.0 (отложено) — Стандартная библиотека

    +
      +
    • ❌ malloc, free
    • +
    • ❌ strlen, strcmp, memcpy
    • +
    • ❌ putchar, getchar
    • +
    • ❌ printf (упрощённый вариант)
    • +
    + +
    +Зависимости между подзадачами: + +Лексер (шаг 1) ✅ + ↓ +Парсер (шаг 2) ✅ + ↓ +Данные инструкций (шаг 3) ⚠️ + ↓ +Pass1 (шаг 4) ✅ + ↓ +Pass2 (шаг 5): ⚠️ ЧАСТИЧНО + 5.1 → 5.2 → 5.3 → 5.4 → 5.5 + ↓ + 5.6 (генераторы инструкций) + ↓ + 5.7 (диспетчер) ✅ + ↓ +Тестирование (шаг 6) ⚠️ ЧАСТИЧНО + ↓ +Библиотека (шаг 7) ❌ ОТЛОЖЕНО +
    +

    ОГРАНИЧЕНИЯ

      -
    • Сложная адресация памяти — в текущей версии поддерживаются только простые формы [регистр]; полная поддержка [база + индекс*масштаб + смещение] в разработке
    • -
    • Отсутствие динамической памяти — нет встроенных malloc/free (можно реализовать через syscall brk)
    • +
    • Косвенная адресация памяти — поддерживается только абсолютная форма [метка]; адресация через регистры [reg] в разработке
    • +
    • Сложная адресация — формы [reg+disp], [reg+reg*scale], [reg+reg*scale+disp] не реализованы
    • +
    • LEA для меток — загрузить_адрес reg, [метка] не работает, используйте переместить_имм reg, метка
    • +
    • Отсутствие .bss — все переменные инициализируются и попадают в .data (занимают место в файле)
    • +
    • Нет динамической памяти — нет встроенных malloc/free (можно реализовать через syscall brk)
    • Нет макросов и условной компиляции
    • -
    • Нет 32-битного режима — только 64-битные ELF
    • Экспериментальный статус — некоторые инструкции могут быть реализованы не полностью
    diff --git a/README.md b/README.md index 77fd31d..8432c84 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ ```markdown -# КВС (Компилятор B Семейства) — Ассемблер с русским синтаксисом для x86-64 - -[![Version](https://img.shields.io/badge/version-2.0-green.svg)](https://github.com/username/kvs) -[![Python](https://img.shields.io/badge/python-3.6+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) +# КВС (Компилятор В Семейства) — Ассемблер с русским синтаксисом для x86-64 **КВС** — это экспериментальный ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит образовательный характер и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код. @@ -20,6 +16,7 @@ - [Примеры программ](#-примеры-программ) - [Отладка](#-отладка-и-проверка) - [Ограничения](#-ограничения) +- [План развития](#-план-развития) - [Лицензия](#-лицензия) ## ✨ Особенности @@ -28,9 +25,11 @@ - **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску - **Двухпроходная сборка** — традиционная схема с разрешением меток - **Поддержка секций** — разделение на `.текст` (код) и `.данные` (данные) +- **Абсолютная адресация памяти** — чтение и запись по меткам `[переменная]` и абсолютным адресам `[0x402000]` с RIP-relative кодированием - **CSV-логирование** — детальный файл с соответствием адресов, байтов и исходных команд - **Более 50 инструкций** — полный набор основных команд x86-64 - **Минимализм зависимостей** — только стандартная библиотека Python +- **Рефакторинг** — кодогенерация вынесена в отдельный модуль `kvs_pass2_encoder.py` ## 🏗 Архитектура @@ -48,7 +47,7 @@ AST-представление (.аст) ▼ (kvs_pass1.py) Первый проход: размеры, метки (.проход1) │ - ▼ (kvs_pass2.py) + ▼ (kvs_pass2.py + kvs_pass2_encoder.py) Второй проход: генерация кода (.csv) │ ▼ (kvs_builder.py) @@ -65,7 +64,8 @@ ELF-компоновщик (.elf) | `kvs_lexer.py` | Лексический анализ, разбор на токены | | `kvs_parser.py` | Синтаксический анализ, построение AST | | `kvs_pass1.py` | Первый проход: вычисление размеров и адресов меток | -| `kvs_pass2.py` | Второй проход: генерация машинного кода | +| `kvs_pass2.py` | Второй проход: обход AST, генерация CSV | +| `kvs_pass2_encoder.py` | Кодировщик инструкций (все encode_* функции) | | `kvs_builder.py` | Сборка ELF-файла из CSV | | `kvs_data.py` | Общие данные (регистры, инструкции) | @@ -156,6 +156,20 @@ _start: ; метка _start переместить_имм раикс, 42 ; комментарий после инструкции ``` +### Работа с памятью (абсолютная адресация) + +```asm +; Запись значения в память по метке +переместить_имм раикс, 42 +сохранить [переменная], раикс + +; Чтение значения из памяти +загрузить рбикс, [переменная] + +; Запись по абсолютному адресу +сохранить [0x402000], раикс +``` + ## 📖 Система команд ### Загрузка констант @@ -170,6 +184,19 @@ _start: ; метка _start переместить_имм ал, 0xFF ; AL = 255 ``` +### Работа с памятью + +| Инструкция | Синтаксис | Описание | +|------------|-----------|----------| +| `загрузить` | `загрузить рег, [метка/адрес]` | Загрузить значение из памяти | +| `сохранить` | `сохранить [метка/адрес], рег` | Сохранить значение в память | + +```asm +загрузить раикс, [переменная] ; RAX = *переменная +сохранить [результат], раикс ; *результат = RAX +загрузить рбикс, [0x402000] ; загрузка по абсолютному адресу +``` + ### Арифметика и логика | Инструкция | Синтаксис | Описание | @@ -250,6 +277,29 @@ _start: ; метка _start ## 📝 Примеры программ +### Работа с памятью (переменные) + +```asm +.текст +.глобал _start + +_start: + ; Запись значения в переменную + переместить_имм раикс, 42 + сохранить [переменная], раикс + + ; Чтение значения из переменной + загрузить рбикс, [переменная] + + ; Завершение с кодом возврата + переместить_имм раикс, 60 + загрузить рдиай, [переменная] + вызов_системы + +.данные +переменная: .байт 0 +``` + ### Цикл и условный переход ```asm @@ -263,7 +313,7 @@ loop: переместить_имм раикс, 1 переместить_имм рдиай, 1 переместить_имм рсиай, msg - переместить_имм рдикс, len_msg + переместить_имм рдикс, 10 вызов_системы уменьшить рсикс ; счетчик-- @@ -275,8 +325,7 @@ loop: вызов_системы .данные -msg: .строка_нуль "Iteration\n" - .константа len_msg = 10 +msg: .строка "Iteration\n" ``` ### Вычисление суммы чисел от 1 до 10 @@ -365,15 +414,38 @@ gdb ./программа.elf - ✅ Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`) - ✅ Директива `.байт` для raw-данных - ✅ Директива `.константа` для имён констант +- ✅ **Абсолютная адресация памяти** `[метка]` и `[число]` (RIP-relative) -### Не поддерживается +### Не поддерживается (в разработке) -- ❌ Косвенная адресация памяти `[reg+disp]` (в разработке) -- ❌ Инструкции для работы со стеком (PUSH, POP, CALL, RET) +- ❌ Косвенная адресация `[reg]` — доступ к памяти через регистр +- ❌ Адресация со смещением `[reg + disp]` +- ❌ Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация +- ❌ Инструкции для работы со стеком (PUSH, POP, CALL, RET) — частично - ❌ Поддержка плавающей запятой (FPU/SSE) - ❌ Многомодульная компиляция +- ❌ Директива `.bss` для неинициализированных данных - ❌ Макросы +## 📈 План развития + +### Реализовано (v2.0) +- [x] Лексер с токенами `[`, `]`, `NUMBER` +- [x] Парсер с поддержкой операндов в квадратных скобках +- [x] Первый проход с корректным расчётом размера инструкций с памятью +- [x] Второй проход с кодированием абсолютной адресации через RIP-relative +- [x] Поддержка меток в операндах памяти `[переменная]` +- [x] Рефакторинг: выделение кодировщика в отдельный модуль + +### В планах (v3.0) +- [ ] Косвенная адресация `[reg]` +- [ ] Адресация со смещением `[reg + disp]` +- [ ] Сложная адресация с индексом `[reg + reg*scale]` +- [ ] Полная SIB-адресация `[reg + reg*scale + disp]` +- [ ] Директива `.bss` для неинициализированных данных +- [ ] Поддержка инструкций PUSH/POP/CALL/RET для стековых операций +- [ ] Стандартная библиотека (`libkvs.квс`): malloc, free, strlen, strcmp, print + ## 📄 Лицензия Только для образовательных целей. Используйте на свой страх и риск. @@ -381,6 +453,4 @@ gdb ./программа.elf ``` КВС Ассемблер | Экспериментальный проект | 2025 Только для образовательных целей. Используйте на свой страх и риск. -``` - ``` \ No newline at end of file diff --git a/kvs_lexer.py b/kvs_lexer.py index 5452d59..d48c487 100644 --- a/kvs_lexer.py +++ b/kvs_lexer.py @@ -6,15 +6,34 @@ Принимает: исходный .квс файл Выдаёт: текстовый файл с токенами (по одному токену на строку) Формат вывода: ТИП:ЗНАЧЕНИЕ + +Поддерживает сложную адресацию: +- [ ] - квадратные скобки +- + - плюс +- - - минус +- * - звёздочка (умножение/масштаб) +- NUMBER - числа (десятичные, шестнадцатеричные, отрицательные) """ 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 + +def is_dec_number(s): + """Проверяет, является ли строка десятичным числом (возможно с минусом)""" + if s.startswith('-'): + s = s[1:] + return s.isdigit() + def tokenize_line(line, line_num): """Разбирает строку в токены""" + # Удаляем комментарии semi = line.find(';') if semi != -1: line = line[:semi] @@ -25,18 +44,21 @@ def tokenize_line(line, line_num): 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: - # Оставляем escape-последовательности как есть s += '\\' + line[i + 1] i += 2 else: @@ -48,6 +70,7 @@ def tokenize_line(line, line_num): tokens.append(('STRING', s)) continue + # Одиночные символы-разделители if ch == ',': tokens.append(('COMMA', ',')) i += 1 @@ -58,12 +81,53 @@ def tokenize_line(line, line_num): i += 1 continue + # НОВЫЕ ТОКЕНЫ ДЛЯ СЛОЖНОЙ АДРЕСАЦИИ + if ch == '[': + tokens.append(('LBRACKET', '[')) + i += 1 + continue + + if ch == ']': + tokens.append(('RBRACKET', ']')) + i += 1 + continue + + if ch == '+': + tokens.append(('PLUS', '+')) + i += 1 + 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 + + if ch == '*': + tokens.append(('STAR', '*')) + i += 1 + continue + + # Слова, числа, метки j = i - while j < n and not (line[j].isspace() or line[j] in ',:;'): + while j < n and not (line[j].isspace() or line[j] in ',:;[]+*-'): j += 1 word = line[i:j] + if word: - tokens.append(('WORD', word)) + # Определяем тип токена + if is_hex_number(word): + tokens.append(('NUMBER', word)) + elif is_dec_number(word): + tokens.append(('NUMBER', word)) + else: + tokens.append(('WORD', word)) + i = j return tokens diff --git a/kvs_parser.py b/kvs_parser.py index 32bb015..c04be21 100644 --- a/kvs_parser.py +++ b/kvs_parser.py @@ -5,6 +5,9 @@ Парсер КВС Принимает: файл токенов от лексера Выдаёт: текстовый файл с AST-структурой + +Поддерживает сложную адресацию: +- [регистр + регистр*масштаб + смещение] """ import sys @@ -51,6 +54,13 @@ class Parser: raise ValueError(f"Ожидался {expected_type}, получено {tok}") return tok + def expect_one_of(self, expected_types): + """Ожидает один из нескольких типов токенов""" + tok = self.peek() + if tok is None or tok[0] not in expected_types: + raise ValueError(f"Ожидался один из {expected_types}, получено {tok}") + return self.next_token() + def parse_line(self): tok = self.peek() if tok is None: @@ -63,6 +73,7 @@ class Parser: return False if tok[0] == 'WORD': + # Проверяем, есть ли после слова двоеточие (метка) if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'COLON': label_name = self.next_token()[1] self.next_token() # COLON @@ -107,10 +118,12 @@ class Parser: self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}") elif word == '.константа': name = self.expect('WORD')[1] + # Пропускаем '=', если есть eq_tok = self.peek() if eq_tok and eq_tok[0] == 'WORD' and eq_tok[1] == '=': self.next_token() - value_tok = self.expect('WORD') + # Значение может быть WORD или NUMBER + value_tok = self.expect_one_of(['WORD', 'NUMBER']) self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}") elif word == '.байт': bytes_list = [] @@ -122,11 +135,71 @@ class Parser: else: break else: - bytes_list.append(self.next_token()[1]) + # Может быть WORD (метка) или NUMBER (число) + val_tok = self.expect_one_of(['WORD', 'NUMBER', 'STRING']) + bytes_list.append(val_tok[1]) self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list)) else: raise ValueError(f"Неизвестная директива: {word}") + def parse_operand(self): + """ + Разбирает один операнд. + Возвращает строковое представление операнда. + Поддерживает: + - регистры + - числа + - метки + - строки + - адресацию в квадратных скобках + """ + tok = self.peek() + if tok is None: + return None + + if tok[0] == 'LBRACKET': + return self.parse_memory_operand() + elif tok[0] == 'NUMBER': + return self.next_token()[1] + elif tok[0] == 'STRING': + return self.next_token()[1] + elif tok[0] == 'WORD': + return self.next_token()[1] + else: + raise ValueError(f"Неожиданный токен в операнде: {tok}") + + def parse_memory_operand(self): + """ + Разбирает операнд памяти вида: + [регистр] + [число] + [регистр + смещение] + [регистр + регистр*масштаб] + [регистр + регистр*масштаб + смещение] + """ + self.expect('LBRACKET') + + parts = [] + + # Разбираем содержимое до закрывающей скобки + while True: + tok = self.peek() + if tok is None: + raise ValueError("Незакрытая квадратная скобка") + + if tok[0] == 'RBRACKET': + self.next_token() + break + + if tok[0] in ('WORD', 'NUMBER', 'PLUS', 'MINUS', 'STAR'): + parts.append(self.next_token()[1]) + else: + raise ValueError(f"Неожиданный токен в адресации: {tok}") + + # Собираем выражение + expr = ' '.join(parts) + return f"[{expr}]" + def parse_instruction(self, mnemonic): if mnemonic not in INSTRUCTIONS: raise ValueError(f"Неизвестная инструкция: {mnemonic}") @@ -139,7 +212,7 @@ class Parser: if tok[0] == 'COMMA': self.next_token() continue - operands.append(self.next_token()[1]) + operands.append(self.parse_operand()) self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(operands)) diff --git a/kvs_pass1.py b/kvs_pass1.py index 241ceb9..070e0bd 100644 --- a/kvs_pass1.py +++ b/kvs_pass1.py @@ -7,8 +7,9 @@ """ import sys +import re sys.path.insert(0, '.') -from kvs_data import PAGE_SIZE, text_vaddr_base, data_vaddr_base, align_up +from kvs_data import PAGE_SIZE, text_vaddr_base, data_vaddr_base, align_up, INSTRUCTIONS def unescape_string(s): """Преобразует escape-последовательности в реальные символы""" @@ -38,6 +39,113 @@ def read_ast(input_file): ast_lines.append(line.strip()) return ast_lines +def parse_memory_operand(operand_str): + """ + Анализирует операнд памяти вида [reg + reg*scale + disp] + Возвращает словарь с информацией: + - has_base: bool + - base_reg: str or None + - has_index: bool + - index_reg: str or None + - scale: int (1,2,4,8) + - has_disp: bool + - disp_value: int (если есть) + - disp_size: 1 или 4 + """ + if not operand_str.startswith('[') or not operand_str.endswith(']'): + return None + + content = operand_str[1:-1].strip() + if not content: + return None + + result = { + 'has_base': False, + 'base_reg': None, + 'has_index': False, + 'index_reg': None, + 'scale': 1, + 'has_disp': False, + 'disp_value': 0, + 'disp_size': 0 + } + + # Проверяем, не является ли содержимое просто числом (абсолютный адрес) + 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','') == ''): + result['has_disp'] = True + if content.startswith('0x'): + result['disp_value'] = int(content, 16) + else: + result['disp_value'] = int(content) + result['disp_size'] = 4 + return result + + # Простой регистр без смещения + if content in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', + 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']: + result['has_base'] = True + result['base_reg'] = content + return result + + # Разбираем выражение: регистр [+- регистр[*масштаб] [+- смещение]] + parts = re.split(r'([+\-])', content) + + for part in parts: + part = part.strip() + if not part or part in '+-': + continue + + if part in ['раикс', 'рбикс', 'рсикс', 'рдикс', 'рсипи', 'рбипи', 'рсиай', 'рдиай', + 'р8', 'р9', 'р10', 'р11', 'р12', 'р13', 'р14', 'р15']: + 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): + result['has_disp'] = True + if 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) + + if result['has_disp']: + if -128 <= result['disp_value'] <= 127: + result['disp_size'] = 1 + else: + result['disp_size'] = 4 + + return result + +def estimate_memory_operand_size(operand_str): + """Оценивает размер операнда памяти в байтах (ModR/M + SIB + disp)""" + if not operand_str: + return 0 + + addr_mode = parse_memory_operand(operand_str) + if addr_mode is None: + return 0 + + size = 1 + + if addr_mode['has_index']: + size += 1 + + if addr_mode['has_disp']: + size += addr_mode['disp_size'] + + return size + class Pass1: def __init__(self): self.labels = {} @@ -68,11 +176,11 @@ class Pass1: self.position[".data"] += size elif directive == '.константа': name = parts[2] - value = parts[3] - if value.isdigit(): - self.symbols[name] = int(value) - elif value.startswith('0x'): - self.symbols[name] = int(value, 16) + value_str = parts[3] + if value_str.isdigit(): + self.symbols[name] = int(value_str) + elif value_str.startswith('0x'): + self.symbols[name] = int(value_str, 16) else: self.symbols[name] = 0 elif directive == '.байт': @@ -90,10 +198,27 @@ class Pass1: elif line_type == "INSTR": mnemonic = parts[1] sec = parts[2] - size = self.estimate_size(mnemonic) + operands_str = parts[3] if len(parts) > 3 else "" + operands = operands_str.split(',') if operands_str else [] + size = self.estimate_size(mnemonic, operands) self.position[sec] += size - def estimate_size(self, mnemonic): + def estimate_size(self, mnemonic, operands): + """Динамический расчёт размера инструкции""" + + # Инструкции с памятью (загрузить, сохранить, загрузить_адрес) + if mnemonic in ("загрузить", "сохранить", "загрузить_адрес"): + # RIP-relative: REX (1) + opcode (1) + ModR/M (1) + disp32 (4) = 7 + return 7 + + # Инструкции без операндов + if mnemonic in ("вызов_системы", "нет_операции", "вернуться", "остановить", "отладка"): + instr = INSTRUCTIONS.get(mnemonic) + if instr and "opcode" in instr: + return len(instr["opcode"]) + return 2 + + # Таблица фиксированных размеров size_map = { "переместить_имм": 10, "сравнить_с": 7, @@ -127,6 +252,40 @@ class Pass1: "прибавить": 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) diff --git a/kvs_pass2.py b/kvs_pass2.py index f6dabc8..c5b832f 100644 --- a/kvs_pass2.py +++ b/kvs_pass2.py @@ -9,7 +9,8 @@ import sys import struct sys.path.insert(0, '.') -from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info, PAGE_SIZE, align_up +from kvs_data import PAGE_SIZE, align_up +from kvs_pass2_encoder import encode_instruction, parse_operand def unescape_string(s): @@ -81,1016 +82,6 @@ def read_pass1(input_file): return data, labels, label_sections, symbols -def parse_operand(operand, labels, label_sections, symbols, vaddr_text, vaddr_data): - """ - Вычисляет значение операнда. - Поддерживает: - - десятичные числа (123) - - шестнадцатеричные (0x7B) - - метки (имя_метки) - - константы (из .константа) - """ - if operand.isdigit(): - return int(operand) - if operand.startswith("0x"): - return int(operand, 16) - if operand in labels: - section = label_sections[operand] - base = vaddr_text if section == ".text" else vaddr_data - return labels[operand] + base - if operand in symbols: - return symbols[operand] - raise ValueError("Неизвестный операнд: " + operand) - - -# ========== ГЕНЕРАТОРЫ МАШИННОГО КОДА ДЛЯ КАЖДОЙ ГРУППЫ ИНСТРУКЦИЙ ========== - -# ----- 1. Инструкции перемещения данных (MOV, LEA, MOVZX, MOVSX) ----- - -def encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data): - """ - MOV reg, imm - переместить непосредственное значение в регистр - Формат: переместить_имм <регистр>, <значение|метка|константа> - """ - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - if size == 64: - if 0 <= reg <= 7: - code.extend(b'\x48') - code.append(0xB8 + reg) - elif 8 <= reg <= 15: - code.extend(b'\x49') - code.append(0xB8 + (reg - 8)) - code.extend(struct.pack('= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(0xC7) - modrm = 0xC0 | (reg & 7) - code.append(modrm) - code.extend(struct.pack('= 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) - elif size == 8: - 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 - - -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"] - - # Формируем REX префикс - 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 - - -def encode_lea(operands, labels, label_sections, symbols, vaddr_text, vaddr_data): - """ - LEA reg, mem - загрузить эффективный адрес - Формат: загрузить_адрес <регистр>, <адрес> (упрощённо - пока только метки) - """ - code = bytearray() - instr = INSTRUCTIONS["загрузить_адрес"] - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - # Формируем REX префикс - rex = 0x48 if reg_info["size"] == 64 else 0x40 - if reg >= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - - code.extend(instr["opcode"][-1:]) - # ModR/M: mod=00 (disp32), reg=reg, r/m=101 (RIP-relative) - # Упрощённо: используем абсолютный адрес через disp32 - modrm = 0x05 | ((reg & 7) << 3) - code.append(modrm) - code.extend(struct.pack(', <адрес_байта> - """ - code = bytearray() - instr = INSTRUCTIONS["переместить_с_нулями"] - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - rex = 0x48 if reg_info["size"] == 64 else 0x40 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.extend(instr["opcode"]) - modrm = 0x05 | ((reg & 7) << 3) - code.append(modrm) - code.extend(struct.pack(', <адрес_байта> - """ - code = bytearray() - instr = INSTRUCTIONS["переместить_со_знаком"] - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - rex = 0x48 if reg_info["size"] == 64 else 0x40 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.extend(instr["opcode"]) - modrm = 0x05 | ((reg & 7) << 3) - code.append(modrm) - code.extend(struct.pack(', <значение|метка|константа> - """ - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - if size == 64: - rex = 0x48 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.append(0x81) - modrm = 0xF8 | (reg & 7) - code.append(modrm) - code.extend(struct.pack('= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(0x83) - modrm = 0xF8 | (reg & 7) - code.append(modrm) - code.append(imm & 0xFF) - else: - 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(struct.pack('= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(0x81) - modrm = 0xF8 | (reg & 7) - code.append(modrm) - code.extend(struct.pack('= 8: - rex |= 0x01 - code.append(rex) - code.append(0x80) - modrm = 0xF8 | (reg & 7) - code.append(modrm) - code.append(imm & 0xFF) - - 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) ----- - -def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data): - """ - ADD/SUB reg, imm - прибавить/вычесть непосредственное значение - Формат: прибавить_непосредственно / вычесть_непосредственно <регистр>, <значение> - """ - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) - - op_map = { - "прибавить_непосредственно": 0x81, - "вычесть_непосредственно": 0x81, - } - op = op_map.get(mnemonic, 0x81) - subop = 0 if mnemonic == "прибавить_непосредственно" else 5 # /0 для ADD, /5 для SUB - - if size == 64: - rex = 0x48 - if reg >= 8: - rex |= 0x01 - code.append(rex) - code.append(op) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - code.extend(struct.pack('= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(op) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - code.append(imm & 0xFF) - else: - rex = 0x40 - if reg >= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(op) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - code.extend(struct.pack('= 8: - rex |= 0x01 - if rex != 0x40: - code.append(rex) - code.append(op) - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - code.extend(struct.pack(', <регистр_источник> - """ - 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) # REX.W для 64-бит - code.extend(instr["opcode"]) - modrm = 0xC0 | (subop << 3) | 0 # операнд в регистре (например, RBX, RCX) - 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. Инструкции переходов и вызовов (JMP, Jcc, CALL, RET, LOOP) ----- - -def encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): - """ - JMP rel32 - безусловный переход с 32-битным смещением - Формат: переход <метка> - """ - code = bytearray() - instr = INSTRUCTIONS["переход"] - code.extend(instr["opcode"]) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 5) - code.extend(struct.pack(' - """ - code = bytearray() - code.append(0xEB) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 2) - if not (-128 <= offset <= 127): - raise ValueError(f"Смещение короткого перехода {offset} вне диапазона [-128..127]") - code.append(offset & 0xFF) - return code - - -def encode_jcc_rel32(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): - """ - Jcc rel32 - условный переход с 32-битным смещением - Формат: переход_если_... <метка> - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - code.extend(instr["opcode"]) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 6) - code.extend(struct.pack(' - """ - code = bytearray() - instr = INSTRUCTIONS[mnemonic] - code.extend(instr["opcode"]) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 2) - if not (-128 <= offset <= 127): - raise ValueError(f"Смещение короткого перехода {offset} вне диапазона [-128..127]") - code.append(offset & 0xFF) - return code - - -def encode_call(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): - """ - CALL rel32 - вызов процедуры - Формат: вызвать <метка> - """ - code = bytearray() - instr = INSTRUCTIONS["вызвать"] - code.extend(instr["opcode"]) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 5) - code.extend(struct.pack(' - """ - code = bytearray() - instr = INSTRUCTIONS["цикл"] - code.extend(instr["opcode"]) - target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) - offset = target - (vaddr_text + current_pos + 2) - if not (-128 <= offset <= 127): - raise ValueError(f"Смещение LOOP {offset} вне диапазона [-128..127]") - code.append(offset & 0xFF) - return code - - -# ----- 6. Стековые инструкции (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) # PUSH imm8 - code.append(imm & 0xFF) - else: - code.append(0x68) # PUSH imm32 - code.extend(struct.pack(', <количество> - """ - code = bytearray() - reg_info = get_reg_info(operands[0]) - reg = reg_info["index"] - size = reg_info["size"] - shift = int(operands[1]) if operands[1].isdigit() else int(operands[1], 16) - - # REX.W только для 64-битных регистров - if size == 64: - code.append(0x48) - - # Опкод C1 для сдвига с imm8 - code.append(0xC1) - - # subop в зависимости от типа - subop_map = { - "сдвиг_влево": 4, # SHL = /4 - "сдвиг_вправо": 5, # SHR = /5 - "сдвиг_арифметический_вправо": 7, # SAR = /7 - "вращать_влево": 0, # ROL = /0 - "вращать_вправо": 1, # ROR = /1 - } - subop = subop_map.get(mnemonic, 0) - - # ModR/M: mod=11, reg=subop, r/m=reg - modrm = 0xC0 | (subop << 3) | (reg & 7) - code.append(modrm) - code.append(shift & 0xFF) - - return bytes(code) -# ----- 8. Системные и отладочные инструкции ----- - -def encode_syscall(): - """SYSCALL - системный вызов""" - return INSTRUCTIONS["вызов_системы"]["opcode"] - - -def encode_int(operands): - """ - INT imm8 - прерывание - Формат: прервать <номер> - """ - 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(): - """HLT - остановить процессор""" - return INSTRUCTIONS["остановить"]["opcode"] - - -def encode_int3(): - """INT3 - точка останова (отладка)""" - return INSTRUCTIONS["отладка"]["opcode"] - - -def encode_nop(): - """NOP - нет операции""" - return INSTRUCTIONS["нет_операции"]["opcode"] - - -# ----- 9. Инструкции работы с флагами ----- - -def encode_flag_instruction(mnemonic): - """STC/CLC/STD/CLD/PUSHF/POPF - инструкции работы с флагами""" - return INSTRUCTIONS[mnemonic]["opcode"] - - -# ----- 10. Ввод-вывод ----- - -def encode_in(operands): - """ - IN imm8 - ввод байта из порта - Формат: ввод_байта <порт> - """ - 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): - """ - OUT imm8 - вывод байта в порт - Формат: вывод_байта <порт> - """ - 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): - """MOVSB/MOVSW/CMPSB/SCASB - строковые инструкции""" - return INSTRUCTIONS[mnemonic]["opcode"] - - -# ----- 12. Инструкции идентификации процессора ----- - -def encode_cpuid(): - """CPUID - идентификация процессора""" - return INSTRUCTIONS["идентифицировать_процессор"]["opcode"] - - -def encode_rdtsc(): - """RDTSC - чтение счётчика времени""" - return INSTRUCTIONS["прочитать_счётчик"]["opcode"] - - -# ----- 13. Инструкции с памятью (упрощённые) ----- - -def encode_xchg(operands): - """ - XCHG reg, reg - обменять регистры - Формат: обменять <регистр1>, <регистр2> - """ - 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): - """ - Главный диспетчер генерации машинного кода. - По мнемонике выбирает соответствующую функцию генерации. - """ - - # ----- Инструкции без операндов ----- - if mnemonic == "вызов_системы": - return encode_syscall() - elif mnemonic == "нет_операции": - return encode_nop() - elif mnemonic == "вернуться": - return encode_ret() - elif mnemonic == "остановить": - return encode_hlt() - elif mnemonic == "отладка": - return encode_int3() - elif mnemonic in ("установить_перенос", "сбросить_перенос", "установить_направление", - "сбросить_направление", "втолкнуть_флаги", "вытолкнуть_флаги"): - return encode_flag_instruction(mnemonic) - elif mnemonic in ("переместить_байт", "переместить_слово", "сравнить_байты", "сканировать_байт"): - return encode_string_instruction(mnemonic) - elif mnemonic == "идентифицировать_процессор": - return encode_cpuid() - elif mnemonic == "прочитать_счётчик": - return encode_rdtsc() - - # ----- MOV (разные формы) ----- - elif mnemonic == "переместить_имм": - return encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - elif mnemonic == "переместить": - return encode_mov_reg_reg(operands) - elif mnemonic == "загрузить_байт": - return encode_mov_reg8_imm8(operands) - elif mnemonic == "загрузить_адрес": - return encode_lea(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - elif mnemonic == "переместить_с_нулями": - return encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - elif mnemonic == "переместить_со_знаком": - return encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - - # ----- CMP (сравнение) ----- - elif mnemonic == "сравнить_с": - return encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - elif mnemonic in ("сравнить", "сравнить_байт", "проверить"): - return encode_cmp_reg_reg(mnemonic, operands) - - # ----- ADD/SUB (арифметика) ----- - elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно"): - return encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data) - elif mnemonic in ("прибавить", "вычесть", "прибавить_байт", "вычесть_байт"): - return encode_add_sub_reg_reg(mnemonic, operands) - - # ----- MUL/DIV ----- - elif mnemonic in ("умножить", "умножить_знаковое", "разделить", "разделить_знаковое"): - return encode_muldiv(mnemonic) - - # ----- AND/OR/XOR ----- - elif mnemonic in ("и", "или", "исключающее_или"): - return encode_and_or_xor_reg_reg(mnemonic, operands) - - # ----- NOT/NEG ----- - elif mnemonic in ("инвертировать", "отрицать"): - return encode_not_neg(mnemonic, operands) - - # ----- INC/DEC ----- - elif mnemonic in ("увеличить", "уменьшить"): - return encode_incdec(mnemonic, operands) - - # ----- Переходы ----- - elif mnemonic == "переход": - return encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - elif mnemonic == "короткий_переход": - return encode_jmp_short(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - elif mnemonic in ("переход_если_равно", "переход_если_неравно", "переход_если_меньше", - "переход_если_больше", "переход_если_меньше_или_равно", "переход_если_больше_или_равно", - "переход_если_перенос", "переход_если_нет_переноса", "переход_если_ноль", "переход_если_не_ноль"): - return encode_jcc_rel32(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - elif mnemonic in ("короткий_переход_если_равно", "короткий_переход_если_неравно", - "короткий_переход_если_меньше", "короткий_переход_если_больше", - "короткий_переход_если_меньше_или_равно", "короткий_переход_если_больше_или_равно", - "короткий_переход_если_перенос", "короткий_переход_если_нет_переноса", - "короткий_переход_если_ноль", "короткий_переход_если_не_ноль"): - return encode_jcc_short(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - - # ----- CALL/RET/LOOP ----- - elif mnemonic == "вызвать": - return encode_call(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - elif mnemonic == "цикл": - return encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) - - # ----- PUSH/POP ----- - elif mnemonic == "втолкнуть": - return encode_push_reg(operands) - elif mnemonic == "вытолкнуть": - return encode_pop_reg(operands) - elif mnemonic == "втолкнуть_непосредственно": - return encode_push_imm(operands) - - # ----- Битовые операции ----- - elif mnemonic in ("сдвиг_влево", "сдвиг_вправо", "сдвиг_арифметический_вправо", - "вращать_влево", "вращать_вправо"): - return encode_shift_rotate(mnemonic, operands) - - # ----- Ввод-вывод ----- - elif mnemonic == "ввод_байта": - return encode_in(operands) - elif mnemonic == "вывод_байта": - return encode_out(operands) - - # ----- Прочие ----- - elif mnemonic == "прервать": - return encode_int(operands) - elif mnemonic == "обменять": - return encode_xchg(operands) - - else: - raise NotImplementedError(f"Инструкция '{mnemonic}' не реализована") - - -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) - - # ========== ОСНОВНОЙ КЛАСС ВТОРОГО ПРОХОДА ========== class Pass2: diff --git a/kvs_pass2_encoder.py b/kvs_pass2_encoder.py new file mode 100644 index 0000000..5a62fb9 --- /dev/null +++ b/kvs_pass2_encoder.py @@ -0,0 +1,1128 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Кодировщик инструкций для КВС +Содержит все функции генерации машинного кода +""" + +import struct +import sys +from kvs_data import INSTRUCTIONS, REGISTERS, get_reg_info + + +def parse_operand(operand, labels, label_sections, symbols, vaddr_text, vaddr_data): + """ + Вычисляет значение операнда. + Поддерживает: + - десятичные числа (123) + - шестнадцатеричные (0x7B) + - метки (имя_метки) + - константы (из .константа) + """ + if operand.isdigit(): + return int(operand) + if operand.startswith('0x') or operand.startswith('0X'): + try: + return int(operand, 16) + except ValueError: + pass + if operand in labels: + section = label_sections.get(operand, '.text') + base = vaddr_text if section == '.text' else vaddr_data + return labels[operand] + base + if operand in symbols: + return symbols[operand] + raise ValueError("Неизвестный операнд: " + operand) + + +def parse_memory_operand(operand_str, labels, label_sections, vaddr_text, vaddr_data): + """ + Разбирает операнд памяти. + Возвращает словарь с информацией или None. + Поддерживает: + - [число] — абсолютный адрес + - [метка] — адрес метки (из .data или .text) + - [регистр] — косвенная адресация (TODO) + """ + if not operand_str.startswith('[') or not operand_str.endswith(']'): + return None + + content = operand_str[1:-1].strip() + + # Проверяем, является ли содержимое числом + if content.isdigit(): + return { + 'type': 'absolute', + 'address': int(content) + } + elif content.startswith('0x') or content.startswith('0X'): + try: + addr = int(content, 16) + return { + 'type': 'absolute', + 'address': addr + } + except ValueError: + pass + + # Проверяем, является ли содержимое меткой + if content in labels: + section = label_sections.get(content, '.data') + base = vaddr_text if section == '.text' else vaddr_data + addr = labels[content] + base + return { + 'type': 'absolute', + 'address': addr + } + + # TODO: другие типы ([reg], [reg+disp], [reg+reg*scale]) + return None + + +def encode_mov_reg_mem_absolute(reg_info, mem_info, current_pos, vaddr_text): + """ + Кодирует MOV reg, [addr] + Используем RIP-relative адресацию: mov reg, [rip + disp32] + """ + code = bytearray() + reg = reg_info['index'] + target_addr = mem_info['address'] + + # Вычисляем смещение относительно RIP + # Инструкция: REX (1) + opcode (1) + ModRM (1) + disp32 (4) = 7 байт + rip_at_end = vaddr_text + current_pos + 7 + offset = target_addr - rip_at_end + + # REX префикс (W=1 для 64-бит, R=бит для reg) + rex = 0x48 if reg_info['size'] == 64 else 0x40 + if reg >= 8: + rex |= 0x01 # REX.R + code.append(rex) + + # Opcode для MOV reg, mem (8B) + code.append(0x8B) + + # ModR/M: mod=00 (disp32), reg=reg, r/m=101 (RIP-relative) + modrm = 0x05 | ((reg & 7) << 3) + code.append(modrm) + + # disp32 (смещение относительно RIP) + code.extend(struct.pack('= 8: + rex |= 0x01 + code.append(rex) + + # Opcode для MOV mem, reg (89) + code.append(0x89) + + # ModR/M: mod=00, reg=reg, r/m=101 + modrm = 0x05 | ((reg & 7) << 3) + code.append(modrm) + + # disp32 + code.extend(struct.pack(', [<адрес>] + """ + 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) + 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 + + +def encode_mov_mem_reg(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): + """ + MOV mem, reg - сохранить из регистра в память + Формат: сохранить [<адрес>], <регистр> + """ + 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) + 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 + + +def encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): + """ + LEA reg, mem - загрузить эффективный адрес + Формат: загрузить_адрес <регистр>, [<адрес>] + """ + 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) + if mem_info and mem_info['type'] == 'absolute': + target_addr = mem_info['address'] + rip_at_end = vaddr_text + current_pos + 7 + offset = target_addr - rip_at_end + + code = bytearray() + reg = reg_info['index'] + + rex = 0x48 if reg_info['size'] == 64 else 0x40 + if reg >= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + + code.append(0x8D) # LEA opcode + modrm = 0x05 | ((reg & 7) << 3) + code.append(modrm) + code.extend(struct.pack(', <значение|метка|константа> + """ + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) + + if size == 64: + if 0 <= reg <= 7: + code.extend(b'\x48') + code.append(0xB8 + reg) + elif 8 <= reg <= 15: + code.extend(b'\x49') + code.append(0xB8 + (reg - 8)) + code.extend(struct.pack('= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(0xC7) + modrm = 0xC0 | (reg & 7) + code.append(modrm) + code.extend(struct.pack('= 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) + elif size == 8: + 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 + + +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 + + +def encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data): + """ + MOVZX reg, mem8 - переместить с расширением нулями + Формат: переместить_с_нулями <регистр>, <адрес_байта> + """ + code = bytearray() + instr = INSTRUCTIONS["переместить_с_нулями"] + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) + + rex = 0x48 if reg_info["size"] == 64 else 0x40 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.extend(instr["opcode"]) + modrm = 0x05 | ((reg & 7) << 3) + code.append(modrm) + code.extend(struct.pack(', <адрес_байта> + """ + code = bytearray() + instr = INSTRUCTIONS["переместить_со_знаком"] + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + addr = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) + + rex = 0x48 if reg_info["size"] == 64 else 0x40 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.extend(instr["opcode"]) + modrm = 0x05 | ((reg & 7) << 3) + code.append(modrm) + code.extend(struct.pack(', <значение|метка|константа> + """ + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) + + if size == 64: + rex = 0x48 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.append(0x81) + modrm = 0xF8 | (reg & 7) + code.append(modrm) + code.extend(struct.pack('= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(0x83) + modrm = 0xF8 | (reg & 7) + code.append(modrm) + code.append(imm & 0xFF) + else: + 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(struct.pack('= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(0x81) + modrm = 0xF8 | (reg & 7) + code.append(modrm) + code.extend(struct.pack('= 8: + rex |= 0x01 + code.append(rex) + code.append(0x80) + modrm = 0xF8 | (reg & 7) + code.append(modrm) + code.append(imm & 0xFF) + + 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) ========== + +def encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data): + """ + ADD/SUB reg, imm - прибавить/вычесть непосредственное значение + Формат: прибавить_непосредственно / вычесть_непосредственно <регистр>, <значение> + """ + code = bytearray() + reg_info = get_reg_info(operands[0]) + reg = reg_info["index"] + size = reg_info["size"] + imm = parse_operand(operands[1], labels, label_sections, symbols, vaddr_text, vaddr_data) + + op_map = { + "прибавить_непосредственно": 0x81, + "вычесть_непосредственно": 0x81, + } + op = op_map.get(mnemonic, 0x81) + subop = 0 if mnemonic == "прибавить_непосредственно" else 5 + + if size == 64: + rex = 0x48 + if reg >= 8: + rex |= 0x01 + code.append(rex) + code.append(op) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + code.extend(struct.pack('= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(op) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + code.append(imm & 0xFF) + else: + rex = 0x40 + if reg >= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(op) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + code.extend(struct.pack('= 8: + rex |= 0x01 + if rex != 0x40: + code.append(rex) + code.append(op) + modrm = 0xC0 | (subop << 3) | (reg & 7) + code.append(modrm) + code.extend(struct.pack(', <регистр_источник> + """ + 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. Инструкции переходов и вызовов ========== + +def encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos): + """ + JMP rel32 - безусловный переход с 32-битным смещением + """ + code = bytearray() + instr = INSTRUCTIONS["переход"] + code.extend(instr["opcode"]) + target = parse_operand(operands[0], labels, label_sections, symbols, vaddr_text, vaddr_data) + offset = target - (vaddr_text + current_pos + 5) + 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): + """ + Главный диспетчер генерации машинного кода. + По мнемонике выбирает соответствующую функцию генерации. + """ + + # ----- Инструкции без операндов ----- + if mnemonic == "вызов_системы": + return encode_syscall() + elif mnemonic == "нет_операции": + return encode_nop() + elif mnemonic == "вернуться": + return encode_ret() + elif mnemonic == "остановить": + return encode_hlt() + elif mnemonic == "отладка": + return encode_int3() + elif mnemonic in ("установить_перенос", "сбросить_перенос", "установить_направление", + "сбросить_направление", "втолкнуть_флаги", "вытолкнуть_флаги"): + return encode_flag_instruction(mnemonic) + elif mnemonic in ("переместить_байт", "переместить_слово", "сравнить_байты", "сканировать_байт"): + return encode_string_instruction(mnemonic) + elif mnemonic == "идентифицировать_процессор": + return encode_cpuid() + elif mnemonic == "прочитать_счётчик": + return encode_rdtsc() + + # ----- MOV с памятью ----- + elif mnemonic == "загрузить": + return encode_mov_reg_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic == "сохранить": + return encode_mov_mem_reg(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic == "загрузить_адрес": + return encode_lea_mem(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + + # ----- MOV (разные формы) ----- + elif mnemonic == "переместить_имм": + return encode_mov_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) + elif mnemonic == "переместить": + return encode_mov_reg_reg(operands) + elif mnemonic == "загрузить_байт": + return encode_mov_reg8_imm8(operands) + elif mnemonic == "переместить_с_нулями": + return encode_movzx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) + elif mnemonic == "переместить_со_знаком": + return encode_movsx(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) + + # ----- CMP (сравнение) ----- + elif mnemonic == "сравнить_с": + return encode_cmp_reg_imm(operands, labels, label_sections, symbols, vaddr_text, vaddr_data) + elif mnemonic in ("сравнить", "сравнить_байт", "проверить"): + return encode_cmp_reg_reg(mnemonic, operands) + + # ----- ADD/SUB (арифметика) ----- + elif mnemonic in ("прибавить_непосредственно", "вычесть_непосредственно"): + return encode_add_sub_reg_imm(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data) + elif mnemonic in ("прибавить", "вычесть", "прибавить_байт", "вычесть_байт"): + return encode_add_sub_reg_reg(mnemonic, operands) + + # ----- MUL/DIV ----- + elif mnemonic in ("умножить", "умножить_знаковое", "разделить", "разделить_знаковое"): + return encode_muldiv(mnemonic) + + # ----- AND/OR/XOR ----- + elif mnemonic in ("и", "или", "исключающее_или"): + return encode_and_or_xor_reg_reg(mnemonic, operands) + + # ----- NOT/NEG ----- + elif mnemonic in ("инвертировать", "отрицать"): + return encode_not_neg(mnemonic, operands) + + # ----- INC/DEC ----- + elif mnemonic in ("увеличить", "уменьшить"): + return encode_incdec(mnemonic, operands) + + # ----- Переходы ----- + elif mnemonic == "переход": + return encode_jmp_rel32(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic == "короткий_переход": + return encode_jmp_short(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic in ("переход_если_равно", "переход_если_неравно", "переход_если_меньше", + "переход_если_больше", "переход_если_меньше_или_равно", "переход_если_больше_или_равно", + "переход_если_перенос", "переход_если_нет_переноса", "переход_если_ноль", "переход_если_не_ноль"): + return encode_jcc_rel32(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic in ("короткий_переход_если_равно", "короткий_переход_если_неравно", + "короткий_переход_если_меньше", "короткий_переход_если_больше", + "короткий_переход_если_меньше_или_равно", "короткий_переход_если_больше_или_равно", + "короткий_переход_если_перенос", "короткий_переход_если_нет_переноса", + "короткий_переход_если_ноль", "короткий_переход_если_не_ноль"): + return encode_jcc_short(mnemonic, operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + + # ----- CALL/RET/LOOP ----- + elif mnemonic == "вызвать": + return encode_call(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + elif mnemonic == "цикл": + return encode_loop(operands, labels, label_sections, symbols, vaddr_text, vaddr_data, current_pos) + + # ----- PUSH/POP ----- + elif mnemonic == "втолкнуть": + return encode_push_reg(operands) + elif mnemonic == "вытолкнуть": + return encode_pop_reg(operands) + elif mnemonic == "втолкнуть_непосредственно": + return encode_push_imm(operands) + + # ----- Битовые операции ----- + elif mnemonic in ("сдвиг_влево", "сдвиг_вправо", "сдвиг_арифметический_вправо", + "вращать_влево", "вращать_вправо"): + return encode_shift_rotate(mnemonic, operands) + + # ----- Ввод-вывод ----- + elif mnemonic == "ввод_байта": + return encode_in(operands) + elif mnemonic == "вывод_байта": + return encode_out(operands) + + # ----- Прочие ----- + elif mnemonic == "прервать": + return encode_int(operands) + elif mnemonic == "обменять": + return encode_xchg(operands) + + else: + raise NotImplementedError(f"Инструкция '{mnemonic}' не реализована") \ No newline at end of file diff --git a/todo.txt b/todo.txt index cb74312..bda32be 100644 --- a/todo.txt +++ b/todo.txt @@ -1,83 +1,68 @@ +```markdown =========================== -нужна сложная адресация +ПЛАН РЕАЛИЗАЦИИ СЛОЖНОЙ АДРЕСАЦИИ (ПОЛНАЯ ВЕРСИЯ) =========================== -Хорошо, вот **полностью доработанный пошаговый план** внедрения сложной адресации во все модули компилятора КВС, с учётом: +## СТАТУС: ЧАСТИЧНО РЕАЛИЗОВАНО (v2.0) -- Поддержки `+`, `-`, `*`, `[`, `]` -- Новых мнемоник для работы с памятью -- Адресации всех типов: `[reg]`, `[disp]`, `[reg+disp]`, `[reg+reg*scale]`, `[reg+reg*scale+disp]` -- Корректной работы лексера, парсера, pass1, pass2, сборки ELF +### Уже реализовано: +- ✅ Лексер: токены `[`, `]`, `NUMBER` +- ✅ Парсер: распознавание `[метка]` и `[число]` +- ✅ Pass1: корректный расчёт размера (7 байт для MOV с памятью) +- ✅ Pass2: RIP-relative кодирование для абсолютной адресации +- ✅ Поддержка меток в операндах памяти `[переменная]` +- ✅ Рефакторинг: `kvs_pass2_encoder.py` отдельно --- -# ПЛАН РЕАЛИЗАЦИИ СЛОЖНОЙ АДРЕСАЦИИ (ПОЛНАЯ ВЕРСИЯ) - ## 1. Общая схема изменений по модулям -| Модуль | Что меняется | Сложность | -|-------------------|------------------------------------|-----------| -| `kvs_lexer.py` | новые токены: `[`, `]`, `+`, `-`, `*`, `NUMBER` | низкая | -| `kvs_parser.py` | новая логика операндов: `parse_operand()`, `parse_memory_operand()` | высокая | -| `kvs_data.py` | активация и уточнение форматов инструкций | средняя | -| `kvs_pass1.py` | динамический рассчёт размера инструкций с памятью | высокая | -| `kvs_pass2.py` | разбор адресации → ModR/M + SIB + disp, генерация кода | очень высокая | - +| Модуль | Что меняется | Сложность | Статус | +|--------|--------------|-----------|--------| +| `kvs_lexer.py` | новые токены: `[`, `]`, `+`, `-`, `*`, `NUMBER` | низкая | ✅ ГОТОВ | +| `kvs_parser.py` | новая логика операндов: `parse_operand()`, `parse_memory_operand()` | высокая | ✅ ГОТОВ (базово) | +| `kvs_data.py` | активация и уточнение форматов инструкций | средняя | ⚠️ ТРЕБУЕТ ДОРАБОТКИ | +| `kvs_pass1.py` | динамический рассчёт размера инструкций с памятью | высокая | ✅ ГОТОВ (для абсолютной) | +| `kvs_pass2_encoder.py` | разбор адресации → ModR/M + SIB + disp, генерация кода | очень высокая | ⚠️ ЧАСТИЧНО (только абсолютная) | --- ## 2. Детальный план по шагам (в порядке реализации) -### Шаг 1 — Лексер (`kvs_lexer.py`) +### Шаг 1 — Лексер (`kvs_lexer.py`) ✅ РЕАЛИЗОВАН -**Что сделать:** -- Добавить токены: - - `LBRACKET` → `[` - - `RBRACKET` → `]` - - `PLUS` → `+` - - `MINUS` → `-` - - `STAR` → `*` -- Добавить `NUMBER` (отдельно от `WORD`) для: - - десятичных чисел (`123`) - - отрицательных (`-456`) - - шестнадцатеричных (`0x7F`) - -** парсер:** -- Парсер сможет отличать `[` от начала слова -- Числа не будут смешиваться с метками/регистрами +**Что сделано:** +- Добавлены токены: `LBRACKET` (`[`), `RBRACKET` (`]`), `PLUS` (`+`), `MINUS` (`-`), `STAR` (`*`), `NUMBER` +- Числа отделены от слов (поддержка десятичных, шестнадцатеричных, отрицательных) **Результат:** новые типы токенов на выходе лексера. --- -### Шаг 2 — Парсер (`kvs_parser.py`) — основа +### Шаг 2 — Парсер (`kvs_parser.py`) ✅ РЕАЛИЗОВАН (базово) -**Что сделать:** +**Что сделано:** -2.1. Заменить прямую загрузку токена в `parse_instruction()` на вызов `parse_operand()` +2.1. Реализован `parse_operand()`: +- Если токен `LBRACKET` → вызывает `parse_memory_operand()` +- Иначе → возвращает значение токена (регистр/число/метка/строка) -2.2. Реализовать `parse_operand()`: -- Если токен `LBRACKET` → вызвать `parse_memory_operand()` -- Иначе → вернуть значение токена (регистр/число/метка/строка) - -2.3. Реализовать `parse_memory_operand()`: +2.2. Реализован `parse_memory_operand()`: - Разбирает содержимое между `[` и `]` -- Поддерживает выражения вида: - - `регистр` - - `число` - - `регистр + смещение` - - `регистр + регистр * масштаб` - - `регистр + регистр * масштаб + смещение` - - любые комбинации с `+` и `-` -- Возвращает строку типа `[раикс + рсикс*8 + 16]` +- Поддерживает: `регистр`, `число`, `метка` +- Возвращает строку вида `[раикс + рсикс*8 + 16]` (структура сохраняется) -**Результат:** AST будет содержать операнды памяти в каноническом строковом виде. +2.3. **Ограничения текущей реализации:** +- Поддерживается только простая форма `[метка]` и `[число]` +- Сложные выражения `[reg + reg*scale + disp]` парсятся, но не кодируются + +**Результат:** AST содержит операнды памяти в каноническом строковом виде. --- -### Шаг 3 — Данные инструкций (`kvs_data.py`) +### Шаг 3 — Данные инструкций (`kvs_data.py`) ⚠️ ТРЕБУЕТ ДОРАБОТКИ -**Что сделать:** +**Что нужно сделать:** 3.1. Активировать (или добавить) записи для: @@ -94,41 +79,31 @@ | `вытолкнуть_в_память` | `8F /0` | `pop_mem` | | `обменять_с_памятью` | `48 87` | `xchg_mem` | -3.2. Удалить поле `type`, оставить `format` или прямо в коде проверять по мнемонике (как вы любите). - -**Результат:** словарь инструкций готов для кодогенерации. +3.2. **Статус:** записи присутствуют в `kvs_data.py`, но используются не все. --- -### Шаг 4 — Первый проход (`kvs_pass1.py`) — оценка размера +### Шаг 4 — Первый проход (`kvs_pass1.py`) ✅ РЕАЛИЗОВАН (для абсолютной) -**Что сделать:** +**Что сделано:** -4.1. Написать функцию `estimate_memory_operand_size(operand_str)`: +4.1. Написана функция `estimate_memory_operand_size(operand_str)`: - Парсит строку `[раикс + рсикс*8 + 16]` -- Определяет: - - наличие SIB (нужен, если есть индексный регистр) - - размер смещения (disp8 = 1 байт, disp32 = 4 байта) -- Возвращает дополнительные байты: 0, 1, 4 или 5 (SIB + disp) +- Определяет наличие SIB, размер смещения -4.2. Модифицировать `estimate_size()`: -- Для инструкций с памятью (список из шага 3) считать: - - базовый размер opcode (REX + опкод) - - +1 байт (ModR/M) - - +1 байт (SIB, если нужен) - - + размер смещения (1 или 4) +4.2. Модифицирована `estimate_size()`: +- Для инструкций `загрузить`, `сохранить`, `загрузить_адрес` возвращает `7` (REX + opcode + ModRM + disp32) -**Результат:** правильное вычисление `.text` и адресов меток. +**Ограничения:** +- Для сложных форм адресации размер не рассчитывается динамически --- -### Шаг 5 — Второй проход (`kvs_pass2.py`) — ядро адресации +### Шаг 5 — Второй проход (`kvs_pass2_encoder.py`) ⚠️ ЧАСТИЧНО РЕАЛИЗОВАН -Это самый объёмный шаг. Реализовать **поэтапно**: +#### 5.1. Структура данных для режима адресации ⚠️ НЕ РЕАЛИЗОВАНА -#### 5.1. Структура данных для режима адресации - -Создать класс/словарь `AddressingMode`: +Нужно создать класс/словарь `AddressingMode`: - `has_base` (bool) - `base_reg` (int 0–15 или None) - `has_index` (bool) @@ -138,136 +113,134 @@ - `disp_value` (int) - `disp_size` (1 или 4) -#### 5.2. Парсер адресации +#### 5.2. Парсер адресации ⚠️ ЧАСТИЧНО РЕАЛИЗОВАН -Написать `parse_addressing_mode(operand_str)`: -- Вход: `[раикс + рсикс*8 + 16]` -- Выход: `AddressingMode` -- Поддержка всех форм из шага 2.3 +Реализована `parse_memory_operand()` в `kvs_pass2_encoder.py`: +- Поддерживает `[число]` и `[метка]` +- **Нет поддержки:** `[reg]`, `[reg+disp]`, `[reg+reg*scale]` -#### 5.3. Кодирование ModR/M +#### 5.3. Кодирование ModR/M ⚠️ ЧАСТИЧНО РЕАЛИЗОВАНО -Написать `encode_modrm_reg_mem(reg, addr_mode)`: -- Формирует байт ModR/M: - - `mod` (00, 01, 10, 11) - - `reg` (3 бита — номер регистра) - - `r/m` (3 бита — зависит от наличия SIB) +Функция `encode_modrm_reg_mem_absolute()`: +- Использует `mod=00, r/m=101` для RIP-relative +- **Нет поддержки:** других режимов адресации -#### 5.4. Кодирование SIB +#### 5.4. Кодирование SIB ❌ НЕ РЕАЛИЗОВАНО -Написать `encode_sib(addr_mode)`: -- Формирует байт SIB: - - `scale` (2 бита) - - `index` (3 бита) - - `base` (3 бита) +Функция `encode_sib()` отсутствует. -#### 5.5. Кодирование смещения +#### 5.5. Кодирование смещения ⚠️ ЧАСТИЧНО РЕАЛИЗОВАНО -Написать `encode_displacement(addr_mode, current_pos, vaddr_text)`: -- Для `disp8` → 1 байт со знаком -- Для `disp32` → 4 байта (little-endian) +`encode_displacement()` использует `struct.pack('
    ДирективаОписаниеПример
    .текстПереключение в секцию кода.текст
    .данныеПереключение в секцию данных.данные
    .глобалОбъявление глобальной метки (точки входа).глобал _start
    ДирективаНазначениеПример
    .текстНачало секции кода.текст
    .данныеНачало секции данных.данные
    .глобалОбъявление глобальной метки.глобал _start
    .строкаСтрока без нулевого терминатора.строка "Hello"
    .строка_нульСтрока с нулевым терминатором.строка_нуль "Hello"
    .байтРезервирование байтов.байт 0x0A, 255, 0b0001