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
+ загрузить рдиай, [переменная]
+ вызов_системы
+
+
+ ⚠️ ВАЖНО: Порядок объявления в секции .данные имеет значение! Переменные, объявленные перед строковыми константами, могут затереть первые байты строк. Рекомендуется объявлять строки первыми, а изменяемые переменные — после.
+
+
РЕГИСТРЫ ПРОЦЕССОРА
| 64-битные | 32-битные | 16-битные | 8-битные (мл.) | 8-битные (ст.) |
@@ -460,10 +552,10 @@ _start:
ДИРЕКТИВЫ АССЕМБЛЕРА
- | Директива | Описание | Пример |
- .текст | Переключение в секцию кода | .текст |
- .данные | Переключение в секцию данных | .данные |
- .глобал | Объявление глобальной метки (точки входа) | .глобал _start |
+ | Директива | Назначение | Пример |
+ .текст | Начало секции кода | .текст |
+ .данные | Начало секции данных | .данные |
+ .глобал | Объявление глобальной метки | .глобал _start |
.строка | Строка без нулевого терминатора | .строка "Hello" |
.строка_нуль | Строка с нулевым терминатором | .строка_нуль "Hello" |
.байт | Резервирование байтов | .байт 0x0A, 255, 0b0001 |
@@ -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
-
-[](https://github.com/username/kvs)
-[](https://python.org)
-[](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('