абсолютная адресация
This commit is contained in:
parent
520da3a8a3
commit
eaef835f4d
242
README.html
242
README.html
|
|
@ -209,6 +209,14 @@
|
||||||
color: #ff8888;
|
color: #ff8888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
border-left: 3px solid #44ff44;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
background: rgba(0, 40, 0, 0.2);
|
||||||
|
color: #88ff88;
|
||||||
|
}
|
||||||
|
|
||||||
.process-diagram {
|
.process-diagram {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
|
|
@ -221,6 +229,22 @@
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-table td:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-done {
|
||||||
|
color: #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-partial {
|
||||||
|
color: #ffff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-pending {
|
||||||
|
color: #ff4444;
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
|
@ -266,6 +290,16 @@
|
||||||
margin: 0;
|
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) {
|
@media (max-width: 768px) {
|
||||||
body {
|
body {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
@ -330,6 +364,7 @@
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<p>Версия 2.0 | Экспериментальный проект | 2025</p>
|
<p>Версия 2.0 | Экспериментальный проект | 2025</p>
|
||||||
|
<p><span style="color: #00ff00;">✓ Абсолютная адресация реализована</span> | <span style="color: #ffff00;">⟳ Косвенная адресация в разработке</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>┌──[ ОГЛАВЛЕНИЕ ]────────────────────────────────────────────┐</h2>
|
<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>
|
||||||
<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>
|
</ul>
|
||||||
<h2>└──────────────────────────────────────────────────────────────┘</h2>
|
<h2>└──────────────────────────────────────────────────────────────┘</h2>
|
||||||
|
|
@ -367,8 +405,10 @@
|
||||||
<li><strong>Генерация исполняемых ELF-файлов</strong> — 64-битные ELF для Linux, готовые к запуску</li>
|
<li><strong>Генерация исполняемых ELF-файлов</strong> — 64-битные ELF для Linux, готовые к запуску</li>
|
||||||
<li><strong>Двухпроходная сборка</strong> — традиционная схема с разрешением меток</li>
|
<li><strong>Двухпроходная сборка</strong> — традиционная схема с разрешением меток</li>
|
||||||
<li><strong>Поддержка секций</strong> — разделение на <code>.текст</code> (код) и <code>.данные</code> (данные)</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>CSV-логирование</strong> — детальный файл <code>.csv</code> с соответствием адресов, байтов и исходных команд</li>
|
||||||
<li><strong>Более 50 инструкций</strong> — полный набор основных команд x86-64</li>
|
<li><strong>Более 50 инструкций</strong> — полный набор основных команд x86-64</li>
|
||||||
|
<li><strong>Рефакторинг</strong> — кодогенерация вынесена в отдельный модуль <code>kvs_pass2_encoder.py</code></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 id="архитектура">АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ</h2>
|
<h2 id="архитектура">АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ</h2>
|
||||||
|
|
@ -384,7 +424,7 @@ AST-представление (.аст)
|
||||||
▼ (kvs_pass1.py)
|
▼ (kvs_pass1.py)
|
||||||
Первый проход: размеры, метки (.проход1)
|
Первый проход: размеры, метки (.проход1)
|
||||||
│
|
│
|
||||||
▼ (kvs_pass2.py)
|
▼ (kvs_pass2.py + kvs_pass2_encoder.py)
|
||||||
Второй проход: генерация кода (.csv)
|
Второй проход: генерация кода (.csv)
|
||||||
│
|
│
|
||||||
▼ (kvs_builder.py)
|
▼ (kvs_builder.py)
|
||||||
|
|
@ -393,6 +433,19 @@ ELF-компоновщик (.elf)
|
||||||
Исполняемый файл!
|
Исполняемый файл!
|
||||||
</div>
|
</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>
|
<h2 id="установка">УСТАНОВКА И ЗАПУСК</h2>
|
||||||
<pre># Клонирование репозитория
|
<pre># Клонирование репозитория
|
||||||
git clone https://github.com/username/kvs.git
|
git clone https://github.com/username/kvs.git
|
||||||
|
|
@ -411,39 +464,78 @@ python3 kvs_build.py программа.квс
|
||||||
<pre>; Комментарий начинается с точки с запятой
|
<pre>; Комментарий начинается с точки с запятой
|
||||||
|
|
||||||
.данные ; Начало секции данных
|
.данные ; Начало секции данных
|
||||||
|
переменная: .байт 0 ; Переменная в памяти
|
||||||
message: .строка_нуль "Привет, мир!"
|
message: .строка_нуль "Привет, мир!"
|
||||||
counter: .байт 0x0A
|
|
||||||
|
|
||||||
.текст ; Начало секции кода
|
.текст ; Начало секции кода
|
||||||
.глобал _start ; Точка входа
|
.глобал _start ; Точка входа
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
; Аргументы для write(1, message, 13)
|
; Работа с памятью
|
||||||
|
переместить_имм раикс, 42
|
||||||
|
сохранить [переменная], раикс ; запись в память
|
||||||
|
|
||||||
|
загрузить рбикс, [переменная] ; чтение из памяти
|
||||||
|
|
||||||
|
; Системный вызов write
|
||||||
переместить_имм раикс, 1
|
переместить_имм раикс, 1
|
||||||
загрузить_адрес рсиай, message
|
загрузить_адрес рсиай, message
|
||||||
переместить_имм рдикс, 13
|
переместить_имм рдикс, 13
|
||||||
вызов_системы
|
вызов_системы
|
||||||
|
|
||||||
; Выход с кодом 0
|
; Выход с кодом из переменной
|
||||||
переместить_имм раикс, 60
|
переместить_имм раикс, 60
|
||||||
переместить_имм рдикс, 0
|
загрузить рдиай, [переменная]
|
||||||
вызов_системы</pre>
|
вызов_системы</pre>
|
||||||
|
|
||||||
<h2 id="команды">СИСТЕМА КОМАНД</h2>
|
<h2 id="команды">СИСТЕМА КОМАНД</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Категория</th><th>Примеры инструкций</th><th>Соответствие NASM</th></tr>
|
<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>, <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>, <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>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>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>, <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></td><td><code>syscall</code>, <code>hlt</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>
|
|
||||||
</table>
|
</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>
|
<h2 id="регистры">РЕГИСТРЫ ПРОЦЕССОРА</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr><th>64-битные</th><th>32-битные</th><th>16-битные</th><th>8-битные (мл.)</th><th>8-битные (ст.)</th></tr>
|
<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>
|
<h2 id="директивы">ДИРЕКТИВЫ АССЕМБЛЕРА</h2>
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Директива</th><th>Описание</th><th>Пример</th></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>.данные</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>.глобал _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>.строка_нуль "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>
|
<tr><td><code>.байт</code></td><td>Резервирование байтов</td><td><code>.байт 0x0A, 255, 0b0001</code></td></tr>
|
||||||
|
|
@ -479,19 +571,37 @@ _start:
|
||||||
_start:
|
_start:
|
||||||
; write(1, msg, 13)
|
; write(1, msg, 13)
|
||||||
переместить_имм раикс, 1
|
переместить_имм раикс, 1
|
||||||
загрузить_адрес рсиай, msg
|
переместить_имм рдиай, 1
|
||||||
|
переместить_имм рсиай, msg
|
||||||
переместить_имм рдикс, 13
|
переместить_имм рдикс, 13
|
||||||
вызов_системы
|
вызов_системы
|
||||||
|
|
||||||
; exit(0)
|
; exit(0)
|
||||||
переместить_имм раикс, 60
|
переместить_имм раикс, 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
|
<pre>.глобал _start
|
||||||
.текст
|
.текст
|
||||||
|
|
||||||
|
|
@ -523,13 +633,99 @@ loop:
|
||||||
<li><strong><code>программа.elf</code></strong> — исполняемый ELF-файл</li>
|
<li><strong><code>программа.elf</code></strong> — исполняемый ELF-файл</li>
|
||||||
</ul>
|
</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>
|
<h2 id="ограничения">ОГРАНИЧЕНИЯ</h2>
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Сложная адресация памяти</strong> — в текущей версии поддерживаются только простые формы <code>[регистр]</code>; полная поддержка <code>[база + индекс*масштаб + смещение]</code> в разработке</li>
|
<li><strong>Косвенная адресация памяти</strong> — поддерживается только абсолютная форма <code>[метка]</code>; адресация через регистры <code>[reg]</code> в разработке</li>
|
||||||
<li><strong>Отсутствие динамической памяти</strong> — нет встроенных malloc/free (можно реализовать через syscall brk)</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>Нет макросов и условной компиляции</strong></li>
|
||||||
<li><strong>Нет 32-битного режима</strong> — только 64-битные ELF</li>
|
|
||||||
<li><strong>Экспериментальный статус</strong> — некоторые инструкции могут быть реализованы не полностью</li>
|
<li><strong>Экспериментальный статус</strong> — некоторые инструкции могут быть реализованы не полностью</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
100
README.md
100
README.md
|
|
@ -1,9 +1,5 @@
|
||||||
```markdown
|
```markdown
|
||||||
# КВС (Компилятор B Семейства) — Ассемблер с русским синтаксисом для x86-64
|
# КВС (Компилятор В Семейства) — Ассемблер с русским синтаксисом для x86-64
|
||||||
|
|
||||||
[](https://github.com/username/kvs)
|
|
||||||
[](https://python.org)
|
|
||||||
[](LICENSE)
|
|
||||||
|
|
||||||
**КВС** — это экспериментальный ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит образовательный характер и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.
|
**КВС** — это экспериментальный ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит образовательный характер и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.
|
||||||
|
|
||||||
|
|
@ -20,6 +16,7 @@
|
||||||
- [Примеры программ](#-примеры-программ)
|
- [Примеры программ](#-примеры-программ)
|
||||||
- [Отладка](#-отладка-и-проверка)
|
- [Отладка](#-отладка-и-проверка)
|
||||||
- [Ограничения](#-ограничения)
|
- [Ограничения](#-ограничения)
|
||||||
|
- [План развития](#-план-развития)
|
||||||
- [Лицензия](#-лицензия)
|
- [Лицензия](#-лицензия)
|
||||||
|
|
||||||
## ✨ Особенности
|
## ✨ Особенности
|
||||||
|
|
@ -28,9 +25,11 @@
|
||||||
- **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску
|
- **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску
|
||||||
- **Двухпроходная сборка** — традиционная схема с разрешением меток
|
- **Двухпроходная сборка** — традиционная схема с разрешением меток
|
||||||
- **Поддержка секций** — разделение на `.текст` (код) и `.данные` (данные)
|
- **Поддержка секций** — разделение на `.текст` (код) и `.данные` (данные)
|
||||||
|
- **Абсолютная адресация памяти** — чтение и запись по меткам `[переменная]` и абсолютным адресам `[0x402000]` с RIP-relative кодированием
|
||||||
- **CSV-логирование** — детальный файл с соответствием адресов, байтов и исходных команд
|
- **CSV-логирование** — детальный файл с соответствием адресов, байтов и исходных команд
|
||||||
- **Более 50 инструкций** — полный набор основных команд x86-64
|
- **Более 50 инструкций** — полный набор основных команд x86-64
|
||||||
- **Минимализм зависимостей** — только стандартная библиотека Python
|
- **Минимализм зависимостей** — только стандартная библиотека Python
|
||||||
|
- **Рефакторинг** — кодогенерация вынесена в отдельный модуль `kvs_pass2_encoder.py`
|
||||||
|
|
||||||
## 🏗 Архитектура
|
## 🏗 Архитектура
|
||||||
|
|
||||||
|
|
@ -48,7 +47,7 @@ AST-представление (.аст)
|
||||||
▼ (kvs_pass1.py)
|
▼ (kvs_pass1.py)
|
||||||
Первый проход: размеры, метки (.проход1)
|
Первый проход: размеры, метки (.проход1)
|
||||||
│
|
│
|
||||||
▼ (kvs_pass2.py)
|
▼ (kvs_pass2.py + kvs_pass2_encoder.py)
|
||||||
Второй проход: генерация кода (.csv)
|
Второй проход: генерация кода (.csv)
|
||||||
│
|
│
|
||||||
▼ (kvs_builder.py)
|
▼ (kvs_builder.py)
|
||||||
|
|
@ -65,7 +64,8 @@ ELF-компоновщик (.elf)
|
||||||
| `kvs_lexer.py` | Лексический анализ, разбор на токены |
|
| `kvs_lexer.py` | Лексический анализ, разбор на токены |
|
||||||
| `kvs_parser.py` | Синтаксический анализ, построение AST |
|
| `kvs_parser.py` | Синтаксический анализ, построение AST |
|
||||||
| `kvs_pass1.py` | Первый проход: вычисление размеров и адресов меток |
|
| `kvs_pass1.py` | Первый проход: вычисление размеров и адресов меток |
|
||||||
| `kvs_pass2.py` | Второй проход: генерация машинного кода |
|
| `kvs_pass2.py` | Второй проход: обход AST, генерация CSV |
|
||||||
|
| `kvs_pass2_encoder.py` | Кодировщик инструкций (все encode_* функции) |
|
||||||
| `kvs_builder.py` | Сборка ELF-файла из CSV |
|
| `kvs_builder.py` | Сборка ELF-файла из CSV |
|
||||||
| `kvs_data.py` | Общие данные (регистры, инструкции) |
|
| `kvs_data.py` | Общие данные (регистры, инструкции) |
|
||||||
|
|
||||||
|
|
@ -156,6 +156,20 @@ _start: ; метка _start
|
||||||
переместить_имм раикс, 42 ; комментарий после инструкции
|
переместить_имм раикс, 42 ; комментарий после инструкции
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Работа с памятью (абсолютная адресация)
|
||||||
|
|
||||||
|
```asm
|
||||||
|
; Запись значения в память по метке
|
||||||
|
переместить_имм раикс, 42
|
||||||
|
сохранить [переменная], раикс
|
||||||
|
|
||||||
|
; Чтение значения из памяти
|
||||||
|
загрузить рбикс, [переменная]
|
||||||
|
|
||||||
|
; Запись по абсолютному адресу
|
||||||
|
сохранить [0x402000], раикс
|
||||||
|
```
|
||||||
|
|
||||||
## 📖 Система команд
|
## 📖 Система команд
|
||||||
|
|
||||||
### Загрузка констант
|
### Загрузка констант
|
||||||
|
|
@ -170,6 +184,19 @@ _start: ; метка _start
|
||||||
переместить_имм ал, 0xFF ; AL = 255
|
переместить_имм ал, 0xFF ; AL = 255
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Работа с памятью
|
||||||
|
|
||||||
|
| Инструкция | Синтаксис | Описание |
|
||||||
|
|------------|-----------|----------|
|
||||||
|
| `загрузить` | `загрузить рег, [метка/адрес]` | Загрузить значение из памяти |
|
||||||
|
| `сохранить` | `сохранить [метка/адрес], рег` | Сохранить значение в память |
|
||||||
|
|
||||||
|
```asm
|
||||||
|
загрузить раикс, [переменная] ; RAX = *переменная
|
||||||
|
сохранить [результат], раикс ; *результат = RAX
|
||||||
|
загрузить рбикс, [0x402000] ; загрузка по абсолютному адресу
|
||||||
|
```
|
||||||
|
|
||||||
### Арифметика и логика
|
### Арифметика и логика
|
||||||
|
|
||||||
| Инструкция | Синтаксис | Описание |
|
| Инструкция | Синтаксис | Описание |
|
||||||
|
|
@ -250,6 +277,29 @@ _start: ; метка _start
|
||||||
|
|
||||||
## 📝 Примеры программ
|
## 📝 Примеры программ
|
||||||
|
|
||||||
|
### Работа с памятью (переменные)
|
||||||
|
|
||||||
|
```asm
|
||||||
|
.текст
|
||||||
|
.глобал _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
; Запись значения в переменную
|
||||||
|
переместить_имм раикс, 42
|
||||||
|
сохранить [переменная], раикс
|
||||||
|
|
||||||
|
; Чтение значения из переменной
|
||||||
|
загрузить рбикс, [переменная]
|
||||||
|
|
||||||
|
; Завершение с кодом возврата
|
||||||
|
переместить_имм раикс, 60
|
||||||
|
загрузить рдиай, [переменная]
|
||||||
|
вызов_системы
|
||||||
|
|
||||||
|
.данные
|
||||||
|
переменная: .байт 0
|
||||||
|
```
|
||||||
|
|
||||||
### Цикл и условный переход
|
### Цикл и условный переход
|
||||||
|
|
||||||
```asm
|
```asm
|
||||||
|
|
@ -263,7 +313,7 @@ loop:
|
||||||
переместить_имм раикс, 1
|
переместить_имм раикс, 1
|
||||||
переместить_имм рдиай, 1
|
переместить_имм рдиай, 1
|
||||||
переместить_имм рсиай, msg
|
переместить_имм рсиай, msg
|
||||||
переместить_имм рдикс, len_msg
|
переместить_имм рдикс, 10
|
||||||
вызов_системы
|
вызов_системы
|
||||||
|
|
||||||
уменьшить рсикс ; счетчик--
|
уменьшить рсикс ; счетчик--
|
||||||
|
|
@ -275,8 +325,7 @@ loop:
|
||||||
вызов_системы
|
вызов_системы
|
||||||
|
|
||||||
.данные
|
.данные
|
||||||
msg: .строка_нуль "Iteration\n"
|
msg: .строка "Iteration\n"
|
||||||
.константа len_msg = 10
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Вычисление суммы чисел от 1 до 10
|
### Вычисление суммы чисел от 1 до 10
|
||||||
|
|
@ -365,15 +414,38 @@ gdb ./программа.elf
|
||||||
- ✅ Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`)
|
- ✅ Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`)
|
||||||
- ✅ Директива `.байт` для raw-данных
|
- ✅ Директива `.байт` для raw-данных
|
||||||
- ✅ Директива `.константа` для имён констант
|
- ✅ Директива `.константа` для имён констант
|
||||||
|
- ✅ **Абсолютная адресация памяти** `[метка]` и `[число]` (RIP-relative)
|
||||||
|
|
||||||
### Не поддерживается
|
### Не поддерживается (в разработке)
|
||||||
|
|
||||||
- ❌ Косвенная адресация памяти `[reg+disp]` (в разработке)
|
- ❌ Косвенная адресация `[reg]` — доступ к памяти через регистр
|
||||||
- ❌ Инструкции для работы со стеком (PUSH, POP, CALL, RET)
|
- ❌ Адресация со смещением `[reg + disp]`
|
||||||
|
- ❌ Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация
|
||||||
|
- ❌ Инструкции для работы со стеком (PUSH, POP, CALL, RET) — частично
|
||||||
- ❌ Поддержка плавающей запятой (FPU/SSE)
|
- ❌ Поддержка плавающей запятой (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
|
КВС Ассемблер | Экспериментальный проект | 2025
|
||||||
Только для образовательных целей. Используйте на свой страх и риск.
|
Только для образовательных целей. Используйте на свой страх и риск.
|
||||||
```
|
|
||||||
|
|
||||||
```
|
```
|
||||||
70
kvs_lexer.py
70
kvs_lexer.py
|
|
@ -6,15 +6,34 @@
|
||||||
Принимает: исходный .квс файл
|
Принимает: исходный .квс файл
|
||||||
Выдаёт: текстовый файл с токенами (по одному токену на строку)
|
Выдаёт: текстовый файл с токенами (по одному токену на строку)
|
||||||
Формат вывода: ТИП:ЗНАЧЕНИЕ
|
Формат вывода: ТИП:ЗНАЧЕНИЕ
|
||||||
|
|
||||||
|
Поддерживает сложную адресацию:
|
||||||
|
- [ ] - квадратные скобки
|
||||||
|
- + - плюс
|
||||||
|
- - - минус
|
||||||
|
- * - звёздочка (умножение/масштаб)
|
||||||
|
- NUMBER - числа (десятичные, шестнадцатеричные, отрицательные)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
def error_unterminated_string(line_num):
|
def error_unterminated_string(line_num):
|
||||||
return f"Незакрытая кавычка в строке на строке {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):
|
def tokenize_line(line, line_num):
|
||||||
"""Разбирает строку в токены"""
|
"""Разбирает строку в токены"""
|
||||||
|
# Удаляем комментарии
|
||||||
semi = line.find(';')
|
semi = line.find(';')
|
||||||
if semi != -1:
|
if semi != -1:
|
||||||
line = line[:semi]
|
line = line[:semi]
|
||||||
|
|
@ -25,18 +44,21 @@ def tokenize_line(line, line_num):
|
||||||
tokens = []
|
tokens = []
|
||||||
i = 0
|
i = 0
|
||||||
n = len(line)
|
n = len(line)
|
||||||
|
|
||||||
while i < n:
|
while i < n:
|
||||||
ch = line[i]
|
ch = line[i]
|
||||||
|
|
||||||
|
# Пропускаем пробелы
|
||||||
if ch.isspace():
|
if ch.isspace():
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Строковые литералы
|
||||||
if ch == '"':
|
if ch == '"':
|
||||||
i += 1
|
i += 1
|
||||||
s = ''
|
s = ''
|
||||||
while i < n and line[i] != '"':
|
while i < n and line[i] != '"':
|
||||||
if line[i] == '\\' and i + 1 < n:
|
if line[i] == '\\' and i + 1 < n:
|
||||||
# Оставляем escape-последовательности как есть
|
|
||||||
s += '\\' + line[i + 1]
|
s += '\\' + line[i + 1]
|
||||||
i += 2
|
i += 2
|
||||||
else:
|
else:
|
||||||
|
|
@ -48,6 +70,7 @@ def tokenize_line(line, line_num):
|
||||||
tokens.append(('STRING', s))
|
tokens.append(('STRING', s))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Одиночные символы-разделители
|
||||||
if ch == ',':
|
if ch == ',':
|
||||||
tokens.append(('COMMA', ','))
|
tokens.append(('COMMA', ','))
|
||||||
i += 1
|
i += 1
|
||||||
|
|
@ -58,12 +81,53 @@ def tokenize_line(line, line_num):
|
||||||
i += 1
|
i += 1
|
||||||
continue
|
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
|
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
|
j += 1
|
||||||
word = line[i:j]
|
word = line[i:j]
|
||||||
|
|
||||||
if word:
|
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
|
i = j
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@
|
||||||
Парсер КВС
|
Парсер КВС
|
||||||
Принимает: файл токенов от лексера
|
Принимает: файл токенов от лексера
|
||||||
Выдаёт: текстовый файл с AST-структурой
|
Выдаёт: текстовый файл с AST-структурой
|
||||||
|
|
||||||
|
Поддерживает сложную адресацию:
|
||||||
|
- [регистр + регистр*масштаб + смещение]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -51,6 +54,13 @@ class Parser:
|
||||||
raise ValueError(f"Ожидался {expected_type}, получено {tok}")
|
raise ValueError(f"Ожидался {expected_type}, получено {tok}")
|
||||||
return 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):
|
def parse_line(self):
|
||||||
tok = self.peek()
|
tok = self.peek()
|
||||||
if tok is None:
|
if tok is None:
|
||||||
|
|
@ -63,6 +73,7 @@ class Parser:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if tok[0] == 'WORD':
|
if tok[0] == 'WORD':
|
||||||
|
# Проверяем, есть ли после слова двоеточие (метка)
|
||||||
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'COLON':
|
if self.pos + 1 < len(self.tokens) and self.tokens[self.pos + 1][0] == 'COLON':
|
||||||
label_name = self.next_token()[1]
|
label_name = self.next_token()[1]
|
||||||
self.next_token() # COLON
|
self.next_token() # COLON
|
||||||
|
|
@ -107,10 +118,12 @@ class Parser:
|
||||||
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}")
|
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:{string_tok[1]}")
|
||||||
elif word == '.константа':
|
elif word == '.константа':
|
||||||
name = self.expect('WORD')[1]
|
name = self.expect('WORD')[1]
|
||||||
|
# Пропускаем '=', если есть
|
||||||
eq_tok = self.peek()
|
eq_tok = self.peek()
|
||||||
if eq_tok and eq_tok[0] == 'WORD' and eq_tok[1] == '=':
|
if eq_tok and eq_tok[0] == 'WORD' and eq_tok[1] == '=':
|
||||||
self.next_token()
|
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]}")
|
self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}")
|
||||||
elif word == '.байт':
|
elif word == '.байт':
|
||||||
bytes_list = []
|
bytes_list = []
|
||||||
|
|
@ -122,11 +135,71 @@ class Parser:
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
else:
|
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))
|
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list))
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Неизвестная директива: {word}")
|
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):
|
def parse_instruction(self, mnemonic):
|
||||||
if mnemonic not in INSTRUCTIONS:
|
if mnemonic not in INSTRUCTIONS:
|
||||||
raise ValueError(f"Неизвестная инструкция: {mnemonic}")
|
raise ValueError(f"Неизвестная инструкция: {mnemonic}")
|
||||||
|
|
@ -139,7 +212,7 @@ class Parser:
|
||||||
if tok[0] == 'COMMA':
|
if tok[0] == 'COMMA':
|
||||||
self.next_token()
|
self.next_token()
|
||||||
continue
|
continue
|
||||||
operands.append(self.next_token()[1])
|
operands.append(self.parse_operand())
|
||||||
|
|
||||||
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(operands))
|
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 sys
|
||||||
|
import re
|
||||||
sys.path.insert(0, '.')
|
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):
|
def unescape_string(s):
|
||||||
"""Преобразует escape-последовательности в реальные символы"""
|
"""Преобразует escape-последовательности в реальные символы"""
|
||||||
|
|
@ -38,6 +39,113 @@ def read_ast(input_file):
|
||||||
ast_lines.append(line.strip())
|
ast_lines.append(line.strip())
|
||||||
return ast_lines
|
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:
|
class Pass1:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.labels = {}
|
self.labels = {}
|
||||||
|
|
@ -68,11 +176,11 @@ class Pass1:
|
||||||
self.position[".data"] += size
|
self.position[".data"] += size
|
||||||
elif directive == '.константа':
|
elif directive == '.константа':
|
||||||
name = parts[2]
|
name = parts[2]
|
||||||
value = parts[3]
|
value_str = parts[3]
|
||||||
if value.isdigit():
|
if value_str.isdigit():
|
||||||
self.symbols[name] = int(value)
|
self.symbols[name] = int(value_str)
|
||||||
elif value.startswith('0x'):
|
elif value_str.startswith('0x'):
|
||||||
self.symbols[name] = int(value, 16)
|
self.symbols[name] = int(value_str, 16)
|
||||||
else:
|
else:
|
||||||
self.symbols[name] = 0
|
self.symbols[name] = 0
|
||||||
elif directive == '.байт':
|
elif directive == '.байт':
|
||||||
|
|
@ -90,10 +198,27 @@ class Pass1:
|
||||||
elif line_type == "INSTR":
|
elif line_type == "INSTR":
|
||||||
mnemonic = parts[1]
|
mnemonic = parts[1]
|
||||||
sec = parts[2]
|
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
|
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 = {
|
size_map = {
|
||||||
"переместить_имм": 10,
|
"переместить_имм": 10,
|
||||||
"сравнить_с": 7,
|
"сравнить_с": 7,
|
||||||
|
|
@ -127,6 +252,40 @@ class Pass1:
|
||||||
"прибавить": 3,
|
"прибавить": 3,
|
||||||
"увеличить": 3,
|
"увеличить": 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)
|
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)
|
||||||
|
|
||||||
- Поддержки `+`, `-`, `*`, `[`, `]`
|
### Уже реализовано:
|
||||||
- Новых мнемоник для работы с памятью
|
- ✅ Лексер: токены `[`, `]`, `NUMBER`
|
||||||
- Адресации всех типов: `[reg]`, `[disp]`, `[reg+disp]`, `[reg+reg*scale]`, `[reg+reg*scale+disp]`
|
- ✅ Парсер: распознавание `[метка]` и `[число]`
|
||||||
- Корректной работы лексера, парсера, pass1, pass2, сборки ELF
|
- ✅ Pass1: корректный расчёт размера (7 байт для MOV с памятью)
|
||||||
|
- ✅ Pass2: RIP-relative кодирование для абсолютной адресации
|
||||||
|
- ✅ Поддержка меток в операндах памяти `[переменная]`
|
||||||
|
- ✅ Рефакторинг: `kvs_pass2_encoder.py` отдельно
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# ПЛАН РЕАЛИЗАЦИИ СЛОЖНОЙ АДРЕСАЦИИ (ПОЛНАЯ ВЕРСИЯ)
|
|
||||||
|
|
||||||
## 1. Общая схема изменений по модулям
|
## 1. Общая схема изменений по модулям
|
||||||
|
|
||||||
| Модуль | Что меняется | Сложность |
|
| Модуль | Что меняется | Сложность | Статус |
|
||||||
|-------------------|------------------------------------|-----------|
|
|--------|--------------|-----------|--------|
|
||||||
| `kvs_lexer.py` | новые токены: `[`, `]`, `+`, `-`, `*`, `NUMBER` | низкая |
|
| `kvs_lexer.py` | новые токены: `[`, `]`, `+`, `-`, `*`, `NUMBER` | низкая | ✅ ГОТОВ |
|
||||||
| `kvs_parser.py` | новая логика операндов: `parse_operand()`, `parse_memory_operand()` | высокая |
|
| `kvs_parser.py` | новая логика операндов: `parse_operand()`, `parse_memory_operand()` | высокая | ✅ ГОТОВ (базово) |
|
||||||
| `kvs_data.py` | активация и уточнение форматов инструкций | средняя |
|
| `kvs_data.py` | активация и уточнение форматов инструкций | средняя | ⚠️ ТРЕБУЕТ ДОРАБОТКИ |
|
||||||
| `kvs_pass1.py` | динамический рассчёт размера инструкций с памятью | высокая |
|
| `kvs_pass1.py` | динамический рассчёт размера инструкций с памятью | высокая | ✅ ГОТОВ (для абсолютной) |
|
||||||
| `kvs_pass2.py` | разбор адресации → ModR/M + SIB + disp, генерация кода | очень высокая |
|
| `kvs_pass2_encoder.py` | разбор адресации → ModR/M + SIB + disp, генерация кода | очень высокая | ⚠️ ЧАСТИЧНО (только абсолютная) |
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Детальный план по шагам (в порядке реализации)
|
## 2. Детальный план по шагам (в порядке реализации)
|
||||||
|
|
||||||
### Шаг 1 — Лексер (`kvs_lexer.py`)
|
### Шаг 1 — Лексер (`kvs_lexer.py`) ✅ РЕАЛИЗОВАН
|
||||||
|
|
||||||
**Что сделать:**
|
**Что сделано:**
|
||||||
- Добавить токены:
|
- Добавлены токены: `LBRACKET` (`[`), `RBRACKET` (`]`), `PLUS` (`+`), `MINUS` (`-`), `STAR` (`*`), `NUMBER`
|
||||||
- `LBRACKET` → `[`
|
- Числа отделены от слов (поддержка десятичных, шестнадцатеричных, отрицательных)
|
||||||
- `RBRACKET` → `]`
|
|
||||||
- `PLUS` → `+`
|
|
||||||
- `MINUS` → `-`
|
|
||||||
- `STAR` → `*`
|
|
||||||
- Добавить `NUMBER` (отдельно от `WORD`) для:
|
|
||||||
- десятичных чисел (`123`)
|
|
||||||
- отрицательных (`-456`)
|
|
||||||
- шестнадцатеричных (`0x7F`)
|
|
||||||
|
|
||||||
** парсер:**
|
|
||||||
- Парсер сможет отличать `[` от начала слова
|
|
||||||
- Числа не будут смешиваться с метками/регистрами
|
|
||||||
|
|
||||||
**Результат:** новые типы токенов на выходе лексера.
|
**Результат:** новые типы токенов на выходе лексера.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Шаг 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()`:
|
2.2. Реализован `parse_memory_operand()`:
|
||||||
- Если токен `LBRACKET` → вызвать `parse_memory_operand()`
|
|
||||||
- Иначе → вернуть значение токена (регистр/число/метка/строка)
|
|
||||||
|
|
||||||
2.3. Реализовать `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. Активировать (или добавить) записи для:
|
3.1. Активировать (или добавить) записи для:
|
||||||
|
|
||||||
|
|
@ -94,41 +79,31 @@
|
||||||
| `вытолкнуть_в_память` | `8F /0` | `pop_mem` |
|
| `вытолкнуть_в_память` | `8F /0` | `pop_mem` |
|
||||||
| `обменять_с_памятью` | `48 87` | `xchg_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]`
|
- Парсит строку `[раикс + рсикс*8 + 16]`
|
||||||
- Определяет:
|
- Определяет наличие SIB, размер смещения
|
||||||
- наличие SIB (нужен, если есть индексный регистр)
|
|
||||||
- размер смещения (disp8 = 1 байт, disp32 = 4 байта)
|
|
||||||
- Возвращает дополнительные байты: 0, 1, 4 или 5 (SIB + disp)
|
|
||||||
|
|
||||||
4.2. Модифицировать `estimate_size()`:
|
4.2. Модифицирована `estimate_size()`:
|
||||||
- Для инструкций с памятью (список из шага 3) считать:
|
- Для инструкций `загрузить`, `сохранить`, `загрузить_адрес` возвращает `7` (REX + opcode + ModRM + disp32)
|
||||||
- базовый размер opcode (REX + опкод)
|
|
||||||
- +1 байт (ModR/M)
|
|
||||||
- +1 байт (SIB, если нужен)
|
|
||||||
- + размер смещения (1 или 4)
|
|
||||||
|
|
||||||
**Результат:** правильное вычисление `.text` и адресов меток.
|
**Ограничения:**
|
||||||
|
- Для сложных форм адресации размер не рассчитывается динамически
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Шаг 5 — Второй проход (`kvs_pass2.py`) — ядро адресации
|
### Шаг 5 — Второй проход (`kvs_pass2_encoder.py`) ⚠️ ЧАСТИЧНО РЕАЛИЗОВАН
|
||||||
|
|
||||||
Это самый объёмный шаг. Реализовать **поэтапно**:
|
#### 5.1. Структура данных для режима адресации ⚠️ НЕ РЕАЛИЗОВАНА
|
||||||
|
|
||||||
#### 5.1. Структура данных для режима адресации
|
Нужно создать класс/словарь `AddressingMode`:
|
||||||
|
|
||||||
Создать класс/словарь `AddressingMode`:
|
|
||||||
- `has_base` (bool)
|
- `has_base` (bool)
|
||||||
- `base_reg` (int 0–15 или None)
|
- `base_reg` (int 0–15 или None)
|
||||||
- `has_index` (bool)
|
- `has_index` (bool)
|
||||||
|
|
@ -138,136 +113,134 @@
|
||||||
- `disp_value` (int)
|
- `disp_value` (int)
|
||||||
- `disp_size` (1 или 4)
|
- `disp_size` (1 или 4)
|
||||||
|
|
||||||
#### 5.2. Парсер адресации
|
#### 5.2. Парсер адресации ⚠️ ЧАСТИЧНО РЕАЛИЗОВАН
|
||||||
|
|
||||||
Написать `parse_addressing_mode(operand_str)`:
|
Реализована `parse_memory_operand()` в `kvs_pass2_encoder.py`:
|
||||||
- Вход: `[раикс + рсикс*8 + 16]`
|
- Поддерживает `[число]` и `[метка]`
|
||||||
- Выход: `AddressingMode`
|
- **Нет поддержки:** `[reg]`, `[reg+disp]`, `[reg+reg*scale]`
|
||||||
- Поддержка всех форм из шага 2.3
|
|
||||||
|
|
||||||
#### 5.3. Кодирование ModR/M
|
#### 5.3. Кодирование ModR/M ⚠️ ЧАСТИЧНО РЕАЛИЗОВАНО
|
||||||
|
|
||||||
Написать `encode_modrm_reg_mem(reg, addr_mode)`:
|
Функция `encode_modrm_reg_mem_absolute()`:
|
||||||
- Формирует байт ModR/M:
|
- Использует `mod=00, r/m=101` для RIP-relative
|
||||||
- `mod` (00, 01, 10, 11)
|
- **Нет поддержки:** других режимов адресации
|
||||||
- `reg` (3 бита — номер регистра)
|
|
||||||
- `r/m` (3 бита — зависит от наличия SIB)
|
|
||||||
|
|
||||||
#### 5.4. Кодирование SIB
|
#### 5.4. Кодирование SIB ❌ НЕ РЕАЛИЗОВАНО
|
||||||
|
|
||||||
Написать `encode_sib(addr_mode)`:
|
Функция `encode_sib()` отсутствует.
|
||||||
- Формирует байт SIB:
|
|
||||||
- `scale` (2 бита)
|
|
||||||
- `index` (3 бита)
|
|
||||||
- `base` (3 бита)
|
|
||||||
|
|
||||||
#### 5.5. Кодирование смещения
|
#### 5.5. Кодирование смещения ⚠️ ЧАСТИЧНО РЕАЛИЗОВАНО
|
||||||
|
|
||||||
Написать `encode_displacement(addr_mode, current_pos, vaddr_text)`:
|
`encode_displacement()` использует `struct.pack('<i', offset)` для disp32.
|
||||||
- Для `disp8` → 1 байт со знаком
|
|
||||||
- Для `disp32` → 4 байта (little-endian)
|
|
||||||
|
|
||||||
#### 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` | ❌ Не реализована |
|
||||||
|
|
||||||
| Функция | Для какой инструкции |
|
#### 5.7. Диспетчер ✅ РЕАЛИЗОВАН
|
||||||
|---------|----------------------|
|
|
||||||
| `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` | `обменять_с_памятью` |
|
|
||||||
|
|
||||||
Каждая функция:
|
`encode_instruction()` содержит ветки для всех инструкций, включая:
|
||||||
- Берёт операнды (регистр + операнд памяти)
|
- `загрузить`, `сохранить`, `загрузить_адрес` (с памятью)
|
||||||
- Вызывает `parse_addressing_mode` для операнда памяти
|
- `переместить_имм`, `переместить` (без памяти)
|
||||||
- Формирует REX-префикс (W, R, B, X биты)
|
|
||||||
- Добавляет opcode
|
|
||||||
- Добавляет ModR/M
|
|
||||||
- Добавляет SIB (если нужен)
|
|
||||||
- Добавляет disp
|
|
||||||
|
|
||||||
#### 5.7. Диспетчер
|
|
||||||
|
|
||||||
Модифицировать `encode_instruction()`:
|
|
||||||
- Добавить ветки для всех новых мнемоник
|
|
||||||
- Вызывать соответствующие функции из 5.6
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Шаг 6 — Тестирование (итеративное)
|
### Шаг 6 — Тестирование
|
||||||
|
|
||||||
Тестировать после каждого подшага 5.x:
|
Тесты для проверки (поэтапно):
|
||||||
|
|
||||||
| Тест | Что проверяет |
|
| Тест | Что проверяет | Статус |
|
||||||
|------|---------------|
|
|------|---------------|--------|
|
||||||
| `загрузить раикс, [рбикс]` | ModR/M, нет SIB, нет disp |
|
| `загрузить раикс, [число]` | Абсолютная адресация | ✅ ПРОЙДЕН |
|
||||||
| `загрузить раикс, [1234]` | disp32, r/m=101 |
|
| `сохранить [число], раикс` | Абсолютная адресация (запись) | ✅ ПРОЙДЕН |
|
||||||
| `загрузить раикс, [рбикс + 8]` | disp8 |
|
| `загрузить раикс, [метка]` | Адресация по метке | ✅ ПРОЙДЕН |
|
||||||
| `загрузить раикс, [рбикс + 1000]` | disp32 |
|
| `сохранить [метка], раикс` | Адресация по метке (запись) | ✅ ПРОЙДЕН |
|
||||||
| `загрузить раикс, [рбикс + рсикс]` | SIB, нет disp |
|
| `загрузить раикс, [рбикс]` | Косвенная адресация | ❌ НЕ РЕАЛИЗОВАНО |
|
||||||
| `загрузить раикс, [рбикс + рсикс*8 + 16]` | SIB + disp8 |
|
| `загрузить раикс, [рбикс + 8]` | Со смещением | ❌ НЕ РЕАЛИЗОВАНО |
|
||||||
| `сохранить [рдиай], рсикс` | MOV mem, reg |
|
| `загрузить раикс, [рбикс + рсикс*8]` | С индексом | ❌ НЕ РЕАЛИЗОВАНО |
|
||||||
| `загрузить_адрес р11, [рбикс + рсикс*4]` | LEA |
|
| `загрузить раикс, [рбикс + рсикс*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.квс` | статические данные для библиотеки |
|
| `libkvs_data.квс` | статические данные для библиотеки |
|
||||||
|
|
||||||
Библиотека будет использовать сложную адресацию для работы со структурами и кучей.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Зависимости между подзадачами
|
## 3. Следующие шаги (приоритеты)
|
||||||
|
|
||||||
```
|
### 🔴 Приоритет 1 (критично):
|
||||||
Лексер (шаг 1)
|
1. **Косвенная адресация `[reg]`** — основа для всех остальных форм
|
||||||
↓
|
- Нужно: `mod=00, r/m=reg` (без SIB, без disp)
|
||||||
Парсер (шаг 2)
|
|
||||||
↓
|
### 🟡 Приоритет 2 (важно):
|
||||||
Данные инструкций (шаг 3)
|
2. **Адресация со смещением `[reg + disp]`**
|
||||||
↓
|
- disp8 для малых смещений (`mod=01`)
|
||||||
Pass1 (шаг 4) ← может тестироваться отдельно
|
- disp32 для больших (`mod=10`)
|
||||||
↓
|
|
||||||
Pass2:
|
### 🟢 Приоритет 3 (расширение):
|
||||||
5.1 → 5.2 → 5.3 → 5.4 → 5.5
|
3. **Сложная адресация с индексом `[reg + reg*scale]`**
|
||||||
↓
|
- Требует SIB байта
|
||||||
5.6 (для каждой мнемоники)
|
4. **Полная SIB-адресация `[reg + reg*scale + disp]`**
|
||||||
↓
|
5. **LEA для меток** `загрузить_адрес reg, [метка]`
|
||||||
5.7 (диспетчер)
|
6. **`.bss` секция** для неинициализированных данных
|
||||||
↓
|
|
||||||
Тестирование (шаг 6)
|
|
||||||
↓
|
|
||||||
Библиотека (шаг 7)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Итог: что получим на выходе
|
## 4. Итог: что получим на выходе
|
||||||
|
|
||||||
- **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
|
- ✅ **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
|
||||||
- **Парсер** понимает `[раикс + рсикс*8 - 16]`
|
- ✅ **Парсер** понимает `[раикс + рсикс*8 - 16]`
|
||||||
- **Pass1** правильно считает размер инструкций с памятью
|
- ✅ **Pass1** правильно считает размер инструкций с памятью (для абсолютной)
|
||||||
- **Pass2** генерирует правильный машинный код для всех форм адресации
|
- ⚠️ **Pass2** генерирует код только для абсолютной адресации
|
||||||
- **ELF** содержит корректные инструкции x86-64 с ModR/M, SIB, disp
|
- ⚠️ **ELF** содержит корректные инструкции x86-64 с RIP-relative
|
||||||
- **Библиотека** позволяет писать более сложные программы (malloc, строки)
|
- ❌ **Библиотека** требует полной адресации
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 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