абсолютная адресация
This commit is contained in:
parent
520da3a8a3
commit
eaef835f4d
242
README.html
242
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 @@
|
|||
</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
100
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
|
||||
|
||||
## 📄 Лицензия
|
||||
|
||||
Только для образовательных целей. Используйте на свой страх и риск.
|
||||
|
|
@ -382,5 +454,3 @@ gdb ./программа.elf
|
|||
КВС Ассемблер | Экспериментальный проект | 2025
|
||||
Только для образовательных целей. Используйте на свой страх и риск.
|
||||
```
|
||||
|
||||
```
|
||||
68
kvs_lexer.py
68
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:
|
||||
# Определяем тип токена
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
175
kvs_pass1.py
175
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)
|
||||
|
||||
|
|
|
|||
1013
kvs_pass2.py
1013
kvs_pass2.py
File diff suppressed because it is too large
Load diff
1128
kvs_pass2_encoder.py
Normal file
1128
kvs_pass2_encoder.py
Normal file
File diff suppressed because it is too large
Load diff
307
todo.txt
307
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('<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
9
тесты/cleaner.py
Normal 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}')
|
||||
15
тесты/test_label.квс
Normal file
15
тесты/test_label.квс
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.текст
|
||||
.глобал _start
|
||||
|
||||
_start:
|
||||
; Запись значения 42 в переменную по метке
|
||||
переместить_имм раикс, 42
|
||||
сохранить [переменная], раикс
|
||||
|
||||
; Завершение с кодом 42
|
||||
переместить_имм раикс, 60
|
||||
загрузить рдиай, [переменная]
|
||||
вызов_системы
|
||||
|
||||
.данные
|
||||
переменная: .байт 0
|
||||
15
тесты/test_minimal.квс
Normal file
15
тесты/test_minimal.квс
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.текст
|
||||
.глобал _start
|
||||
|
||||
_start:
|
||||
; Запись значения 42 в переменную по адресу 0x402000
|
||||
переместить_имм раикс, 42
|
||||
сохранить [0x402000], раикс
|
||||
|
||||
; Завершение с кодом 42 (читаем из переменной)
|
||||
переместить_имм раикс, 60
|
||||
загрузить рдиай, [0x402000]
|
||||
вызов_системы
|
||||
|
||||
.данные
|
||||
тестовая_переменная: .байт 0
|
||||
28
тесты/test_success.квс
Normal file
28
тесты/test_success.квс
Normal 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
66
тесты/т1.квс
Normal 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
|
||||
вызов_системы
|
||||
Loading…
Reference in a new issue