абсолютная адресация

This commit is contained in:
mzhoot 2026-05-11 15:08:03 +03:00
parent 520da3a8a3
commit eaef835f4d
13 changed files with 2017 additions and 1230 deletions

View file

@ -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 @@
</pre>
</div>
<p>Версия 2.0 | Экспериментальный проект | 2025</p>
<p><span style="color: #00ff00;">✓ Абсолютная адресация реализована</span> | <span style="color: #ffff00;">⟳ Косвенная адресация в разработке</span></p>
</div>
<h2>┌──[ ОГЛАВЛЕНИЕ ]────────────────────────────────────────────┐</h2>
@ -340,10 +375,13 @@
<li><a href="#установка">Установка и запуск</a></li>
<li><a href="#синтаксис">Синтаксис исходных файлов (.квс)</a></li>
<li><a href="#команды">Система команд</a></li>
<li><a href="#память">Работа с памятью</a></li>
<li><a href="#регистры">Регистры процессора</a></li>
<li><a href="#директивы">Директивы ассемблера</a></li>
<li><a href="#примеры">Примеры программ</a></li>
<li><a href="#трансляция">Процесс трансляции</a></li>
<li><a href="#статус">Статус реализации</a></li>
<li><a href="#план">План развития</a></li>
<li><a href="#ограничения">Ограничения</a></li>
</ul>
<h2>└──────────────────────────────────────────────────────────────┘</h2>
@ -367,8 +405,10 @@
<li><strong>Генерация исполняемых ELF-файлов</strong> — 64-битные ELF для Linux, готовые к запуску</li>
<li><strong>Двухпроходная сборка</strong> — традиционная схема с разрешением меток</li>
<li><strong>Поддержка секций</strong> — разделение на <code>.текст</code> (код) и <code>.данные</code> (данные)</li>
<li><strong>Абсолютная адресация памяти</strong> — чтение и запись по меткам <code>[переменная]</code> и абсолютным адресам <code>[0x402000]</code> с RIP-relative кодированием</li>
<li><strong>CSV-логирование</strong> — детальный файл <code>.csv</code> с соответствием адресов, байтов и исходных команд</li>
<li><strong>Более 50 инструкций</strong> — полный набор основных команд x86-64</li>
<li><strong>Рефакторинг</strong> — кодогенерация вынесена в отдельный модуль <code>kvs_pass2_encoder.py</code></li>
</ul>
<h2 id="архитектура">АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ</h2>
@ -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)
Исполняемый файл!
</div>
<h3>Модули компилятора</h3>
<table>
<tr><th>Файл</th><th>Назначение</th></tr>
<tr><td><code>kvs_build.py</code></td><td>Главный сборочный скрипт</td></tr>
<tr><td><code>kvs_lexer.py</code></td><td>Лексический анализ, разбор на токены</td></tr>
<tr><td><code>kvs_parser.py</code></td><td>Синтаксический анализ, построение AST</td></tr>
<tr><td><code>kvs_pass1.py</code></td><td>Первый проход: вычисление размеров и адресов меток</td></tr>
<tr><td><code>kvs_pass2.py</code></td><td>Второй проход: обход AST, генерация CSV</td></tr>
<tr><td><code>kvs_pass2_encoder.py</code></td><td>Кодировщик инструкций (все encode_* функции)</td></tr>
<tr><td><code>kvs_builder.py</code></td><td>Сборка ELF-файла из CSV</td></tr>
<tr><td><code>kvs_data.py</code></td><td>Общие данные (регистры, инструкции)</td></tr>
</table>
<h2 id="установка">УСТАНОВКА И ЗАПУСК</h2>
<pre># Клонирование репозитория
git clone https://github.com/username/kvs.git
@ -411,39 +464,78 @@ python3 kvs_build.py программа.квс
<pre>; Комментарий начинается с точки с запятой
.данные ; Начало секции данных
переменная: .байт 0 ; Переменная в памяти
message: .строка_нуль "Привет, мир!"
counter: .байт 0x0A
.текст ; Начало секции кода
.глобал _start ; Точка входа
_start:
; Аргументы для write(1, message, 13)
; Работа с памятью
переместить_имм раикс, 42
сохранить [переменная], раикс ; запись в память
загрузить рбикс, [переменная] ; чтение из памяти
; Системный вызов write
переместить_имм раикс, 1
загрузить_адрес рсиай, message
переместить_имм рдикс, 13
вызов_системы
; Выход с кодом 0
; Выход с кодом из переменной
переместить_имм раикс, 60
переместить_имм рдикс, 0
загрузить рдиай, [переменная]
вызов_системы</pre>
<h2 id="команды">СИСТЕМА КОМАНД</h2>
<table>
<tr><th>Категория</th><th>Примеры инструкций</th><th>Соответствие NASM</th></tr>
<tr><td>Перемещение данных</td><td><code>переместить</code>, <code>загрузить</code>, <code>сохранить</code>, <code>переместить_имм</code></td><td><code>mov</code>, <code>lea</code></td></tr>
<tr><td>Перемещение данных</td><td><code>переместить</code>, <code>переместить_имм</code></td><td><code>mov</code>, <code>mov reg, imm</code></td></tr>
<tr><td>Работа с памятью</td><td><code>загрузить</code>, <code>сохранить</code>, <code>загрузить_адрес</code></td><td><code>mov reg, mem</code>, <code>mov mem, reg</code>, <code>lea</code></td></tr>
<tr><td>Арифметика</td><td><code>прибавить</code>, <code>вычесть</code>, <code>умножить</code>, <code>разделить</code>, <code>увеличить</code>, <code>уменьшить</code></td><td><code>add</code>, <code>sub</code>, <code>mul</code>, <code>div</code>, <code>inc</code>, <code>dec</code></td></tr>
<tr><td>Логические</td><td><code>и</code>, <code>или</code>, <code>исключающее_или</code>, <code>инвертировать</code>, <code>отрицать</code></td><td><code>and</code>, <code>or</code>, <code>xor</code>, <code>not</code>, <code>neg</code></td></tr>
<tr><td>Сравнение и проверка</td><td><code>сравнить</code>, <code>сравнить_с</code>, <code>проверить</code></td><td><code>cmp</code>, <code>test</code></td></tr>
<tr><td>Условные переходы</td><td><code>переход_если_равно</code>, <code>переход_если_больше</code>, <code>короткий_переход_если_равно</code></td><td><code>je</code>, <code>jg</code>, <code>je short</code></td></tr>
<tr><td>Сравнение</td><td><code>сравнить</code>, <code>сравнить_с</code>, <code>проверить</code></td><td><code>cmp</code>, <code>test</code></td></tr>
<tr><td>Условные переходы</td><td><code>переход_если_равно</code>, <code>переход_если_больше</code>, <code>короткий_переход_если_равно</code></td><td><code>je</code>, <code>jg</code>, <code>je short</code></td><tr>
<tr><td>Безусловные переходы</td><td><code>переход</code>, <code>короткий_переход</code>, <code>вызвать</code>, <code>вернуться</code></td><td><code>jmp</code>, <code>jmp short</code>, <code>call</code>, <code>ret</code></td></tr>
<tr><td>Стек</td><td><code>втолкнуть</code>, <code>вытолкнуть</code>, <code>втолкнуть_непосредственно</code></td><td><code>push</code>, <code>pop</code>, <code>push imm</code></td></tr>
<tr><td>Сдвиги и вращения</td><td><code>сдвиг_влево</code>, <code>сдвиг_вправо</code>, <code>вращать_влево</code>, <code>вращать_вправо</code></td><td><code>shl</code>, <code>shr</code>, <code>rol</code>, <code>ror</code></td></tr>
<tr><td>Без операндов</td><td><code>нет_операции</code>, <code>вызов_системы</code>, <code>остановить</code>, <code>отладка</code></td><td><code>nop</code>, <code>syscall</code>, <code>hlt</code>, <code>int3</code></td></tr>
<tr><td>Строковые</td><td><code>переместить_байт</code>, <code>сравнить_байты</code></td><td><code>movsb</code>, <code>cmpsb</code></td></tr>
<tr><td>Системные</td><td><code>вызов_системы</code>, <code>остановить</code></td><td><code>syscall</code>, <code>hlt</code></td></tr>
</table>
<h2 id="память">РАБОТА С ПАМЯТЬЮ</h2>
<div class="success">
<strong>✓ РЕАЛИЗОВАНО (v2.0):</strong> Абсолютная адресация через метки и числа
</div>
<h3>Поддерживаемые формы:</h3>
<table>
<tr><th>Синтаксис</th><th>Описание</th><th>Пример</th></tr>
<tr><td><code>[метка]</code></td><td>Адрес метки (из .данные)</td><td><code>загрузить раикс, [переменная]</code></td></tr>
<tr><td><code>[число]</code></td><td>Абсолютный адрес</td><td><code>сохранить [0x402000], раикс</code></td></tr>
</table>
<h3>Пример использования:</h3>
<pre>.данные
переменная: .байт 0
результат: .байт 0
.текст
_start:
; Запись значения
переместить_имм раикс, 42
сохранить [переменная], раикс
; Чтение значения
загрузить рбикс, [переменная]
; Завершение с кодом 42
переместить_имм раикс, 60
загрузить рдиай, [переменная]
вызов_системы</pre>
<div class="warning">
<strong>⚠️ ВАЖНО:</strong> Порядок объявления в секции <code>.данные</code> имеет значение! Переменные, объявленные перед строковыми константами, могут затереть первые байты строк. Рекомендуется объявлять строки первыми, а изменяемые переменные — после.
</div>
<h2 id="регистры">РЕГИСТРЫ ПРОЦЕССОРА</h2>
<table>
<tr><th>64-битные</th><th>32-битные</th><th>16-битные</th><th>8-битные (мл.)</th><th>8-битные (ст.)</th></tr>
@ -460,10 +552,10 @@ _start:
<h2 id="директивы">ДИРЕКТИВЫ АССЕМБЛЕРА</h2>
<table>
<tr><th>Директива</th><th>Описание</th><th>Пример</th></tr>
<tr><td><code>.текст</code></td><td>Переключение в секцию кода</td><td><code>.текст</code></td></tr>
<tr><td><code>.данные</code></td><td>Переключение в секцию данных</td><td><code>.данные</code></td></tr>
<tr><td><code>.глобал</code></td><td>Объявление глобальной метки (точки входа)</td><td><code>.глобал _start</code></td></tr>
<tr><th>Директива</th><th>Назначение</th><th>Пример</th></tr>
<tr><td><code>.текст</code></td><td>Начало секции кода</td><td><code>.текст</code></td></tr>
<tr><td><code>.данные</code></td><td>Начало секции данных</td><td><code>.данные</code></td></tr>
<tr><td><code>.глобал</code></td><td>Объявление глобальной метки</td><td><code>.глобал _start</code></td></tr>
<tr><td><code>.строка</code></td><td>Строка без нулевого терминатора</td><td><code>.строка "Hello"</code></td></tr>
<tr><td><code>.строка_нуль</code></td><td>Строка с нулевым терминатором</td><td><code>.строка_нуль "Hello"</code></td></tr>
<tr><td><code>.байт</code></td><td>Резервирование байтов</td><td><code>.байт 0x0A, 255, 0b0001</code></td></tr>
@ -479,19 +571,37 @@ _start:
_start:
; write(1, msg, 13)
переместить_имм раикс, 1
загрузить_адрес рсиай, msg
переместить_имм рдиай, 1
переместить_имм рсиай, msg
переместить_имм рдикс, 13
вызов_системы
; exit(0)
переместить_имм раикс, 60
переместить_имм рдикс, 0
переместить_имм рдиай, 0
вызов_системы
.данные
msg: .строка_нуль "Привет, мир!"</pre>
msg: .строка_нуль "Hello, World!"</pre>
<h3>Пример 2: Цикл для вычисления суммы</h3>
<h3>Пример 2: Работа с переменными в памяти</h3>
<pre>.глобал _start
.текст
_start:
; Запись 42 в переменную
переместить_имм раикс, 42
сохранить [переменная], раикс
; Завершение с кодом 42
переместить_имм раикс, 60
загрузить рдиай, [переменная]
вызов_системы
.данные
переменная: .байт 0</pre>
<h3>Пример 3: Цикл для вычисления суммы</h3>
<pre>.глобал _start
.текст
@ -523,13 +633,99 @@ loop:
<li><strong><code>программа.elf</code></strong> — исполняемый ELF-файл</li>
</ul>
<h2 id="статус">СТАТУС РЕАЛИЗАЦИИ</h2>
<h3>✅ Полностью реализовано (v2.0)</h3>
<ul>
<li>Лексер с токенами <code>[</code>, <code>]</code>, <code>NUMBER</code></li>
<li>Парсер с поддержкой операндов в квадратных скобках</li>
<li>Первый проход с корректным расчётом размера инструкций с памятью</li>
<li>Второй проход с кодированием абсолютной адресации через RIP-relative</li>
<li>Поддержка меток в операндах памяти <code>[переменная]</code></li>
<li>Рефакторинг: выделение кодировщика в отдельный модуль <code>kvs_pass2_encoder.py</code></li>
<li>Более 50 инструкций x86-64</li>
<li>CSV-логирование</li>
</ul>
<h3>⚠️ Частично реализовано</h3>
<ul>
<li><code>загрузить_адрес</code> (LEA) — только для чисел, не для меток</li>
<li>Сложные выражения в операндах памяти парсятся, но не кодируются</li>
</ul>
<h3>❌ В разработке (v3.0)</h3>
<ul>
<li>Косвенная адресация <code>[reg]</code></li>
<li>Адресация со смещением <code>[reg + disp]</code></li>
<li>Сложная адресация с индексом <code>[reg + reg*scale]</code></li>
<li>Полная SIB-адресация <code>[reg + reg*scale + disp]</code></li>
<li>Директива <code>.bss</code> для неинициализированных данных</li>
<li>LEA для меток (<code>загрузить_адрес reg, [метка]</code>)</li>
<li>Стандартная библиотека</li>
</ul>
<h2 id="план">ПЛАН РАЗВИТИЯ</h2>
<h3>v2.0 (текущая) — Абсолютная адресация</h3>
<ul>
<li>✅ Лексер: <code>[</code>, <code>]</code>, <code>NUMBER</code></li>
<li>✅ Парсер: <code>[метка]</code> и <code>[число]</code></li>
<li>✅ Pass1: размер = 7 байт</li>
<li>✅ Pass2: RIP-relative кодирование</li>
<li>✅ Поддержка меток в памяти</li>
<li>✅ Рефакторинг энкодера</li>
</ul>
<h3>v3.0 (планируется) — Косвенная и относительная адресация</h3>
<ul>
<li>❌ <code>[reg]</code> — косвенная адресация через регистр</li>
<li>❌ <code>[reg + disp]</code> — со смещением (disp8/disp32)</li>
<li>❌ <code>[reg + reg*scale]</code> — с индексным регистром</li>
<li>❌ <code>[reg + reg*scale + disp]</code> — полная SIB-адресация</li>
<li>❌ <code>.bss</code> — секция неинициализированных данных</li>
<li>❌ LEA для меток</li>
</ul>
<h3>v4.0 (отложено) — Стандартная библиотека</h3>
<ul>
<li>❌ <code>malloc</code>, <code>free</code></li>
<li>❌ <code>strlen</code>, <code>strcmp</code>, <code>memcpy</code></li>
<li>❌ <code>putchar</code>, <code>getchar</code></li>
<li>❌ <code>printf</code> (упрощённый вариант)</li>
</ul>
<div class="process-diagram">
Зависимости между подзадачами:
Лексер (шаг 1) ✅
↓
Парсер (шаг 2) ✅
↓
Данные инструкций (шаг 3) ⚠️
↓
Pass1 (шаг 4) ✅
↓
Pass2 (шаг 5): ⚠️ ЧАСТИЧНО
5.1 → 5.2 → 5.3 → 5.4 → 5.5
↓
5.6 (генераторы инструкций)
↓
5.7 (диспетчер) ✅
↓
Тестирование (шаг 6) ⚠️ ЧАСТИЧНО
↓
Библиотека (шаг 7) ❌ ОТЛОЖЕНО
</div>
<h2 id="ограничения">ОГРАНИЧЕНИЯ</h2>
<div class="warning">
<ul>
<li><strong>Сложная адресация памяти</strong> — в текущей версии поддерживаются только простые формы <code>[регистр]</code>; полная поддержка <code>[база + индекс*масштаб + смещение]</code> в разработке</li>
<li><strong>Отсутствие динамической памяти</strong> — нет встроенных malloc/free (можно реализовать через syscall brk)</li>
<li><strong>Косвенная адресация памяти</strong> — поддерживается только абсолютная форма <code>[метка]</code>; адресация через регистры <code>[reg]</code> в разработке</li>
<li><strong>Сложная адресация</strong> — формы <code>[reg+disp]</code>, <code>[reg+reg*scale]</code>, <code>[reg+reg*scale+disp]</code> не реализованы</li>
<li><strong>LEA для меток</strong> — <code>загрузить_адрес reg, [метка]</code> не работает, используйте <code>переместить_имм reg, метка</code></li>
<li><strong>Отсутствие .bss</strong> — все переменные инициализируются и попадают в <code>.data</code> (занимают место в файле)</li>
<li><strong>Нет динамической памяти</strong> — нет встроенных malloc/free (можно реализовать через syscall brk)</li>
<li><strong>Нет макросов и условной компиляции</strong></li>
<li><strong>Нет 32-битного режима</strong> — только 64-битные ELF</li>
<li><strong>Экспериментальный статус</strong> — некоторые инструкции могут быть реализованы не полностью</li>
</ul>
</div>

100
README.md
View file

@ -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
Только для образовательных целей. Используйте на свой страх и риск.
```
```

View file

@ -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

View file

@ -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))

View file

@ -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)

File diff suppressed because it is too large Load diff

1128
kvs_pass2_encoder.py Normal file

File diff suppressed because it is too large Load diff

307
todo.txt
View file

@ -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('<i', offset)` для disp32.
#### 5.6. Генераторы инструкций
#### 5.6. Генераторы инструкций ⚠️ ЧАСТИЧНО РЕАЛИЗОВАНЫ
Написать для каждой мнемоники:
| Функция | Статус |
|---------|--------|
| `encode_mov_reg_mem` | ✅ Реализована (только абсолютная адресация) |
| `encode_mov_mem_reg` | ✅ Реализована (только абсолютная адресация) |
| `encode_lea` | ⚠️ Есть старая версия, требует доработки |
| `encode_mov_reg8_mem` | ❌ Не реализована |
| `encode_mov_mem8_reg8` | ❌ Не реализована |
| `encode_movzx` | ❌ Не реализована |
| `encode_movsx` | ❌ Не реализована |
| `encode_push_mem` | ❌ Не реализована |
| `encode_pop_mem` | ❌ Не реализована |
| `encode_xchg_mem` | ❌ Не реализована |
| Функция | Для какой инструкции |
|---------|----------------------|
| `encode_mov_reg_mem` | `загрузить` |
| `encode_mov_mem_reg` | `сохранить` |
| `encode_mov_reg8_mem` | `загрузить_байт_из_памяти` |
| `encode_mov_mem8_reg8` | `сохранить_байт` |
| `encode_lea` | `загрузить_адрес` |
| `encode_movzx` | `переместить_с_нулями` |
| `encode_movsx` | `переместить_со_знаком` |
| `encode_push_mem` | `втолкнуть_из_памяти` |
| `encode_pop_mem` | `вытолкнуть_в_память` |
| `encode_xchg_mem` | `обменять_с_памятью` |
#### 5.7. Диспетчер ✅ РЕАЛИЗОВАН
Каждая функция:
- Берёт операнды (регистр + операнд памяти)
- Вызывает `parse_addressing_mode` для операнда памяти
- Формирует REX-префикс (W, R, B, X биты)
- Добавляет opcode
- Добавляет ModR/M
- Добавляет SIB (если нужен)
- Добавляет disp
#### 5.7. Диспетчер
Модифицировать `encode_instruction()`:
- Добавить ветки для всех новых мнемоник
- Вызывать соответствующие функции из 5.6
`encode_instruction()` содержит ветки для всех инструкций, включая:
- `загрузить`, `сохранить`, `загрузить_адрес` (с памятью)
- `переместить_имм`, `переместить` (без памяти)
---
### Шаг 6 — Тестирование (итеративное)
### Шаг 6 — Тестирование
Тестировать после каждого подшага 5.x:
Тесты для проверки (поэтапно):
| Тест | Что проверяет |
|------|---------------|
| `загрузить раикс, [рбикс]` | ModR/M, нет SIB, нет disp |
| `загрузить раикс, [1234]` | disp32, r/m=101 |
| `загрузить раикс, [рбикс + 8]` | disp8 |
| `загрузить раикс, [рбикс + 1000]` | disp32 |
| `загрузить раикс, [рбикс + рсикс]` | SIB, нет disp |
| `загрузить раикс, [рбикс + рсикс*8 + 16]` | SIB + disp8 |
| `сохранить [рдиай], рсикс` | MOV mem, reg |
| `загрузить_адрес р11, [рбикс + рсикс*4]` | LEA |
| Тест | Что проверяет | Статус |
|------|---------------|--------|
| `загрузить раикс, [число]` | Абсолютная адресация | ✅ ПРОЙДЕН |
| `сохранить [число], раикс` | Абсолютная адресация (запись) | ✅ ПРОЙДЕН |
| `загрузить раикс, [метка]` | Адресация по метке | ✅ ПРОЙДЕН |
| `сохранить [метка], раикс` | Адресация по метке (запись) | ✅ ПРОЙДЕН |
| `загрузить раикс, [рбикс]` | Косвенная адресация | ❌ НЕ РЕАЛИЗОВАНО |
| `загрузить раикс, [рбикс + 8]` | Со смещением | ❌ НЕ РЕАЛИЗОВАНО |
| `загрузить раикс, [рбикс + рсикс*8]` | С индексом | ❌ НЕ РЕАЛИЗОВАНО |
| `загрузить раикс, [рбикс + рсикс*8 + 16]` | Полная SIB | ❌ НЕ РЕАЛИЗОВАНО |
| `загрузить_адрес р11, [переменная]` | LEA с меткой | ❌ НЕ РЕАЛИЗОВАНО |
---
### Шаг 7 — Стандартная библиотека (параллельно или после)
### Шаг 7 — Стандартная библиотека (отложено)
После того как адресация заработает, можно добавить:
После полной реализации адресации:
| Файл | Содержание |
|------|------------|
| `libkvs.квс` | `malloc`, `free`, `strlen`, `strcmp`, `memcpy`, `putchar`, `getchar`, `printf` (упрощённый) |
| `libkvs.квс` | `malloc`, `free`, `strlen`, `strcmp`, `memcpy`, `putchar`, `getchar`, `printf` |
| `libkvs_data.квс` | статические данные для библиотеки |
Библиотека будет использовать сложную адресацию для работы со структурами и кучей.
---
## 3. Зависимости между подзадачами
## 3. Следующие шаги (приоритеты)
```
Лексер (шаг 1)
↓
Парсер (шаг 2)
↓
Данные инструкций (шаг 3)
↓
Pass1 (шаг 4) ← может тестироваться отдельно
↓
Pass2:
5.1 → 5.2 → 5.3 → 5.4 → 5.5
↓
5.6 (для каждой мнемоники)
↓
5.7 (диспетчер)
↓
Тестирование (шаг 6)
↓
Библиотека (шаг 7)
```
### 🔴 Приоритет 1 (критично):
1. **Косвенная адресация `[reg]`** — основа для всех остальных форм
- Нужно: `mod=00, r/m=reg` (без SIB, без disp)
### 🟡 Приоритет 2 (важно):
2. **Адресация со смещением `[reg + disp]`**
- disp8 для малых смещений (`mod=01`)
- disp32 для больших (`mod=10`)
### 🟢 Приоритет 3 (расширение):
3. **Сложная адресация с индексом `[reg + reg*scale]`**
- Требует SIB байта
4. **Полная SIB-адресация `[reg + reg*scale + disp]`**
5. **LEA для меток** `загрузить_адрес reg, [метка]`
6. **`.bss` секция** для неинициализированных данных
---
## 4. Итог: что получим на выходе
- **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
- **Парсер** понимает `[раикс + рсикс*8 - 16]`
- **Pass1** правильно считает размер инструкций с памятью
- **Pass2** генерирует правильный машинный код для всех форм адресации
- **ELF** содержит корректные инструкции x86-64 с ModR/M, SIB, disp
- **Библиотека** позволяет писать более сложные программы (malloc, строки)
- ✅ **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
- ✅ **Парсер** понимает `[раикс + рсикс*8 - 16]`
- ✅ **Pass1** правильно считает размер инструкций с памятью (для абсолютной)
- ⚠️ **Pass2** генерирует код только для абсолютной адресации
- ⚠️ **ELF** содержит корректные инструкции x86-64 с RIP-relative
- ❌ **Библиотека** требует полной адресации
---
## 5. Зависимости между подзадачами
```
Лексер (шаг 1) ✅
↓
Парсер (шаг 2) ✅
↓
Данные инструкций (шаг 3) ⚠️
↓
Pass1 (шаг 4) ✅
↓
Pass2 (шаг 5):
5.1 → 5.2 → 5.3 → 5.4 → 5.5 ⚠️ ЧАСТИЧНО
↓
5.6 (для каждой мнемоники) ⚠️ ЧАСТИЧНО
↓
5.7 (диспетчер) ✅
↓
Тестирование (шаг 6) ⚠️ ЧАСТИЧНО
↓
Библиотека (шаг 7) ❌ ОТЛОЖЕНО
```
---
План готов. Когда скажете — начинаем писать код по шагам.
**Статус: v2.0 — абсолютная адресация реализована полностью.**
**Следующая цель: v3.0 — косвенная адресация `[reg]`.**
```

9
тесты/cleaner.py Normal file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env python3
import os
extensions = {'.аст', '.csv', '.токены', '.проход1', '.elf'}
for file in os.listdir('.'):
if os.path.isfile(file) and os.path.splitext(file)[1] in extensions:
os.remove(file)
#print(f'Удален: {file}')

View file

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

View file

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

View file

@ -0,0 +1,28 @@
.текст
.глобал _start
_start:
; 25 - длина сообщения из метки msg
; Запись значения 25 в переменную по метке
переместить_имм рдикс, 25
сохранить [переменная], рдикс
; перепишем для теста
переместить_имм рдикс, 0
; Системный вызов write(1, msg, len)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg
загрузить рдикс, [переменная]
вызов_системы
; Системный вызов exit()
переместить_имм раикс, 60 ; номер syscall exit = 60
переместить_имм рдиай, 44 ; код возврата (старый метод)
вызов_системы
.данные
msg: .строка "Тест пройден!\n" ; не менять их местами, чтоб не затереть текст
переменная: .байт 0 ; тут либо резервировать пустые, либо .bss изобретать

66
тесты/т1.квс Normal file
View file

@ -0,0 +1,66 @@
.глобал _start
.текст
_start:
нет_операции
вызов_системы
вернуться
втолкнуть раикс
втолкнуть р8
вытолкнуть раикс
вытолкнуть р8
переместить раикс, рбикс
переместить р8, р9
сравнить раикс, рбикс
сравнить р8, р9
проверить раикс, рбикс
проверить р8, р9
прибавить раикс, рбикс
прибавить р8, р9
вычесть раикс, рбикс
вычесть р8, р9
и раикс, рбикс
и р8, р9
или раикс, рбикс
или р8, р9
исключающее_или раикс, рбикс
исключающее_или р8, р9
увеличить раикс
увеличить р8
увеличить еаикс
уменьшить раикс
уменьшить р8
уменьшить еаикс
инвертировать раикс
инвертировать еаикс
отрицать раикс
отрицать еаикс
переместить_имм раикс, 10
переместить_имм рбикс, 5
умножить рбикс
переместить_имм раикс, 100
переместить_имм рбикс, 7
разделить рбикс
умножить_знаковое рбикс
разделить_знаковое рбикс
сдвиг_влево раикс, 2
сдвиг_вправо раикс, 2
сдвиг_арифметический_вправо раикс, 2
вращать_влево раикс, 2
вращать_вправо раикс, 2
переход метка_длинная
короткий_переход метка_короткая
цикл метка_короткая
вызвать метка_длинная
метка_длинная:
нет_операции
нет_операции
нет_операции
метка_короткая:
нет_операции
переместить_имм раикс, 60
переместить_имм рдикс, 0
вызов_системы