пересборка

This commit is contained in:
mzhoot 2026-05-02 23:04:46 +03:00
parent 054dcad97d
commit 4c5a952bae
99 changed files with 4683 additions and 115046 deletions

559
README.html Normal file
View file

@ -0,0 +1,559 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>КВС - Ассемблер с русским синтаксисом</title>
<style>
/* Сброс и базовые настройки */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', 'Courier', monospace;
font-size: 14px;
line-height: 1.4;
color: #00ff00;
background-color: #000000;
background-image:
linear-gradient(rgba(0, 20, 0, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 20, 0, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
padding: 20px;
min-height: 100vh;
text-shadow: 0 0 5px rgba(0, 255, 0, 0.3);
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.cursor {
display: inline-block;
width: 8px;
height: 16px;
background-color: #00ff00;
margin-left: 2px;
vertical-align: text-bottom;
animation: blink 1s infinite;
}
.terminal {
max-width: 1200px;
margin: 0 auto;
border: 2px solid #00aa00;
padding: 20px;
position: relative;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.2), inset 0 0 20px rgba(0, 255, 0, 0.1);
}
.terminal::before {
content: "┌───[ КВС v2.0 ]───┐";
position: absolute;
top: -11px;
left: 20px;
background: #000;
padding: 0 10px;
font-size: 12px;
color: #00aa00;
}
.terminal::after {
content: "└─────────────────────┘";
position: absolute;
bottom: -11px;
right: 20px;
background: #000;
padding: 0 10px;
font-size: 12px;
color: #00aa00;
}
h1, h2, h3, h4 {
color: #00ff00;
margin: 25px 0 15px 0;
border-bottom: 1px dashed #00aa00;
padding-bottom: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
h1 {
font-size: 24px;
color: #00ff88;
text-align: center;
margin-top: 0;
padding: 10px;
border: 1px solid #00aa00;
background: rgba(0, 20, 0, 0.3);
}
h2 {
font-size: 18px;
color: #00ff66;
}
h3 {
font-size: 16px;
color: #00ff44;
}
p {
margin: 15px 0;
text-align: justify;
}
ul, ol {
margin: 15px 0 15px 30px;
}
li {
margin: 8px 0;
position: relative;
}
li::before {
content: "";
color: #00ff00;
position: absolute;
left: -15px;
}
a {
color: #00ffff;
text-decoration: none;
border-bottom: 1px dotted #00aaaa;
}
a:hover {
color: #ffff00;
border-bottom: 1px solid #ffff00;
text-shadow: 0 0 10px rgba(255, 255, 0, 0.5);
}
code, pre {
font-family: 'Courier New', monospace;
background: rgba(0, 30, 0, 0.5);
border: 1px solid #005500;
padding: 15px;
margin: 15px 0;
display: block;
overflow-x: auto;
white-space: pre;
color: #88ff88;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
position: relative;
}
pre::before {
content: "┌──[ код ]";
position: absolute;
top: -10px;
left: 10px;
background: #000;
padding: 0 5px;
font-size: 11px;
color: #00aa00;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
border: 1px solid #005500;
}
th {
background: rgba(0, 40, 0, 0.5);
color: #00ff88;
padding: 10px;
border: 1px solid #005500;
text-align: left;
font-weight: bold;
text-transform: uppercase;
font-size: 12px;
}
td {
padding: 10px;
border: 1px solid #005500;
color: #88ff88;
vertical-align: top;
}
tr:nth-child(even) {
background: rgba(0, 25, 0, 0.3);
}
tr:hover {
background: rgba(0, 50, 0, 0.4);
}
.note {
border-left: 3px solid #ffff00;
padding: 15px;
margin: 20px 0;
background: rgba(40, 40, 0, 0.2);
color: #ffff88;
}
.warning {
border-left: 3px solid #ff4444;
padding: 15px;
margin: 20px 0;
background: rgba(40, 0, 0, 0.2);
color: #ff8888;
}
.process-diagram {
text-align: center;
margin: 30px 0;
font-family: monospace;
white-space: pre;
color: #00ffaa;
background: rgba(0, 20, 0, 0.3);
padding: 20px;
border: 1px dashed #00aa00;
overflow-x: auto;
}
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #005500;
text-align: center;
color: #00aa00;
font-size: 12px;
}
.typewriter {
overflow: hidden;
border-right: 3px solid #00ff00;
white-space: nowrap;
margin: 0 auto;
animation: typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes blink-caret {
from, to { border-color: transparent; }
50% { border-color: #00ff00; }
}
.ascii-art {
text-align: center;
margin: 30px 0;
font-family: monospace;
color: #00ff88;
line-height: 1.2;
white-space: pre;
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.ascii-art-box {
display: inline-block;
border: none;
background: none;
padding: 0;
margin: 0;
}
@media (max-width: 768px) {
body {
padding: 10px;
font-size: 12px;
}
.terminal {
padding: 15px;
}
h1 {
font-size: 18px;
}
h2 {
font-size: 16px;
}
table {
font-size: 11px;
}
pre, code {
font-size: 11px;
padding: 10px;
}
.ascii-art {
font-size: 10px;
overflow-x: auto;
}
}
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
background: rgba(0, 20, 0, 0.3);
border: 1px solid #005500;
}
::-webkit-scrollbar-thumb {
background: #005500;
border: 1px solid #00aa00;
}
::-webkit-scrollbar-thumb:hover {
background: #00aa00;
}
</style>
</head>
<body>
<div class="terminal">
<h1 class="typewriter">КВС — Ассемблер с русским синтаксисом для x86-64</h1>
<div style="text-align: center; margin: 30px 0; color: #00ff88;">
<div class="ascii-art">
<pre class="ascii-art-box">
╔══════════════════════════════════╗
║ ██╗ ██╗ ██╗ ██╗ ███████╗ ║
║ ██║ ██╔╝ ██╗ ██╗ ██╔════╝ ║
║ █████╔╝ ██╗ ██╗ ███████╗ ║
║ ██║╚██╗ ╚██╗ ██╔╝ ╚════██║ ║
║ ██║ ╚██╗ ╚██╔╝ ███████║ ║
║ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ║
╚══════════════════════════════════╝
</pre>
</div>
<p>Версия 2.0 | Экспериментальный проект | 2025</p>
</div>
<h2>┌──[ ОГЛАВЛЕНИЕ ]────────────────────────────────────────────┐</h2>
<ul style="columns: 2; column-gap: 40px;">
<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>
<h2 id="назначение">НАЗНАЧЕНИЕ И КОНЦЕПЦИЯ</h2>
<p><strong>КВС</strong> — это ассемблер для архитектуры x86-64, полностью использующий <strong>русскую лексику</strong> для мнемоник инструкций, имён регистров и директив. Проект носит <strong>образовательный и экспериментальный характер</strong> и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.</p>
<div class="note">
<strong>Ключевые принципы проекта:</strong>
<ul>
<li><strong>Русскоязычный синтаксис</strong> — все инструкции, регистры и директивы на кириллице</li>
<li><strong>Модульная архитектура</strong> — лексер, парсер, два прохода, сборщик ELF</li>
<li><strong>Минимализм зависимостей</strong> — только стандартная библиотека Python</li>
<li><strong>Подготовка к самокомпиляции</strong> — архитектура, допускающая написание компилятора на самом КВС</li>
</ul>
</div>
<h2 id="особенности">ОСОБЕННОСТИ И ВОЗМОЖНОСТИ</h2>
<ul>
<li><strong>Полностью русский синтаксис</strong><code>переместить</code>, <code>прибавить</code>, <code>вызвать</code>, регистры <code>раикс</code>, <code>есипи</code>, <code>ал</code>, директивы <code>.текст</code>, <code>.данные</code></li>
<li><strong>Генерация исполняемых ELF-файлов</strong> — 64-битные ELF для Linux, готовые к запуску</li>
<li><strong>Двухпроходная сборка</strong> — традиционная схема с разрешением меток</li>
<li><strong>Поддержка секций</strong> — разделение на <code>.текст</code> (код) и <code>.данные</code> (данные)</li>
<li><strong>CSV-логирование</strong> — детальный файл <code>.csv</code> с соответствием адресов, байтов и исходных команд</li>
<li><strong>Более 50 инструкций</strong> — полный набор основных команд x86-64</li>
</ul>
<h2 id="архитектура">АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ</h2>
<div class="process-diagram">
Исходный файл (.квс)
▼ (kvs_lexer.py)
Файл токенов (.токены)
▼ (kvs_parser.py)
AST-представление (.аст)
▼ (kvs_pass1.py)
Первый проход: размеры, метки (.проход1)
▼ (kvs_pass2.py)
Второй проход: генерация кода (.csv)
▼ (kvs_builder.py)
ELF-компоновщик (.elf)
Исполняемый файл!
</div>
<h2 id="установка">УСТАНОВКА И ЗАПУСК</h2>
<pre># Клонирование репозитория
git clone https://github.com/username/kvs.git
cd kvs
# Установка прав на выполнение (при необходимости)
chmod +x kvs_build.py
# Компиляция программы
python3 kvs_build.py программа.квс
# Запуск скомпилированной программы
./программа.elf</pre>
<h2 id="синтаксис">СИНТАКСИС ИСХОДНЫХ ФАЙЛОВ (.квс)</h2>
<pre>; Комментарий начинается с точки с запятой
.данные ; Начало секции данных
message: .строкауль "Привет, мир!"
counter: .байт 0x0A
.текст ; Начало секции кода
.глобал _start ; Точка входа
_start:
; Аргументы для write(1, message, 13)
переместить_имм раикс, 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>, <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>, <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>
</table>
<h2 id="регистры">РЕГИСТРЫ ПРОЦЕССОРА</h2>
<table>
<tr><th>64-битные</th><th>32-битные</th><th>16-битные</th><th>8-битные (мл.)</th><th>8-битные (ст.)</th></tr>
<tr><td><code>раикс</code></td><td><code>еаикс</code></td><td><code>аикс</code></td><td><code>ал</code></td><td><code>аш</code></td></tr>
<tr><td><code>рбикс</code></td><td><code>ебикс</code></td><td><code>бикс</code></td><td><code>бл</code></td><td><code>бш</code></td></tr>
<tr><td><code>рсикс</code></td><td><code>есикс</code></td><td><code>сикс</code></td><td><code>кл</code></td><td><code>чш</code></td></tr>
<tr><td><code>рдикс</code></td><td><code>едикс</code></td><td><code>дикс</code></td><td><code>дл</code></td><td><code>дш</code></td></tr>
<tr><td><code>рсипи</code></td><td><code>есипи</code></td><td><code>эсп</code></td><td><code>спл</code></td><td></td></tr>
<tr><td><code>рбипи</code></td><td><code>ебипи</code></td><td><code>бипи</code></td><td><code>бпл</code></td><td></td></tr>
<tr><td><code>рсиай</code></td><td><code>есиай</code></td><td><code>эс</code></td><td><code>сил</code></td><td></td></tr>
<tr><td><code>рдиай</code></td><td><code>едиай</code></td><td><code>ди</code></td><td><code>дил</code></td><td></td></tr>
<tr><td><code>р8</code><code>р15</code></td><td><code>р</code><code>р15д</code></td><td><code>р</code><code>р15в</code></td><td><code>р8б</code><code>р15б</code></td><td></td></tr>
</table>
<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><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>.константа SIZE = 100</code></td></tr>
</table>
<h2 id="примеры">ПРИМЕРЫ ПРОГРАММ</h2>
<h3>Пример 1: "Привет, мир!"</h3>
<pre>.глобал _start
.текст
_start:
; write(1, msg, 13)
переместить_имм раикс, 1
загрузить_адрес рсиай, msg
переместить_имм рдикс, 13
вызов_системы
; exit(0)
переместить_имм раикс, 60
переместить_имм рдикс, 0
вызов_системы
.данные
msg: .строкауль "Привет, мир!"</pre>
<h3>Пример 2: Цикл для вычисления суммы</h3>
<pre>.глобал _start
.текст
_start:
переместить_имм р8, 0 ; сумма
переместить_имм р9, 10 ; счётчик
loop:
прибавить р8, р9
уменьшить р9
сравнить_с р9, 0
переход_если_больше loop
; exit(сумма в r8)
переместить_имм раикс, 60
переместить рдикс, р8
вызов_системы</pre>
<h2 id="трансляция">ПРОЦЕСС ТРАНСЛЯЦИИ И ВЫХОДНЫЕ ФАЙЛЫ</h2>
<p>Запуск ассемблера:</p>
<pre>python3 kvs_build.py программа.квс</pre>
<p><strong>В результате создаются файлы:</strong></p>
<ul>
<li><strong><code>программа.токены</code></strong> — результат лексического анализа</li>
<li><strong><code>программа.аст</code></strong> — AST-представление</li>
<li><strong><code>программа.проход1</code></strong> — результаты первого прохода (размеры, метки)</li>
<li><strong><code>программа.csv</code></strong> — детальный лог с адресами, байтами и командами</li>
<li><strong><code>программа.elf</code></strong> — исполняемый ELF-файл</li>
</ul>
<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></li>
<li><strong>Нет 32-битного режима</strong> — только 64-битные ELF</li>
<li><strong>Экспериментальный статус</strong> — некоторые инструкции могут быть реализованы не полностью</li>
</ul>
</div>
<footer>
<p>───────────────────────────────────────────────────────────────────────</p>
<p>КВС Ассемблер v2.0 | Экспериментальный проект | 2025</p>
<p>Только для образовательных целей. Используйте на свой страх и риск.</p>
<p style="margin-top: 10px; font-size: 10px; color: #005500;">
> Система готова. Введите команду...
<span class="cursor"></span>
</p>
</footer>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('h1, h2, h3, p, li, td, th, pre, code');
elements.forEach((el, index) => {
el.style.opacity = '0';
el.style.transition = 'opacity 0.5s ease';
setTimeout(() => { el.style.opacity = '1'; }, 100 + index * 10);
});
});
</script>
</body>
</html>

418
README.md Normal file
View file

@ -0,0 +1,418 @@
# Документация компилятора КВС
## 1. Общие сведения
**КВС** (язык программирования) — это низкоуровневый язык, транслируемый в 64-битный ELF-исполняемый файл для архитектуры x86-64. Компилятор написан на Python и состоит из нескольких модулей, выполняющих лексический анализ, синтаксический разбор, двухпроходную генерацию кода и сборку ELF-файла.
### 1.1. Состав компилятора
| Файл | Назначение |
|------|------------|
| `kvs_8.py` | Монолитный ассемблер (оригинальная версия) |
| `kvs_build.py` | Главный сборочный скрипт, запускающий все этапы |
| `kvs_lexer.py` | Лексический анализ, разбор на токены |
| `kvs_parser.py` | Синтаксический анализ, построение AST |
| `kvs_pass1.py` | Первый проход: вычисление размеров и адресов меток |
| `kvs_pass2.py` | Второй проход: генерация машинного кода |
| `kvs_builder.py` | Сборка ELF-файла из CSV-данных |
| `kvs_data.py` | Общие данные (регистры, инструкции, константы) |
### 1.2. Запуск компилятора
```bash
# Монолитная версия
python3 kvs_8.py <файл.квс>
# Декомпозированная версия
python3 kvs_build.py <файл.квс>
```
Входной файл должен иметь расширение **`.квс`**.
### 1.3. Результаты компиляции
| Файл | Содержимое |
|------|------------|
| `<имя>.elf` | Исполняемый ELF-файл (64-bit) |
| `<имя>.log.csv` | Лог с адресами, байтами и соответствием командам |
| `<имя>.токены` | Промежуточный файл токенов (только для декомп. версии) |
| `<имя>.аст` | Промежуточный AST-файл (только для декомп. версии) |
| `<имя>.проход1` | Данные первого прохода (только для декомп. версии) |
| `<имя>.csv` | CSV с машинным кодом (только для декомп. версии) |
---
## 2. Синтаксис языка КВС
### 2.1. Структура программы
Программа на КВС состоит из секций:
```
.текст ; секция кода (обязательна)
; ... инструкции ...
.данные ; секция данных (опциональна)
; ... данные ...
```
### 2.2. Метки
Метка — это имя, за которым следует двоеточие. Метки могут использоваться как цели переходов.
```
_start: ; метка _start
переместить_имм раикс, 1
переход _start ; переход на метку
```
### 2.3. Директивы
| Директива | Назначение | Пример |
|-----------|------------|--------|
| `.текст` | Начало секции кода | `.текст` |
| `.данные` | Начало секции данных | `.данные` |
| `.глобал` | Объявление глобальной метки (точки входа) | `.глобал _start` |
| `.строка` | Строка без завершающего нуля | `.строка "Hello"` |
| `.строкауль` | Строка с завершающим нулём | `.строкауль "Hello"` |
| `.байт` | Последовательность байтов | `.байт 0x48, 0x65, 108` |
| `.константа` | Определение константы | `.константа LEN = 10` |
### 2.4. Комментарии
Однострочные комментарии начинаются с `;`:
```
; Это комментарий
переместить_имм раикс, 42 ; комментарий после инструкции
```
---
## 3. Система команд
### 3.1. Регистры
КВС поддерживает все основные 64-битные регистры x86-64 в различных размерностях:
| Размер | 64-бит | 32-бит | 16-бит | 8-бит (мл.) | 8-бит (ст.) |
|--------|--------|--------|--------|-------------|-------------|
| RAX | `раикс` | `еаикс` | `аикс` | `ал` | `аш` |
| RCX | `рсикс` | `есикс` | `сикс` | `кл` | `чш` |
| RDX | `рдикс` | `едикс` | `дикс` | `дл` | `дш` |
| RBX | `рбикс` | `ебикс` | `бикс` | `бл` | `бш` |
| RSP | `рсипи` | `есипи` | `эсп` | `спл` | — |
| RBP | `рбипи` | `ебипи` | `бипи` | `бпл` | — |
| RSI | `рсиай` | `есиай` | `эс` | `сил` | — |
| RDI | `рдиай` | `едиай` | `ди` | `дил` | — |
| R8-R15 | `р8`…`р15` | `р8д`…`р15д` | `р8в`…`р15в` | `р8б`…`р15б` | — |
### 3.2. Инструкции
#### Загрузка констант
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| `переместить_имм` | `переместить_имм рег, значение` | Загрузить константу в регистр |
```asm
переместить_имм раикс, 42 ; RAX = 42
переместить_имм еаикс, 0x7FFF ; EAX = 32767 (32-бит)
переместить_имм ал, 0xFF ; AL = 255 (8-бит)
```
#### Арифметика и логика
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| `прибавить` | `прибавить рег1, рег2` | рег1 += рег2 |
| `вычесть` | `вычесть рег1, рег2` | рег1 -= рег2 |
| `увеличить` | `увеличить рег` | рег++ |
| `уменьшить` | `уменьшить рег` | рег-- |
| `сравнить` | `сравнить рег1, рег2` | Сравнить два регистра (установка флагов) |
| `сравнить_с` | `сравнить_с рег, значение` | Сравнить регистр с константой |
| `проверить` | `проверить рег1, рег2` | TEST — логическое AND (установка ZF) |
```asm
прибавить раикс, рбикс ; RAX += RBX
увеличить рсикс ; RCX++
сравнить раикс, 10 ; сравнить RAX с 10
```
#### Переходы
| Инструкция | Синтаксис | Условие |
|------------|-----------|---------|
| `переход` | `переход метка` | Безусловный |
| ороткий_переход` | ороткий_переход метка` | Безусловный (смещение ±127) |
| `переход_если_равно` | `переход_если_равно метка` | ZF = 1 |
| `переход_если_неравно` | `переход_если_неравно метка` | ZF = 0 |
| `переход_если_меньше` | `переход_если_меньше метка` | SF ≠ OF (signed <) |
| `переход_если_больше` | `переход_если_больше метка` | ZF = 0 и SF = OF (signed >) |
| `переход_если_меньше_или_равно` | `переход_если_меньше_или_равно метка` | ZF = 1 или SF ≠ OF |
| `переход_если_больше_или_равно` | `переход_если_больше_или_равно метка` | SF = OF |
| `переход_если_перенос` | `переход_если_перенос метка` | CF = 1 |
| `переход_если_нет_переноса` | `переход_если_нет_переноса метка` | CF = 0 |
| `переход_если_ноль` | `переход_если_ноль метка` | ZF = 1 (синоним JE) |
| `переход_если_неоль` | `переход_если_неоль метка` | ZF = 0 (синоним JNE) |
**Короткие условные переходы** (префикс ороткий_`):
- ороткий_переход_если_равно`, ороткий_переход_если_неравно`, и т.д.
- Генерируют 2-байтовые инструкции (смещение ±127 байт)
#### Системные вызовы
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| `вызов_системы` | `вызов_системы` | Вызов ядра Linux (syscall) |
```asm
; Пример: вывод строки
переместить_имм раикс, 1 ; syscall 1 = write
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, msg ; указатель на строку
переместить_имм рдикс, len ; длина строки
вызов_системы
; Пример: завершение программы
переместить_имм раикс, 60 ; syscall 60 = exit
переместить_имм рдиай, 0 ; код возврата 0
вызов_системы
```
#### Прочие инструкции
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| ет_операции` | ет_операции` | NOP (0x90) |
---
## 4. Примеры программ
### 4.1. Hello World
```asm
.текст
.глобал _start
_start:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg
переместить_имм рдикс, len_msg
вызов_системы
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
msg: .строкауль "Hello, World!\n"
.константа len_msg = 15
```
### 4.2. Цикл и условный переход
```asm
.текст
.глобал _start
_start:
переместить_имм рсикс, 10 ; счетчик = 10
loop:
; тело цикла
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg
переместить_имм рдикс, len_msg
вызов_системы
уменьшить рсикс ; счетчик--
сравнить_с рсикс, 0
переход_если_больше loop ; если > 0, продолжаем
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
msg: .строкауль "Iteration\n"
.константа len_msg = 10
```
---
## 5. Проверка исполняемого файла стандартными утилитами
### 5.1. Запуск программы
```bash
./<имя>.elf
```
### 5.2. Просмотр секций (`objdump -h`)
```bash
objdump -h программа.elf
```
Вывод показывает размер, виртуальный адрес и атрибуты каждой секции.
```
Разделы:
Idx Name Разм VMA LMA Фа смещ.
0 .text 00000232 0000000000401000 0000000000401000 00001000
1 .data 0000011c 0000000000402000 0000000000402000 00002000
```
### 5.3. Дизассемблирование (`objdump -d`)
```bash
objdump -d программа.elf
```
Показывает машинный код в виде ассемблерных инструкций x86-64.
```bash
# Ограничить вывод
objdump -d программа.elf | head -50
# Сохранить в файл
objdump -d программа.elf > код.asm
```
### 5.4. Просмотр заголовков программы (`readelf -l`)
```bash
readelf -l программа.elf
```
Показывает сегменты LOAD, их смещения, виртуальные адреса и права доступа.
```
Заголовки программы:
Тип Смещ. Вирт.адр Физ.адр Рзм.фйл Рзм.пм Флаги
LOAD 0x001000 0x401000 0x401000 0x232 0x1000 R E
LOAD 0x002000 0x402000 0x402000 0x11c 0x1000 RW
```
### 5.5. Проверка целостности ELF (`readelf -h`)
```bash
readelf -h программа.elf
```
Выводит заголовок ELF-файла (магия, тип, архитектура, точка входа).
### 5.6. Просмотр всех заголовков секций (`readelf -S`)
```bash
readelf -S программа.elf
```
### 5.7. Сравнение двух ELF-файлов
```bash
# Сравнить дизассемблированный код
diff <(objdump -d файл1.elf) <(objdump -d файл2.elf)
# Сравнить заголовки секций
diff <(objdump -h файл1.elf) <(objdump -h файл2.elf)
```
### 5.8. Просмотр строк из секции `.data`
```bash
# Извлечь строки из ELF
strings программа.elf
# Показать шестнадцатеричный дамп секции .data
objdump -s -j .data программа.elf
```
### 5.9. Трассировка системных вызовов
```bash
strace ./программа.elf
```
Показывает все системные вызовы, выполняемые программой.
### 5.10. Отладка с GDB
```bash
gdb ./программа.elf
```
В GDB:
```
(gdb) break _start ; установить точку останова
(gdb) run ; запустить
(gdb) info registers ; показать регистры
(gdb) x/10i $rip ; показать 10 инструкций
(gdb) stepi ; выполнить одну инструкцию
```
---
## 6. Особенности и ограничения
### 6.1. Поддерживаемые возможности
- ✅ 64-битные, 32-битные, 16-битные и 8-битные регистры
- ✅ Непосредственная загрузка констант (imm) в регистры
- ✅ Сравнение регистров и регистров с константами
- ✅ Условные и безусловные переходы (длинные и короткие)
- ✅ Арифметические операции (ADD, SUB, INC, DEC)
- ✅ Логическая операция TEST
- ✅ Системные вызовы Linux (syscall)
- ✅ Секции `.text` и `.data`
- ✅ Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`)
- ✅ Директива `.байт` для raw-данных
- ✅ Директива `.константа` для имён констант
### 6.2. Ограничения
- ❌ Отсутствует поддержка косвенной адресации памяти `[reg+disp]`
- ❌ Нет инструкций для работы со стеком (PUSH, POP, CALL, RET)
- ❌ Нет поддержки плавающей запятой (FPU/SSE)
- ❌ Нет многомодульной компиляции
- ❌ Нет макросов
### 6.3. Примечания
- Все переходы используют абсолютные целевые адреса (метки преобразуются в адреса)
- Для длинных переходов генерируется 5-байтовая инструкция JMP или 6-байтовая Jcc
- Секция `.data` выравнивается на границу страницы (0x1000) после `.text`
- Точка входа по умолчанию — `_start`
---
## 7. Сообщения об ошибках
Компилятор выводит подробные сообщения об ошибках с указанием файла и строки:
```
Ошибка в файле test.квс, строка 42:
переместить_имм аикс, 100000
Неподдерживаемый размер регистра
```
Основные ошибки:
| Сообщение | Причина |
|-----------|---------|
| `Неизвестная инструкция: '...'` | Неверное имя инструкции |
| `Недопустимый формат числа: '...'` | Неверный синтаксис числа |
| `Недопустимый регистр` | Регистр не найден в таблице |
| `Метка не найдена: '...'` | Использование неопределённой метки |
| `Цель слишком далеко для короткого перехода` | Смещение > 127 байт для условного перехода |
| `Незакрытая кавычка в строке` | Ошибка в синтаксисе строки |
---
## 8. Заключение
Компилятор КВС предоставляет возможность писать низкоуровневые программы на русскоязычном ассемблероподобном языке с последующей компиляцией в нативный 64-битный ELF-исполняемый файл. Декомпозированная архитектура компилятора облегчает понимание этапов трансляции и модификацию отдельных модулей.

1999
asm21_.py

File diff suppressed because it is too large Load diff

589
elfreader.py Normal file
View file

@ -0,0 +1,589 @@
#!/usr/bin/env python3
"""
===============================================================================
ELF Reader аналог readelf для ELF64 (AMD64/x86-64)
===============================================================================
НАЗНАЧЕНИЕ
----------
Модуль читает и отображает заголовки исполняемых ELF-файлов в стиле утилиты
readelf из пакета GNU Binutils. Вывод на русском языке, формат максимально
приближен к оригинальному readelf -h -l -S.
Поддерживается только архитектура AMD64 (x86-64), little-endian, 64-битный
формат ELF.
ИСПОЛЬЗОВАНИЕ
-------------
python3 elfreader.py <путь_к_elf_файлу>
Пример:
python3 elfreader.py /bin/ls
python3 elfreader.py моя_программа.elf
ФУНКЦИОНАЛЬНОСТЬ
-----------------
Выводит три блока информации:
1. Заголовок ELF (readelf -h):
- Magic-число и класс (ELF64)
- Порядок байт (little-endian)
- Тип файла (EXEC, DYN, REL, CORE)
- Целевая машина (X86-64)
- Адрес точки входа
- Расположение и количество программных/секционных заголовков
- Индекс строковой таблицы секций
2. Заголовки разделов (readelf -S):
- Таблица всех секций с адресами, смещениями, размерами
- Флаги секций (W=запись, A=выделение памяти, X=исполнение, I=инфо)
- Поля ссылок, информации и выравнивания
- Легенда обозначений флагов
3. Заголовки программы/сегментов (readelf -l):
- Таблица сегментов с виртуальными/физическими адресами
- Файловый и памяти'ный размеры
- Флаги доступа (R=чтение, W=запись, E=исполнение)
- Соответствие сегментов секциям
ВЫВОДИМЫЕ ДАННЫЕ
-----------------
Все числовые значения выводятся в шестнадцатеричном виде (адреса, смещения,
размеры) или десятичном (счётчики, индексы). Флаги секций и сегментов
отображаются в сокращённой буквенной нотации.
ОГРАНИЧЕНИЯ
-----------
- Только ELF64 little-endian (архитектура x86-64)
- Не поддерживаются 32-битные ELF-файлы
- Не анализируются таблицы символов (.symtab, .dynsym)
- Не обрабатываются релокации (.rel, .rela)
- Не читаются отладочные секции (.debug_*)
- Не разбираются динамические секции (.dynamic)
- Не интерпретируются NOTE-секции
- Не выводится информация о версиях символов
- Не читается интерпретатор из секции .interp
ТРЕБОВАНИЯ
----------
- Python 3.6 или выше
- Только стандартная библиотека (struct, sys)
- Внешние зависимости отсутствуют
АРХИТЕКТУРА МОДУЛЯ
-------------------
Весь код организован в виде набора чистых функций без классов:
Чтение структур:
read_elf_header() - парсинг основного ELF-заголовка (64 байта)
read_section_headers() - парсинг заголовков секций (по 64 байта каждый)
read_program_headers() - парсинг программных заголовков (по 56 байт каждый)
get_section_name() - извлечение имени секции из строковой таблицы
Форматирование вывода:
format_elf_header() - форматирование ELF-заголовка в строку
format_sections() - форматирование таблицы секций
format_program_headers() - форматирование таблицы сегментов
format_segment_section_map() - форматирование связей сегменты->секции
Вспомогательные:
get_flags_description() - преобразование битовых флагов секции в строку
ФОРМАТ ELF64 (краткая справка)
-------------------------------
ELF-заголовок (Elf64_Ehdr): 64 байта
- e_ident[16]: magic (4 байта), класс (1), порядок байт (1), версия (1),
OS/ABI (1), ABI version (1), padding (7)
- e_type (2 байта): тип файла
- e_machine (2 байта): архитектура (0x3E = x86-64)
- e_version (4 байта): версия формата
- e_entry (8 байт): адрес точки входа
- e_phoff (8 байт): смещение таблицы программных заголовков
- e_shoff (8 байт): смещение таблицы заголовков секций
- e_flags (4 байта): флаги процессора
- e_ehsize (2 байта): размер этого заголовка
- e_phentsize (2 байта): размер одного программного заголовка
- e_phnum (2 байта): количество программных заголовков
- e_shentsize (2 байта): размер одного заголовка секции
- e_shnum (2 байта): количество заголовков секций
- e_shstrndx (2 байта): индекс секции со строковой таблицей имён секций
Заголовок секции (Elf64_Shdr): 64 байта
- sh_name (4 байта): смещение имени в .shstrtab
- sh_type (4 байта): тип секции
- sh_flags (8 байт): флаги (W=1, A=2, X=4, I=0x8)
- sh_addr (8 байт): виртуальный адрес в памяти
- sh_offset (8 байт): смещение данных в файле
- sh_size (8 байт): размер секции в байтах
- sh_link (4 байта): ссылка на другую секцию
- sh_info (4 байта): дополнительная информация
- sh_addralign (8 байт): выравнивание адреса
- sh_entsize (8 байт): размер элемента (для таблиц)
Программный заголовок (Elf64_Phdr): 56 байт
- p_type (4 байта): тип сегмента (LOAD, DYNAMIC, INTERP, ...)
- p_flags (4 байта): флаги доступа (R=4, W=2, E=1)
- p_offset (8 байт): смещение данных в файле
- p_vaddr (8 байт): виртуальный адрес загрузки
- p_paddr (8 байт): физический адрес
- p_filesz (8 байт): размер в файле
- p_memsz (8 байт): размер в памяти
- p_align (8 байт): выравнивание
ПРИМЕР ВЫВОДА
-------------
$ python3 elfreader.py программа.elf
Заголовок ELF:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Класс: ELF64
Данные: дополнение до 2, от младшего к старшему
Version: 1 (current)
OS/ABI: UNIX - System V
Версия ABI: 0
Тип: EXEC (Исполняемый файл)
Машина: Advanced Micro Devices X86-64
...
ИНТЕГРАЦИЯ
----------
Модуль можно использовать как самостоятельную утилиту:
python3 elfreader.py файл.elf
Или импортировать функции для программного анализа ELF-файлов:
from elfreader import read_elf_header, read_section_headers
with open('программа.elf', 'rb') as f:
hdr = read_elf_header(f)
print(f"Точка входа: 0x{hdr['e_entry']:x}")
СРАВНЕНИЕ С readelf
-------------------
Вывод elfreader.py максимально приближен к выводу readelf из GNU Binutils.
Основные отличия:
- Все заголовки на русском языке (Заголовок ELF, Заголовки разделов, ...)
- Незначительные различия в форматировании пробелов
- Отсутствует часть расширенной информации (символы, релокации, dynamic)
- Не отображается интерпретатор из секции .interp
Для полного анализа ELF-файлов рекомендуется использовать оригинальный
readelf или библиотеку pyelftools для программного доступа.
АВТОР И ЛИЦЕНЗИЯ
-----------------
Создано в образовательных целях для изучения формата ELF.
Свободное использование, модификация и распространение.
"""
import struct
import sys
# ---------- константы ELF64 ----------
ELFMAG = b'\x7fELF'
ELFCLASS64 = 2
ELFDATA2LSB = 1
EM_X86_64 = 0x3E
# Типы ELF (e_type)
ET_NONE = 0
ET_REL = 1
ET_EXEC = 2
ET_DYN = 3
ET_CORE = 4
ETYPE_NAMES = {
ET_NONE: 'NONE (Нет типа файла)',
ET_REL: 'REL (Перемещаемый файл)',
ET_EXEC: 'EXEC (Исполняемый файл)',
ET_DYN: 'DYN (Совместно используемый объектный файл)',
ET_CORE: 'CORE (Файл ядра)'
}
# Типы секций (sh_type)
SHT_NULL = 0
SHT_PROGBITS = 1
SHT_SYMTAB = 2
SHT_STRTAB = 3
SHT_RELA = 4
SHT_HASH = 5
SHT_DYNAMIC = 6
SHT_NOTE = 7
SHT_NOBITS = 8
SHT_REL = 9
SHT_SHLIB = 10
SHT_DYNSYM = 11
SHT_INIT_ARRAY = 14
SHT_FINI_ARRAY = 15
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
SHT_NUM = 19
SHT_GNU_HASH = 0x6ffffff6
SHT_VERSYM = 0x6fffffff
SHT_VERNEED = 0x6ffffffe
SHT_VERDEF = 0x6ffffffd
SHT_NAMES = {
SHT_NULL: 'NULL',
SHT_PROGBITS: 'PROGBITS',
SHT_SYMTAB: 'SYMTAB',
SHT_STRTAB: 'STRTAB',
SHT_RELA: 'RELA',
SHT_HASH: 'HASH',
SHT_DYNAMIC: 'DYNAMIC',
SHT_NOTE: 'NOTE',
SHT_NOBITS: 'NOBITS',
SHT_REL: 'REL',
SHT_SHLIB: 'SHLIB',
SHT_DYNSYM: 'DYNSYM',
SHT_INIT_ARRAY: 'INIT_ARRAY',
SHT_FINI_ARRAY: 'FINI_ARRAY',
SHT_PREINIT_ARRAY: 'PREINIT_ARRAY',
SHT_GROUP: 'GROUP',
SHT_SYMTAB_SHNDX: 'SYMTAB_SHNDX',
SHT_NUM: 'NUM',
SHT_GNU_HASH: 'GNU_HASH',
SHT_VERSYM: 'VERSYM',
SHT_VERNEED: 'VERNEED',
SHT_VERDEF: 'VERDEF'
}
# Типы программных заголовков (p_type)
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_TLS = 7
PT_GNU_EH_FRAME = 0x6474e550
PT_GNU_STACK = 0x6474e551
PT_GNU_RELRO = 0x6474e552
PT_GNU_PROPERTY = 0x6474e553
PT_NAMES = {
PT_NULL: 'NULL',
PT_LOAD: 'LOAD',
PT_DYNAMIC: 'DYNAMIC',
PT_INTERP: 'INTERP',
PT_NOTE: 'NOTE',
PT_SHLIB: 'SHLIB',
PT_PHDR: 'PHDR',
PT_TLS: 'TLS',
PT_GNU_EH_FRAME: 'GNU_EH_FRAME',
PT_GNU_STACK: 'GNU_STACK',
PT_GNU_RELRO: 'GNU_RELRO',
PT_GNU_PROPERTY: 'GNU_PROPERTY'
}
# OS/ABI идентификаторы
OSABI_NAMES = {
0: 'UNIX - System V',
1: 'HP-UX',
2: 'NetBSD',
3: 'GNU/Linux',
6: 'Solaris',
9: 'FreeBSD',
12: 'OpenBSD'
}
# ---------- функции чтения структур ----------
def read_elf_header(f):
"""Читает и возвращает словарь с полями ELF-заголовка (64-бит)."""
data = f.read(64)
if len(data) < 64:
raise ValueError("Слишком маленький файл для ELF64 заголовка")
fmt = '16s H H I Q Q Q I H H H H H H'
(e_ident, e_type, e_machine, e_version,
e_entry, e_phoff, e_shoff, e_flags,
e_ehsize, e_phentsize, e_phnum,
e_shentsize, e_shnum, e_shstrndx) = struct.unpack(fmt, data)
if e_ident[:4] != ELFMAG:
raise ValueError("Не ELF файл")
if e_ident[4] != ELFCLASS64:
raise ValueError("Не 64-битный ELF")
if e_ident[5] != ELFDATA2LSB:
raise ValueError("Не little-endian (ожидался x86-64)")
return {
'e_ident': e_ident,
'e_type': e_type,
'e_machine': e_machine,
'e_version': e_version,
'e_entry': e_entry,
'e_phoff': e_phoff,
'e_shoff': e_shoff,
'e_flags': e_flags,
'e_ehsize': e_ehsize,
'e_phentsize': e_phentsize,
'e_phnum': e_phnum,
'e_shentsize': e_shentsize,
'e_shnum': e_shnum,
'e_shstrndx': e_shstrndx
}
def read_section_headers(f, shoff, shnum):
"""Возвращает список словарей с заголовками секций."""
f.seek(shoff)
sections = []
fmt = 'I I Q Q Q Q I I Q Q'
for _ in range(shnum):
data = f.read(64)
vals = struct.unpack(fmt, data)
sections.append({
'sh_name': vals[0],
'sh_type': vals[1],
'sh_flags': vals[2],
'sh_addr': vals[3],
'sh_offset': vals[4],
'sh_size': vals[5],
'sh_link': vals[6],
'sh_info': vals[7],
'sh_addralign': vals[8],
'sh_entsize': vals[9]
})
return sections
def read_program_headers(f, phoff, phnum):
"""Возвращает список словарей с программными заголовками."""
f.seek(phoff)
headers = []
fmt = 'I I Q Q Q Q Q Q'
for _ in range(phnum):
data = f.read(56)
vals = struct.unpack(fmt, data)
headers.append({
'p_type': vals[0],
'p_flags': vals[1],
'p_offset': vals[2],
'p_vaddr': vals[3],
'p_paddr': vals[4],
'p_filesz': vals[5],
'p_memsz': vals[6],
'p_align': vals[7]
})
return headers
def get_section_name(f, shstrtab_offset, shstrtab_size, sh_name_offset):
"""Извлекает имя секции из таблицы строк."""
f.seek(shstrtab_offset + sh_name_offset)
name = b''
while True:
ch = f.read(1)
if ch == b'\0' or ch == b'':
break
name += ch
return name.decode('latin-1', errors='replace')
def get_flags_description(sh_flags, sh_type=0, sh_info=0):
"""
Расшифровка флагов секции: W (запись), A (назнач), X (исполняемый), I (инфо).
Для секций REL/RELA флаг 'I' устанавливается, если sh_info указывает
на целевую секцию (sh_info > 0), как это делает readelf.
"""
flags = []
if sh_flags & 0x1: flags.append('W') # WRITE
if sh_flags & 0x2: flags.append('A') # ALLOC
if sh_flags & 0x4: flags.append('X') # EXECINSTR
if sh_flags & 0x8: flags.append('I') # INFO (GNU extension)
# Дополнительно: REL/RELA секции с sh_info > 0 получают флаг I
if (sh_type == SHT_REL or sh_type == SHT_RELA) and sh_info > 0:
if 'I' not in flags:
flags.append('I')
return ''.join(flags)
def is_section_in_segment(sec, ph):
"""
Проверяет, принадлежит ли секция сегменту.
Исключает секции с нулевым адресом (не загружаемые в память).
"""
if sec['sh_size'] == 0:
return False
if sec['sh_addr'] == 0:
return False
if ph['p_memsz'] == 0:
return False
# Проверяем, что секция находится внутри сегмента
return (sec['sh_addr'] >= ph['p_vaddr'] and
sec['sh_addr'] + sec['sh_size'] <= ph['p_vaddr'] + ph['p_memsz'])
# ---------- функции форматирования (на русском) ----------
def format_elf_header(hdr):
"""Возвращает строку с ELF-заголовком в стиле readelf -h."""
lines = []
lines.append("Заголовок ELF:")
lines.append(f" Magic: {hdr['e_ident'].hex(' ')}")
lines.append(f" Класс: ELF64")
if hdr['e_ident'][5] == 1:
lines.append(f" Данные: дополнение до 2, от младшего к старшему")
else:
lines.append(f" Данные: дополнение до 2, от старшего к младшему")
lines.append(f" Version: {hdr['e_ident'][6]} (current)")
lines.append(f" OS/ABI: {OSABI_NAMES.get(hdr['e_ident'][7], 'Неизвестно')}")
lines.append(f" Версия ABI: {hdr['e_ident'][8]}")
etype = ETYPE_NAMES.get(hdr['e_type'], 'UNKNOWN')
lines.append(f" Тип: {etype}")
if hdr['e_machine'] == EM_X86_64:
lines.append(f" Машина: Advanced Micro Devices X86-64")
else:
lines.append(f" Машина: 0x{hdr['e_machine']:x}")
lines.append(f" Версия: 0x{hdr['e_version']:x}")
lines.append(f" Адрес точки входа: 0x{hdr['e_entry']:x}")
lines.append(f" Начало заголовков программы: {hdr['e_phoff']} (байт в файле)")
lines.append(f" Начало заголовков раздела: {hdr['e_shoff']} (байт в файле)")
lines.append(f" Флаги: 0x{hdr['e_flags']:x}")
lines.append(f" Size of this header: {hdr['e_ehsize']} (bytes)")
lines.append(f" Size of program headers: {hdr['e_phentsize']} (bytes)")
lines.append(f" Number of program headers: {hdr['e_phnum']}")
lines.append(f" Size of section headers: {hdr['e_shentsize']} (bytes)")
lines.append(f" Number of section headers: {hdr['e_shnum']}")
lines.append(f" Section header string table index: {hdr['e_shstrndx']}")
return '\n'.join(lines)
def format_sections(f, sections, shstrtab_offset, shstrtab_size):
"""Возвращает строку с таблицей секций в стиле readelf -S."""
lines = []
lines.append("\nЗаголовки разделов:")
lines.append(" [Нм] Имя Тип Адрес Смещение Размер Разм.Ent Флаги Ссылк Инфо Выравн")
for idx, sec in enumerate(sections):
name = get_section_name(f, shstrtab_offset, shstrtab_size, sec['sh_name'])
stype = SHT_NAMES.get(sec['sh_type'], f"0x{sec['sh_type']:x}")
flags = get_flags_description(sec['sh_flags'], sec['sh_type'], sec['sh_info'])
if idx == 0:
# Пустая секция - специальный формат
lines.append(f" [{idx:2d}] {name:20s} {stype:16s} {sec['sh_addr']:016x} {sec['sh_offset']:08x}")
lines.append(f" {sec['sh_size']:016x} {sec['sh_entsize']:016x} {sec['sh_link']:<6d}{sec['sh_info']:<6d}{sec['sh_addralign']:<8d}")
else:
flags_str = flags if flags else " "
lines.append(f" [{idx:2d}] {name:20s} {stype:16s} {sec['sh_addr']:016x} {sec['sh_offset']:08x}")
lines.append(f" {sec['sh_size']:016x} {sec['sh_entsize']:016x} {flags_str:4s} {sec['sh_link']:<6d}{sec['sh_info']:<6d}{sec['sh_addralign']:<8d}")
# Добавляем легенду флагов
lines.append("Обозначения флагов:")
lines.append(" W (запись), A (назнач), X (исполняемый), M (слияние), S (строки),")
lines.append(" I (инфо), L (порядок ссылок), O (требуется дополнительная работа ОС),")
lines.append(" G (группа), T (TLS), C (сжат), x (неизвестно), o (специфич. для ОС),")
lines.append(" E (исключён), D (mbind), l (большой), p (processor specific)")
return '\n'.join(lines)
def format_program_headers(phdrs):
"""Возвращает строку с программными заголовками в стиле readelf -l."""
lines = []
lines.append("\nЗаголовки программы:")
lines.append(" Тип Смещ. Вирт.адр Физ.адр")
lines.append(" Рзм.фйл Рзм.пм Флаги Выравн")
for ph in phdrs:
ptype = PT_NAMES.get(ph['p_type'], f"0x{ph['p_type']:x}")
# Формируем флаги
flags = ''
if ph['p_flags'] & 4: flags += 'R'
if ph['p_flags'] & 2: flags += 'W'
if ph['p_flags'] & 1: flags += 'E'
if not flags: flags = ' '
lines.append(f" {ptype:14s} 0x{ph['p_offset']:016x} 0x{ph['p_vaddr']:016x} 0x{ph['p_paddr']:016x}")
lines.append(f" 0x{ph['p_filesz']:016x} 0x{ph['p_memsz']:016x} {flags:4s} 0x{ph['p_align']:x}")
return '\n'.join(lines)
def format_segment_section_map(sections, phdrs, get_name_func):
"""Форматирует соответствие сегментов и секций."""
lines = []
lines.append("\n Соответствие раздел-сегмент:")
lines.append(" Сегмент Разделы...")
for pidx, ph in enumerate(phdrs):
seg_sections = []
for sidx, sec in enumerate(sections):
if is_section_in_segment(sec, ph):
name = get_name_func(sidx)
if name:
seg_sections.append(name)
if seg_sections:
section_names = ' '.join(seg_sections)
lines.append(f" {pidx:02d} {section_names}")
else:
lines.append(f" {pidx:02d} ")
return '\n'.join(lines)
# ---------- главная функция ----------
def main():
if len(sys.argv) != 2:
print(f"Использование: {sys.argv[0]} <elf-файл>", flush=True)
sys.exit(1)
filename = sys.argv[1]
try:
with open(filename, 'rb') as f:
# 1. ELF заголовок
hdr = read_elf_header(f)
if hdr['e_machine'] != EM_X86_64:
print("Предупреждение: обнаружена не x86-64 архитектура, вывод может быть некорректным.", flush=True)
print(format_elf_header(hdr), flush=True)
# Читаем секции для использования в других блоках
sections = []
if hdr['e_shnum'] > 0:
sections = read_section_headers(f, hdr['e_shoff'], hdr['e_shnum'])
# Функция для получения имён секций (для маппинга)
def get_sec_name(sidx):
if sidx < len(sections):
shstrtab = sections[hdr['e_shstrndx']]
return get_section_name(f, shstrtab['sh_offset'], shstrtab['sh_size'], sections[sidx]['sh_name'])
return '???'
# Вывод секций
if hdr['e_shnum'] > 0:
shstrtab = sections[hdr['e_shstrndx']]
print(format_sections(f, sections, shstrtab['sh_offset'], shstrtab['sh_size']), flush=True)
else:
print("\nФайл не содержит таблицы разделов.", flush=True)
# Вывод программных заголовков
if hdr['e_phnum'] > 0:
phdrs = read_program_headers(f, hdr['e_phoff'], hdr['e_phnum'])
print(format_program_headers(phdrs), flush=True)
# Соответствие сегментов и секций
print(format_segment_section_map(sections, phdrs, get_sec_name), flush=True)
else:
print("\nФайл не содержит программных заголовков.", flush=True)
except Exception as e:
print(f"Ошибка: {e}", file=sys.stderr, flush=True)
sys.exit(1)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load diff

View file

@ -1,103 +0,0 @@
section .text
global _start
_start:
; Подготовка флагов через сравнение
mov rax, 5
mov rbx, 5
cmp rax, rbx ; устанавливает ZF = 1
; je / jz — короткий переход если равно / ноль
je .label_je
; Если не сработал — ошибка (но он сработает)
mov rdi, 1
mov rax, 60 ; sys_exit
syscall
.label_je:
mov rax, 10
mov rbx, 20
cmp rax, rbx ; ZF = 0, SF ≠ OF → jl сработает
; jl — короткий переход если меньше
jl .label_jl
mov rdi, 2
mov rax, 60
syscall
.label_jl:
; jg — короткий переход если больше
mov rax, 30
mov rbx, 20
cmp rax, rbx ; rax > rbx → jg должен сработать
jg .label_jg
mov rdi, 3
mov rax, 60
syscall
.label_jg:
; jle — меньше или равно
mov rax, 15
mov rbx, 15
cmp rax, rbx ; равно → jle сработает
jle .label_jle
mov rdi, 4
mov rax, 60
syscall
.label_jle:
; jge — больше или равно
mov rax, 100
mov rbx, 50
cmp rax, rbx ; больше → jge сработает
jge .label_jge
mov rdi, 5
mov rax, 60
syscall
.label_jge:
; jc — переход если перенос (CF=1)
; Установим CF через вычитание
mov rax, 5
mov rbx, 10
cmp rax, rbx ; 5 < 10 → CF = 1
jc .label_jc
mov rdi, 6
mov rax, 60
syscall
.label_jc:
; jnc — переход если нет переноса
mov rax, 10
mov rbx, 5
cmp rax, rbx ; CF = 0
jnc .label_jnc
mov rdi, 7
mov rax, 60
syscall
.label_jnc:
; jne — переход если не равно
mov rax, 1
mov rbx, 2
cmp rax, rbx ; не равно → ZF=0 → jne сработает
jne .label_jne
mov rdi, 8
mov rax, 60
syscall
.label_jne:
; Безусловный короткий переход
jmp .label_done
; Если сюда попали — ошибка
mov rdi, 9
mov rax, 60
syscall
.label_done:
; Успешный выход
mov rdi, 0
mov rax, 60 ; sys_exit
syscall

View file

@ -1,102 +0,0 @@
.глобал _start
.текст
_start:
; Подготовка: раикс = 5, рбикс = 5 → сравнение → ZF=1
переместить_имм раикс, 5
переместить_имм рбикс, 5
сравнить раикс, рбикс
; короткий_переход_если_равно
короткий_переход_если_равно метка_je
; Ошибка (не должно сработать)
переместить_имм рдиай, 1
переместить_имм раикс, 60
вызов_системы
метка_je:
переместить_имм раикс, 10
переместить_имм рбикс, 20
сравнить раикс, рбикс
; короткий_переход_если_меньше
короткий_переход_если_меньше метка_jl
переместить_имм рдиай, 2
переместить_имм раикс, 60
вызов_системы
метка_jl:
; короткий_переход_если_больше
переместить_имм раикс, 30
переместить_имм рбикс, 20
сравнить раикс, рбикс
короткий_переход_если_больше метка_jg
переместить_имм рдиай, 3
переместить_имм раикс, 60
вызов_системы
метка_jg:
; короткий_переход_если_меньше_или_равно
переместить_имм раикс, 15
переместить_имм рбикс, 15
сравнить раикс, рбикс
короткий_переход_если_меньше_или_равно метка_jle
переместить_имм рдиай, 4
переместить_имм раикс, 60
вызов_системы
метка_jle:
; короткий_переход_если_больше_или_равно
переместить_имм раикс, 100
переместить_имм рбикс, 50
сравнить раикс, рбикс
короткий_переход_если_больше_или_равно метка_jge
переместить_имм рдиай, 5
переместить_имм раикс, 60
вызов_системы
метка_jge:
; короткий_переход_если_перенос
переместить_имм раикс, 5
переместить_имм рбикс, 10
сравнить раикс, рбикс
короткий_переход_если_перенос метка_jc
переместить_имм рдиай, 6
переместить_имм раикс, 60
вызов_системы
метка_jc:
; короткий_переход_если_нет_переноса
переместить_имм раикс, 10
переместить_имм рбикс, 5
сравнить раикс, рбикс
короткий_переход_если_нет_переноса метка_jnc
переместить_имм рдиай, 7
переместить_имм раикс, 60
вызов_системы
метка_jnc:
; короткий_переход_если_неравно
переместить_имм раикс, 1
переместить_имм рбикс, 2
сравнить раикс, рбикс
короткий_переход_если_неравно метка_jne
переместить_имм рдиай, 8
переместить_имм раикс, 60
вызов_системы
метка_jne:
; короткий_переход (безусловный)
короткий_переход метка_done
; Ошибка — сюда не должны попасть
переместить_имм рдиай, 9
переместить_имм раикс, 60
вызов_системы
метка_done:
; Успешный выход
переместить_имм рдиай, 0
переместить_имм раикс, 60
вызов_системы

View file

@ -1,160 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import csv
import os
# --- Основная функция конвертации ---
def convert_csv_to_html(csv_file_path, html_file_path):
"""
Читает semicolon-separated CSV файл и создает красивую HTML-страницу.
Возвращает (True, сообщение) в случае успеха или (False, сообщение) в случае ошибки.
"""
try:
with open(csv_file_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f, delimiter=';')
headers = reader.fieldnames
if not headers:
return False, "CSV-файл пуст или не имеет заголовков."
rows = list(reader)
except FileNotFoundError:
return False, f"Файл не найден: {csv_file_path}"
except Exception as e:
return False, f"Ошибка при чтении CSV: {e}"
# CSS для красивого оформления
css_style = """
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: #f4f7f6; color: #333; margin: 0; padding: 20px; }
.container { max-width: 1400px; margin: 20px auto; background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); }
h1 { color: #2c3e50; text-align: center; margin-bottom: 25px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #e0e0e0; vertical-align: top; word-wrap: break-word; }
th { background-color: #34495e; color: #ffffff; font-weight: 600; text-transform: uppercase; font-size: 13px; }
tr:nth-child(even) { background-color: #f8f9fa; }
tr:hover { background-color: #e9ecef; }
a { color: #3498db; text-decoration: none; font-weight: bold; }
a:hover { text-decoration: underline; color: #2980b9; }
.mono { font-family: 'Courier New', Courier, monospace; font-size: 13px; white-space: pre-wrap; }
tr:target { background-color: #fff3cd !important; border: 2px solid #ffeaa7; animation: highlight 2s ease-in-out; }
@keyframes highlight { from { background-color: #ffeaa7; } to { background-color: #fff3cd; } }
</style>
"""
# Начало HTML-документа
html_content = f"""
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{os.path.basename(html_file_path)}</title>
{css_style}
</head>
<body>
<div class="container">
<h1>Анализ дизассемблерного листинга: {os.path.basename(csv_file_path)}</h1>
<table>
<thead><tr>
"""
for header in headers:
html_content += f"<th>{header}</th>\n"
html_content += "</tr></thead><tbody>\n"
# Добавляем строки таблицы
for row in rows:
address = row.get('адрес', '').strip()
target_address = row.get('целевой_адрес', '').strip()
link_html = target_address
if target_address:
link_html = f'<a href="#{target_address}">{target_address}</a>'
# Присваиваем id каждой строке на основе ее адреса
row_id = address if address else ''
html_content += f'<tr id="{row_id}">\n'
html_content += f'<td class="mono">{address}</td>\n'
html_content += f'<td class="mono">{row.get("байт", "")}</td>\n'
html_content += f'<td class="mono">{link_html}</td>\n'
html_content += f'<td>{row.get("исходная_команда", "")}</td>\n'
html_content += "</tr>\n"
html_content += "</tbody></table></div></body></html>"
# Запись HTML в файл
try:
with open(html_file_path, 'w', encoding='utf-8') as f:
f.write(html_content)
return True, f"Успешно! Файл сохранен как:\n{html_file_path}"
except Exception as e:
return False, f"Ошибка при записи HTML файла: {e}"
# --- Функция-обработчик нажатия на кнопку ---
def open_file_and_convert():
# Открываем диалог выбора файла
file_path = filedialog.askopenfilename(
title="Выберите CSV-файл для конвертации",
filetypes=(("CSV файлы", "*.csv"), ("Все файлы", "*.*"))
)
# Если пользователь не выбрал файл, выходим
if not file_path:
status_label.config(text="Операция отменена.")
return
# Формируем имя для выходного HTML-файла
base_name = os.path.splitext(file_path)[0]
output_path = f"{base_name}.html"
# Обновляем статус
status_label.config(text="Обработка файла...")
root.update_idletasks() # Принудительно обновляем интерфейс
# Вызываем функцию конвертации
success, message = convert_csv_to_html(file_path, output_path)
# Показываем результат
if success:
status_label.config(text="Готово!")
messagebox.showinfo("Успех", message)
else:
status_label.config(text="Произошла ошибка.")
messagebox.showerror("Ошибка", message)
# --- Создание графического интерфейса ---
root = tk.Tk()
root.title("CSV в HTML Конвертер")
root.geometry("450x200") # Размер окна
root.resizable(False, False) # Запрещаем изменять размер
# Создаем фрейм для лучшего расположения элементов
main_frame = tk.Frame(root, padx=20, pady=20)
main_frame.pack(expand=True, fill=tk.BOTH)
# Кнопка "Открыть"
open_button = tk.Button(
main_frame,
text="Открыть CSV-файл",
command=open_file_and_convert,
width=20,
height=2,
font=("Helvetica", 12)
)
open_button.pack(pady=10)
# Метка для отображения статуса
status_label = tk.Label(
main_frame,
text="Нажмите кнопку, чтобы выбрать файл",
wraplength=400, # Перенос длинных строк
justify=tk.CENTER
)
status_label.pack(pady=10)
# Запускаем главный цикл приложения
root.mainloop()

View file

@ -1,149 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
from datetime import datetime
def binary_to_disasm(input_file, output_file=None, skip_zeros=True):
"""Конвертирует бинарный файл в дизассемблированный код с помощью ndisasm."""
try:
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_disasm.txt"
# Вызываем ndisasm для дизассемблирования
result = subprocess.run(
['ndisasm', '-b64', input_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Проверяем наличие ошибок от ndisasm
if result.returncode != 0:
error_msg = result.stderr.strip()
return False, f"Ошибка ndisasm: {error_msg}"
# Получаем текущую дату и время
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Фильтруем строки в зависимости от настройки
lines = result.stdout.splitlines()
if skip_zeros:
filtered_lines = [line for line in lines if 'add [rax],al' not in line]
filter_status = "Нули (add [rax],al) пропущены"
else:
filtered_lines = lines
filter_status = "Нули (add [rax],al) не пропущены"
# Записываем отфильтрованные строки в файл с заголовком
with open(output_file, 'w', encoding='utf-8') as out_file:
# Добавляем заголовок с информацией
out_file.write("=" * 60 + "\n")
out_file.write(f"Дизассемблированный код сгенерирован с помощью ndisasm\n")
out_file.write(f"Исходный файл: {os.path.basename(input_file)}\n")
out_file.write(f"Дата создания: {current_time}\n")
out_file.write(f"Статус фильтра: {filter_status}\n")
out_file.write("=" * 60 + "\n\n")
for line in filtered_lines:
out_file.write(line + '\n')
return True, output_file
except FileNotFoundError:
return False, "ndisasm не найден. Убедитесь, что NASM установлен и доступен в PATH"
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.bin *.elf *.o"), ("Все файлы", "*.*"))
)
if not input_file:
return
# Получаем состояние галочки
skip_zeros = skip_zeros_var.get()
success, result = binary_to_disasm(input_file, skip_zeros=skip_zeros)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно дизассемблирован!\n"
f"Пропуск нулей: {'ВКЛ' if skip_zeros else 'ВЫКЛ'}\n"
f"Результат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось дизассемблировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
global skip_zeros_var
root = tk.Tk()
root.title("Дизассемблер бинарных файлов")
root.geometry("500x250")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Переменная для хранения состояния галочки
skip_zeros_var = tk.BooleanVar(value=True) # По умолчанию включено
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Дизассемблер бинарных файлов (ndisasm)",
font=('Arial', 14),
pady=20
)
label.pack()
# Фрейм для галочки
checkbox_frame = tk.Frame(root)
checkbox_frame.pack(pady=10)
# Галочка для пропуска нулей
skip_zeros_check = tk.Checkbutton(
checkbox_frame,
text="Пропускать нули (add [rax],al)",
variable=skip_zeros_var,
font=('Arial', 10)
)
skip_zeros_check.pack()
# Кнопка конвертации
btn_convert = tk.Button(
root,
text="Выбрать файл и дизассемблировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack(pady=10)
# Кнопка выхода
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=10)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,653 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_5.py
КВС - язык программирования
без использования re
(расширенная версия с короткими переходами и полными регистрами)
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
# === Короткие переходы (добавлено) ===
"короткий_переход": {"code": b"\xEB", "type": "short_jmp"},
"короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"},
"короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"},
"короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"},
"короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"},
"короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"},
"короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"},
"короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"},
"короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неоль" : {"code": b"\x75", "type": "short_jcc"},
}
REGISTERS = {
# 64-битные
"раикс": 0, "рсикс": 1, "рдикс": 2, "рбикс": 3,
"рсипи": 4, "рбипи": 5, "рсиай": 6, "рдиай": 7,
"р8": 8, "р9": 9, "р10": 10, "р11": 11,
"р12": 12, "р13": 13, "р14": 14, "р15": 15,
# 32-битные
"еаикс": 0, "есикс": 1, "едикс": 2, "ебикс": 3,
"есипи": 4, "ебипи": 5, "есиай": 6, "едиай": 7,
"р": 8, "р": 9, "р10д": 10, "р11д": 11,
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
# 16-битные
"аикс": 0, "сикс": 1, "дикс": 2, "бикс": 3,
"эсп": 4, "бипи": 5, "эс": 6, "ди": 7,
"р": 8, "р": 9, "р10в": 10, "р11в": 11,
"р12в": 12, "р13в": 13, "р14в": 14, "р15в": 15,
# 8-битные младшие
"ал": 0, "кл": 1, "дл": 2, "бл": 3,
"спл": 4, "бпл": 5, "сил": 6, "дил": 7,
"р8б": 8, "р9б": 9, "р10б": 10, "р11б": 11,
"р12б": 12, "р13б": 13, "р14б": 14, "р15б": 15,
# 8-битные старшие (только первые 4)
"аш": 0, "чш": 1, "дш": 2, "бш": 3,
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000
text_vaddr_base = 0x401000
data_vaddr_base = 0x402000
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]:
return {"size": 16, "index": REGISTERS[reg_name]}
elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]:
return {"size": 32, "index": REGISTERS[reg_name]}
else:
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n " + line_text + "\n" + detail
def error_invalid_operand_count(mnemonic, expected, got):
return "Инструкция '" + mnemonic + "':\n ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got)
def error_unknown_mnemonic(mnemonic):
return "Неизвестная инструкция: '" + mnemonic + "'"
def error_invalid_register(op_name, reg_name, mnemonic):
return ("Инструкция '" + mnemonic + "':\n операнд '" + op_name + "' = '" + reg_name +
"' не является допустимым регистром.\n Допустимые регистры: " + ", ".join(list(REGISTERS.keys())))
def error_invalid_number_format(s):
return "Недопустимый формат числа: '" + s + "'"
def error_unknown_directive(word):
return "Неизвестная директива: '" + word + "'"
def error_missing_label(label):
return "Метка не найдена: '" + label + "'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if pass_num == 2:
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
else:
return labels[operand]
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError("Недопустимый регистр в '" + mnemonic + "'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm))
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель условного перехода слишком далеко: " + str(offset))
code.extend(struct.pack('<i', offset))
# === НОВОЕ: короткие переходы ===
elif itype == "short_jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset))
code.append(0xEB)
code.append(offset & 0xFF)
elif itype == "short_jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset))
code.append(offset & 0xFF)
else:
raise ValueError("Неизвестный тип инструкции: " + itype)
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError("Ожидалось слово, получено: " + str(first))
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(word + " требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError("Недопустимый токен в операндах: " + str(tok))
if pass_num == 2:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = ""
try:
if itype in ("jmp", "jcc", "short_jmp", "short_jcc"):
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
addr = labels[op_str] + (vaddr_text if sec == ".text" else vaddr_data)
target_addr = hex(addr)
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = "{:02X}".format(byte)
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
"short_jmp": 2,
"short_jcc": 2,
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype)
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
log_entries = []
lines = source.split('\n')
# ПРОХОД 1
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print("ПРОХОД 1: text_size=" + str(text_size_pass1) + " bytes, data_size=" + str(data_size_pass1) + " bytes")
# РАЗМЕЩЕНИЕ
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print("РАЗМЕЩЕНИЕ: .text at offset=" + hex(offset_text) + ", .data at offset=" + hex(offset_data))
print("ВИРТУАЛЬНЫЕ АДРЕСА: .text at " + hex(vaddr_text) + ", .data at " + hex(vaddr_data))
print("РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: " + hex(vaddr_data - vaddr_text) + " bytes")
sections[".comment"] = comment_content
# ПРОХОД 2
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print("ПРОХОД 2: text_size=" + str(text_size_pass2) + " bytes, data_size=" + str(data_size_pass2) + " bytes")
if text_size_pass1 != text_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: " + str(text_size_pass1) + " -> " + str(text_size_pass2))
if data_size_pass1 != data_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: " + str(data_size_pass1) + " -> " + str(data_size_pass2))
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
actual_text_size = len(text)
actual_data_size = len(data)
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print("КОРРЕКЦИЯ: vaddr_data исправлен с " + hex(vaddr_data) + " на " + hex(expected_vaddr_data))
vaddr_data = expected_vaddr_data
print("ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text=" + str(actual_text_size) + ", .data=" + str(actual_data_size))
print("КОНЕЧНЫЕ АДРЕСА: .text ends at " + hex(vaddr_text + actual_text_size) + ", .data starts at " + hex(vaddr_data))
print("ПРОВЕРКА ПЕРЕКРЫТИЯ: " + ("ПЕРЕКРЫТИЕ!" if (vaddr_text + actual_text_size) > vaddr_data else "OK"))
entry_addr = vaddr_text + labels.get(entry_point, 0)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2,
0x3e,
1,
entry_addr,
elf_header_size,
shdr_offset,
0,
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(1, 4, 0, elf_base_vaddr, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text), align_up(len(text), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data), align_up(len(data), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_4.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
log_file = source_file[:-4] + ".log.csv"
with open(log_file, "w", encoding="utf-8") as f_log:
f_log.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(addr + ";" + byte + ";" + target + ";" + cmd + "\n")
print("Лог создан: " + log_file)
print("ELF-файл создан: " + elf_file)
print("Запустить: ./" + elf_file)
except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)

View file

@ -1,930 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_6.py
КВС - язык программирования
без использования re
(расширенная версия с короткими переходами и полными регистрами)
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
# === Короткие переходы ===
"короткий_переход": {"code": b"\xEB", "type": "short_jmp"},
"короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"},
"короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"},
"короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"},
"короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"},
"короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"},
"короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"},
"короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"},
"короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неоль" : {"code": b"\x75", "type": "short_jcc"},
# === Новые команды для флагов ===
"сравнить": {"code": b"\x48\x39", "type": "cmp_reg_reg"},
"проверить": {"code": b"\x48\x85", "type": "test_reg_reg"},
"вычесть": {"code": b"\x48\x29", "type": "sub_reg_reg"},
"прибавить": {"code": b"\x48\x01", "type": "add_reg_reg"},
"увеличить": {"code": b"\xFF", "subop": 0, "type": "incdec"},
"уменьшить": {"code": b"\xFF", "subop": 1, "type": "incdec"},
}
REGISTERS = {
# 64-битные
"раикс": 0, "рсикс": 1, "рдикс": 2, "рбикс": 3,
"рсипи": 4, "рбипи": 5, "рсиай": 6, "рдиай": 7,
"р8": 8, "р9": 9, "р10": 10, "р11": 11,
"р12": 12, "р13": 13, "р14": 14, "р15": 15,
# 32-битные
"еаикс": 0, "есикс": 1, "едикс": 2, "ебикс": 3,
"есипи": 4, "ебипи": 5, "есиай": 6, "едиай": 7,
"р": 8, "р": 9, "р10д": 10, "р11д": 11,
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
# 16-битные
"аикс": 0, "сикс": 1, "дикс": 2, "бикс": 3,
"эсп": 4, "бипи": 5, "эс": 6, "ди": 7,
"р": 8, "р": 9, "р10в": 10, "р11в": 11,
"р12в": 12, "р13в": 13, "р14в": 14, "р15в": 15,
# 8-битные младшие
"ал": 0, "кл": 1, "дл": 2, "бл": 3,
"спл": 4, "бпл": 5, "сил": 6, "дил": 7,
"р8б": 8, "р9б": 9, "р10б": 10, "р11б": 11,
"р12б": 12, "р13б": 13, "р14б": 14, "р15б": 15,
# 8-битные старшие (только первые 4)
"аш": 0, "чш": 1, "дш": 2, "бш": 3,
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000
text_vaddr_base = 0x401000
data_vaddr_base = 0x402000
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]:
return {"size": 16, "index": REGISTERS[reg_name]}
elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]:
return {"size": 32, "index": REGISTERS[reg_name]}
else:
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n " + line_text + "\n" + detail
def error_invalid_operand_count(mnemonic, expected, got):
return "Инструкция '" + mnemonic + "':\n ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got)
def error_unknown_mnemonic(mnemonic):
return "Неизвестная инструкция: '" + mnemonic + "'"
def error_invalid_register(op_name, reg_name, mnemonic):
return ("Инструкция '" + mnemonic + "':\n операнд '" + op_name + "' = '" + reg_name +
"' не является допустимым регистром.\n Допустимые регистры: " + ", ".join(list(REGISTERS.keys())))
def error_invalid_number_format(s):
return "Недопустимый формат числа: '" + s + "'"
def error_unknown_directive(word):
return "Неизвестная директива: '" + word + "'"
def error_missing_label(label):
return "Метка не найдена: '" + label + "'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if pass_num == 2:
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
else:
return labels[operand]
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
if size == 64:
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError("Недопустимый регистр в '" + mnemonic + "'")
elif size == 32:
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное значение вне диапазона")
elif size == 16:
if 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0xB0 + reg + 4)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xB0 + reg)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xB0 + (reg & 7))
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
if size == 64:
if -128 <= imm <= 127:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm))
elif size == 32:
if -128 <= imm <= 127:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное непосредственное значение вне диапазона")
elif size == 16:
if -128 <= imm <= 127:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное непосредственное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x80)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель условного перехода слишком далеко: " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "short_jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset))
code.append(0xEB)
code.append(offset & 0xFF)
elif itype == "short_jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset))
code.append(offset & 0xFF)
elif itype in ("cmp_reg_reg", "test_reg_reg", "add_reg_reg", "sub_reg_reg"):
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
if dst_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
if src_info is None:
raise ValueError(error_invalid_register("источника", operands[1], mnemonic))
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
if size != src_info["size"]:
raise ValueError("Размеры операндов не совпадают в инструкции '" + mnemonic + "'")
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
if rex != 0x40:
raise ValueError("Старшие байты несовместимы с REX")
rex = 0
op_map = {
"cmp_reg_reg": 0x39,
"test_reg_reg": 0x85,
"add_reg_reg": 0x01,
"sub_reg_reg": 0x29,
}
op = op_map[itype]
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.append(op)
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
elif itype == "incdec":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
subop = instr["subop"]
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFF)
elif size == 32:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 8:
if reg_info.get("high8"):
if reg < 4:
code.append(0xFE)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xFE)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFE)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return bytes(code)
else:
raise ValueError("Неподдерживаемый размер регистра")
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
else:
raise ValueError("Неизвестный тип инструкции: " + itype)
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point, symbols # Не забудьте добавить symbols в global
first = tokens[0]
if first[0] != 'word':
raise ValueError("Ожидалось слово, получено: " + str(first))
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(word + " требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
# === НОВАЯ ДИРЕКТИВА .КОНСТАНТА ===
elif word == '.константа':
if len(tokens) < 3 or tokens[1][0] != 'word' or tokens[2][0] != 'word' or tokens[2][1] != '=' or len(tokens) < 4 or tokens[3][0] not in ('word', 'string'): # Проверяем формат: .константа имя = значение
raise ValueError(".константа требует формат: .константа <имя> = <значение>")
const_name = tokens[1][1]
const_value_str = tokens[3][1]
try:
# Попробуем распарсить значение как число или символ (если это метка)
const_value = parse_operand(const_value_str)
except ValueError:
raise ValueError("Невозможно распознать значение константы '" + const_value_str + "'")
# Записываем в глобальный словарь символов
symbols[const_name] = const_value
# Директива .константа не добавляет байты в секцию
return
# === КОНЕЦ НОВОЙ ДИРЕКТИВЫ ===
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError("Недопустимый токен в операндах: " + str(tok))
if pass_num == 2:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = ""
try:
if itype in ("jmp", "jcc", "short_jmp", "short_jcc"):
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
addr = labels[op_str] + (vaddr_text if sec == ".text" else vaddr_data)
target_addr = hex(addr)
elif op_str in symbols: # Проверяем символы
target_addr = hex(symbols[op_str])
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = "{:02X}".format(byte)
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
"short_jmp": 2,
"short_jcc": 2,
"cmp_reg_reg": 3,
"test_reg_reg": 3,
"add_reg_reg": 3,
"sub_reg_reg": 3,
"incdec": 3,
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype)
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
log_entries = []
lines = source.split('\n')
# ПРОХОД 1
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print("ПРОХОД 1: text_size=" + str(text_size_pass1) + " bytes, data_size=" + str(data_size_pass1) + " bytes")
# РАЗМЕЩЕНИЕ
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print("РАЗМЕЩЕНИЕ: .text at offset=" + hex(offset_text) + ", .data at offset=" + hex(offset_data))
print("ВИРТУАЛЬНЫЕ АДРЕСА: .text at " + hex(vaddr_text) + ", .data at " + hex(vaddr_data))
print("РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: " + hex(vaddr_data - vaddr_text) + " bytes")
sections[".comment"] = comment_content
# ПРОХОД 2
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print("ПРОХОД 2: text_size=" + str(text_size_pass2) + " bytes, data_size=" + str(data_size_pass2) + " bytes")
if text_size_pass1 != text_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: " + str(text_size_pass1) + " -> " + str(text_size_pass2))
if data_size_pass1 != data_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: " + str(data_size_pass1) + " -> " + str(data_size_pass2))
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
actual_text_size = len(text)
actual_data_size = len(data)
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print("КОРРЕКЦИЯ: vaddr_data исправлен с " + hex(vaddr_data) + " на " + hex(expected_vaddr_data))
vaddr_data = expected_vaddr_data
print("ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text=" + str(actual_text_size) + ", .data=" + str(actual_data_size))
print("КОНЕЧНЫЕ АДРЕСА: .text ends at " + hex(vaddr_text + actual_text_size) + ", .data starts at " + hex(vaddr_data))
print("ПРОВЕРКА ПЕРЕКРЫТИЯ: " + ("ПЕРЕКРЫТИЕ!" if (vaddr_text + actual_text_size) > vaddr_data else "OK"))
entry_addr = vaddr_text + labels.get(entry_point, 0)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2,
0x3e,
1,
entry_addr,
elf_header_size,
shdr_offset,
0,
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(1, 4, 0, elf_base_vaddr, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text), align_up(len(text), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data), align_up(len(data), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
f_out = open(filename, "wb")
f_out.write(elf_data)
f_out.close()
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_5.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
log_file = source_file[:-4] + ".log.csv"
f_log = open(log_file, "w", encoding="utf-8")
f_log.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(addr + ";" + byte + ";" + target + ";" + cmd + "\n")
f_log.close()
print("Лог создан: " + log_file)
print("ELF-файл создан: " + elf_file)
print("Запустить: ./" + elf_file)
except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)

View file

@ -1,933 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_7.py
КВС - язык программирования
без использования re
(расширенная версия с короткими и длинными переходами и полными регистрами)
(исправленная версия с использованием 32-битной формы для 64-битных регистров в cmp_reg_imm)
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"переход_если_равно": {"code": b"\x0F\x84", "type": "jcc"}, # je / jz
"переход_если_меньше": {"code": b"\x0F\x8C", "type": "jcc"}, # jl
"переход_если_больше": {"code": b"\x0F\x8F", "type": "jcc"}, # jg
"переход_если_меньше_или_равно": {"code": b"\x0F\x8E", "type": "jcc"}, # jle
"переход_если_больше_или_равно": {"code": b"\x0F\x8D", "type": "jcc"}, # jge
"переход_если_перенос": {"code": b"\x0F\x82", "type": "jcc"}, # jc
"переход_если_нет_переноса": {"code": b"\x0F\x83", "type": "jcc"}, # jnc
"переход_если_ноль": {"code": b"\x0F\x84", "type": "jcc"}, # jz (дубль JE)
"переход_если_неоль": {"code": b"\x0F\x85", "type": "jcc"}, # jnz (дубль JNE)
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
# === Короткие переходы ===
"короткий_переход": {"code": b"\xEB", "type": "short_jmp"},
"короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"},
"короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"},
"короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"},
"короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"},
"короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"},
"короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"},
"короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"},
"короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неоль" : {"code": b"\x75", "type": "short_jcc"},
# === Новые команды для флагов ===
"сравнить": {"code": b"\x48\x39", "type": "cmp_reg_reg"},
"проверить": {"code": b"\x48\x85", "type": "test_reg_reg"},
"вычесть": {"code": b"\x48\x29", "type": "sub_reg_reg"},
"прибавить": {"code": b"\x48\x01", "type": "add_reg_reg"},
"увеличить": {"code": b"\xFF", "subop": 0, "type": "incdec"},
"уменьшить": {"code": b"\xFF", "subop": 1, "type": "incdec"},
}
REGISTERS = {
# 64-битные
"раикс": 0, "рсикс": 1, "рдикс": 2, "рбикс": 3,
"рсипи": 4, "рбипи": 5, "рсиай": 6, "рдиай": 7,
"р8": 8, "р9": 9, "р10": 10, "р11": 11,
"р12": 12, "р13": 13, "р14": 14, "р15": 15,
# 32-битные
"еаикс": 0, "есикс": 1, "едикс": 2, "ебикс": 3,
"есипи": 4, "ебипи": 5, "есиай": 6, "едиай": 7,
"р": 8, "р": 9, "р10д": 10, "р11д": 11,
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
# 16-битные
"аикс": 0, "сикс": 1, "дикс": 2, "бикс": 3,
"эсп": 4, "бипи": 5, "эс": 6, "ди": 7,
"р": 8, "р": 9, "р10в": 10, "р11в": 11,
"р12в": 12, "р13в": 13, "р14в": 14, "р15в": 15,
# 8-битные младшие
"ал": 0, "кл": 1, "дл": 2, "бл": 3,
"спл": 4, "бпл": 5, "сил": 6, "дил": 7,
"р8б": 8, "р9б": 9, "р10б": 10, "р11б": 11,
"р12б": 12, "р13б": 13, "р14б": 14, "р15б": 15,
# 8-битные старшие (только первые 4)
"аш": 0, "чш": 1, "дш": 2, "бш": 3,
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000
text_vaddr_base = 0x401000
data_vaddr_base = 0x402000
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]:
return {"size": 16, "index": REGISTERS[reg_name]}
elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]:
return {"size": 32, "index": REGISTERS[reg_name]}
else:
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n " + line_text + "\n" + detail
def error_invalid_operand_count(mnemonic, expected, got):
return "Инструкция '" + mnemonic + "':\n ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got)
def error_unknown_mnemonic(mnemonic):
return "Неизвестная инструкция: '" + mnemonic + "'"
def error_invalid_register(op_name, reg_name, mnemonic):
return ("Инструкция '" + mnemonic + "':\n операнд '" + op_name + "' = '" + reg_name +
"' не является допустимым регистром.\n Допустимые регистры: " + ", ".join(list(REGISTERS.keys())))
def error_invalid_number_format(s):
return "Недопустимый формат числа: '" + s + "'"
def error_unknown_directive(word):
return "Неизвестная директива: '" + word + "'"
def error_missing_label(label):
return "Метка не найдена: '" + label + "'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if pass_num == 2:
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
else:
return labels[operand]
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
if size == 64:
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError("Недопустимый регистр в '" + mnemonic + "'")
elif size == 32:
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное значение вне диапазона")
elif size == 16:
if 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0xB0 + reg + 4)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xB0 + reg)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xB0 + (reg & 7))
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
# --- ИЗМЕНЕНИЕ: Всегда используем 32-битную форму для 64-битных регистров, если imm помещается ---
if size == 64:
if -0x80000000 <= imm <= 0x7FFFFFFF:
# Используем 32-битное сравнение (с REX.W для 64-битного регистра)
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81) # cmp r/m64, imm32
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm)) # imm32 sign-extended
else:
raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm))
elif size == 32:
if -128 <= imm <= 127:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное непосредственное значение вне диапазона")
elif size == 16:
if -128 <= imm <= 127:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное непосредственное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x80)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель условного перехода слишком далеко: " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "short_jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset))
code.append(0xEB)
code.append(offset & 0xFF)
elif itype == "short_jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset))
code.append(offset & 0xFF)
elif itype in ("cmp_reg_reg", "test_reg_reg", "add_reg_reg", "sub_reg_reg"):
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
if dst_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
if src_info is None:
raise ValueError(error_invalid_register("источника", operands[1], mnemonic))
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
if size != src_info["size"]:
raise ValueError("Размеры операндов не совпадают в инструкции '" + mnemonic + "'")
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
if rex != 0x40:
raise ValueError("Старшие байты несовместимы с REX")
rex = 0
op_map = {
"cmp_reg_reg": 0x39,
"test_reg_reg": 0x85,
"add_reg_reg": 0x01,
"sub_reg_reg": 0x29,
}
op = op_map[itype]
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.append(op)
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
elif itype == "incdec":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
subop = instr["subop"]
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFF)
elif size == 32:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 8:
if reg_info.get("high8"):
if reg < 4:
code.append(0xFE)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xFE)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFE)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return bytes(code)
else:
raise ValueError("Неподдерживаемый размер регистра")
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
else:
raise ValueError("Неизвестный тип инструкции: " + itype)
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point, symbols # Не забудьте добавить symbols в global
first = tokens[0]
if first[0] != 'word':
raise ValueError("Ожидалось слово, получено: " + str(first))
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(word + " требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
# === НОВАЯ ДИРЕКТИВА .КОНСТАНТА ===
elif word == '.константа':
if len(tokens) < 3 or tokens[1][0] != 'word' or tokens[2][0] != 'word' or tokens[2][1] != '=' or len(tokens) < 4 or tokens[3][0] not in ('word', 'string'): # Проверяем формат: .константа имя = значение
raise ValueError(".константа требует формат: .константа <имя> = <значение>")
const_name = tokens[1][1]
const_value_str = tokens[3][1]
try:
# Попробуем распарсить значение как число или символ (если это метка)
const_value = parse_operand(const_value_str)
except ValueError:
raise ValueError("Невозможно распознать значение константы '" + const_value_str + "'")
# Записываем в глобальный словарь символов
symbols[const_name] = const_value
# Директива .константа не добавляет байты в секцию
return
# === КОНЕЦ НОВОЙ ДИРЕКТИВЫ ===
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError("Недопустимый токен в операндах: " + str(tok))
if pass_num == 2:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = ""
try:
if itype in ("jmp", "jcc", "short_jmp", "short_jcc"):
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
addr = labels[op_str] + (vaddr_text if sec == ".text" else vaddr_data)
target_addr = hex(addr)
elif op_str in symbols: # Проверяем символы
target_addr = hex(symbols[op_str])
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = "{:02X}".format(byte)
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7, # 6 байт для REX.W + 0x81 + ModR/M + imm32
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
"short_jmp": 2,
"short_jcc": 2,
"cmp_reg_reg": 3,
"test_reg_reg": 3,
"add_reg_reg": 3,
"sub_reg_reg": 3,
"incdec": 3,
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype)
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
log_entries = []
lines = source.split('\n')
# ПРОХОД 1
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print("ПРОХОД 1: text_size=" + str(text_size_pass1) + " bytes, data_size=" + str(data_size_pass1) + " bytes")
# РАЗМЕЩЕНИЕ
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print("РАЗМЕЩЕНИЕ: .text at offset=" + hex(offset_text) + ", .data at offset=" + hex(offset_data))
print("ВИРТУАЛЬНЫЕ АДРЕСА: .text at " + hex(vaddr_text) + ", .data at " + hex(vaddr_data))
print("РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: " + hex(vaddr_data - vaddr_text) + " bytes")
sections[".comment"] = comment_content
# ПРОХОД 2
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print("ПРОХОД 2: text_size=" + str(text_size_pass2) + " bytes, data_size=" + str(data_size_pass2) + " bytes")
if text_size_pass1 != text_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: " + str(text_size_pass1) + " -> " + str(text_size_pass2))
if data_size_pass1 != data_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: " + str(data_size_pass1) + " -> " + str(data_size_pass2))
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
actual_text_size = len(text)
actual_data_size = len(data)
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print("КОРРЕКЦИЯ: vaddr_data исправлен с " + hex(vaddr_data) + " на " + hex(expected_vaddr_data))
vaddr_data = expected_vaddr_data
print("ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text=" + str(actual_text_size) + ", .data=" + str(actual_data_size))
print("КОНЕЧНЫЕ АДРЕСА: .text ends at " + hex(vaddr_text + actual_text_size) + ", .data starts at " + hex(vaddr_data))
print("ПРОВЕРКА ПЕРЕКРЫТИЯ: " + ("ПЕРЕКРЫТИЕ!" if (vaddr_text + actual_text_size) > vaddr_data else "OK"))
entry_addr = vaddr_text + labels.get(entry_point, 0)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2,
0x3e,
1,
entry_addr,
elf_header_size,
shdr_offset,
0,
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(1, 4, 0, elf_base_vaddr, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text), align_up(len(text), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data), align_up(len(data), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
f_out = open(filename, "wb")
f_out.write(elf_data)
f_out.close()
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_7.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
log_file = source_file[:-4] + ".log.csv"
f_log = open(log_file, "w", encoding="utf-8")
f_log.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(addr + ";" + byte + ";" + target + ";" + cmd + "\n")
f_log.close()
print("Лог создан: " + log_file)
print("ELF-файл создан: " + elf_file)
print("Запустить: ./" + elf_file)
except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)

View file

@ -1,124 +0,0 @@
;длин_тест0.квс
; Тест: один длинный переход и один мусорный сегмент
.текст
.глобал _start
_start:
; Инициализация регистра
переместить_имм раикс, 10 ; rax = 10 (не ноль)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'вывести_сообщение', пропуская мертвый_код_1
переход_если_неоль вывести_сообщение
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
; Добавляем много "мусора", чтобы точно выйти за пределы короткого перехода
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
; --- КОД ОШИБКИ (на всякий случай) ---
; Если мы сюда попали, значит переход НЕ сработал (ошибка)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_fail
переместить_имм рдикс, len_msg_fail
вызов_системы
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14
msg_fail: .строкауль "Ошибка: переход не сработал!"
.константа len_msg_fail = 28

View file

@ -1,230 +0,0 @@
; длин_тест1.квс
; Тест: два длинных перехода и два мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10 (не ноль)
переместить_имм рбикс, 5 ; rbx = 5 (для сравнения)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
; Добавляем "мусор", чтобы точно выйти за пределы короткого перехода
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0 (т.е. когда НЕ равны).
; У нас нет инструкции 'переход_если_не_равно' (jne), но она эквивалентна 'переход_если_неоль' (jnz) для флага ZF.
; Однако, у нас есть 'переход_если_равно' (je), который сработает при ZF=1.
; Мы хотим, чтобы переход НЕ срабатывал при ZF=0.
; Поэтому мы используем логику: if (ZF == 0) jmp; (псевдокод).
; Это можно выразить как: if (ZF == 1) goto skip_jmp; jmp target; skip_jmp: ... (псевдокод).
; Это и есть: cmp ...; je skip_jmp; jmp target; skip_jmp: ...
; Где 'jmp target' - это прыжок, который происходит, если ZF=0 (т.е. когда je НЕ сработал).
; Итак:
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0, переход НЕ сработает -> идем дальше)
; переход мимоертвогоода_2 (ZF=0, СРАБОТАЕТ -> прыгаем мимо мертвого кода)
; skip_jmp: (ZF=1, мы сюда попали, прыгнув через безусловный переход)
; [мертвый код 2 - выполнится ТОЛЬКО если ZF=1]
; мимоертвогоода_2:
; [продолжение]
; Это как раз то, что нужно!
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0 -> не прыгаем)
; переход мимоертвогоода_2 (ZF=0 -> прыгаем мимо мертвого кода 2)
; skip_jmp: (ZF=1 -> сюда попали, прыгнув через безусловный переход)
сравнить раикс, рбикс
переход_если_равно skip_jmp
переход мимоертвогоода_2
skip_jmp:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
; Добавляем "мусор", чтобы точно выйти за пределы короткого перехода
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12 ;
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 14
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14

View file

@ -1,175 +0,0 @@
; длин_тест2.квс
; Тест: три длинных перехода и три мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12 ; Длина "Успех!\n" в UTF-8 без нулевого байта
; "Мертвый код 3!" = 12 букв * 2 байта (UTF-8) + 1 (!) + 1 (\n от .строкауль) = 24 + 2 = 26 байт. len = 25 (без \0)
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 25 ; Длина "Мертвый код 3!" в UTF-8 без нулевого байта
; "Мертвый код 2!" = 12 букв * 2 байта (UTF-8) + 1 (!) + 1 (\n от .строкауль) = 24 + 2 = 26 байт. len = 25 (без \0)
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 25 ; Длина "Мертвый код 2!" в UTF-8 без нулевого байта
; "Мертвый код 1!" = 12 букв * 2 байта (UTF-8) + 1 (!) + 1 (\n от .строкауль) = 24 + 2 = 26 байт. len = 25 (без \0)
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 25 ; Длина "Мертвый код 1!" в UTF-8 без нулевого байта

View file

@ -1,196 +0,0 @@
; длин_тест3.квс
; Тест: четыре длинных перехода и четыре мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 1 (\n от .строкауль) = 10 + 2 = 12 байт. len = 12
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 19
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 19
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19

View file

@ -1,260 +0,0 @@
; длин_тест4.квс
; Тест: пять длинных переходов и пять мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
переход_если_меньше_или_равно skip_jmp_5
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 1 (\n от .строкауль) = 10 + 2 = 12 байт. len = 12
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 19
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 19
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 19
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19

View file

@ -1,280 +0,0 @@
; длин_тест5.квс
; Тест: шесть длинных переходов и шесть мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
переход_если_меньше_или_равно skip_jmp_5
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 1 (\n от .строкауль) = 10 + 2 = 12 байт. len = 12
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 19
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 19
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 19
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 19
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19

View file

@ -1,374 +0,0 @@
; длин_тест6.квс
; Тест: семь длинных переходов и семь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
; Добавляем "мусор", чтобы точно выйти за пределы короткого перехода
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
переход_если_меньше_или_равно skip_jmp_5
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,396 +0,0 @@
; длин_тест7.квс
; Тест: восемь длинных переходов и восемь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
; Добавляем "мусор", чтобы точно выйти за пределы короткого перехода
переход_если_неоль проверка_2
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
переход_если_меньше_или_равно skip_jmp_5
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 1 (\n от .строкауль) = 12 байт на содержимое. len = 12
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 19
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 19
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 19
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 19
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 19
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 19
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19

View file

@ -1,409 +0,0 @@
; длин_тест8.квс
; Тест: девять длинных переходов и девять мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
; Добавляем "мусор", чтобы точно выйти за пределы короткого перехода
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 2
переместить_имм рдиай, 2
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 3
переместить_имм рдиай, 3
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 4
переместить_имм рдиай, 4
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 5
переместить_имм рдиай, 5
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 6
переместить_имм рдиай, 6
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 7
переместить_имм рдиай, 7
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 8
переместить_имм рдиай, 8
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 9
переместить_имм рдиай, 9
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 10
переместить_имм рдиай, 10
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 11
переместить_имм рдиай, 11
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 12
переместить_имм рдиай, 12
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 13
переместить_имм рдиай, 13
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 14
переместить_имм рдиай, 14
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
переместить_имм раикс, 15
переместить_имм рдиай, 15
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
переход_если_равно skip_jmp_2
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
переход_если_меньше skip_jmp_3
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
переход_если_меньше_или_равно skip_jmp_5
; Добавляем "мусор" из нет_операции для увеличения расстояния
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; ДЕВЯТЫЙ ПЕРЕХОД: переход (безусловный)
; Просто прыгаем мимо мертвого кода 9.
переход мимоертвогоода_9
; --- МЕРТВЫЙ КОД 9 ---
; Этот код НЕ должен выполниться (так как jmp сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_9
переместить_имм рдикс, len_msg_dead_9
вызов_системы
; ---------------------
мимоертвогоода_9:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 1 (\n от .строкауль) = 10 + 2 = 12 байт на содержимое. len = 12
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 12
; "Мертвый код 9!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_9: .строкауль "Мертвый код 9!"
.константа len_msg_dead_9 = 19
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 19
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 19
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 19
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 19
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 19
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 19
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19

View file

@ -1,35 +0,0 @@
.текст
.глобал _start
_start:
; Инициализация: raikc = 1 → пропустить первую строку
; Попробуйте изменить на 0 — тогда обе строки напечатаются
переместить_имм раикс, 1
сравнить_с раикс, 1 ; ← обязательно!
; Если raikc == 0 → прыгаем к выводу второй строки
короткий_переход_если_ноль пропустить_первую
; Вывод первой строки
переместить_имм раикс, 1 ; write
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, msg0 ; адрес строки
переместить_имм рдикс, 21 ; длина "Привет мир!\n" = 13 байт
вызов_системы
пропустить_первую:
; Всегда выводим вторую строку
переместить_имм раикс, 1 ; write
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, msg1 ; адрес строки
переместить_имм рдикс, 21 ; длина "Поди прочь!\n" = 13 байт
вызов_системы
; Завершение
переместить_имм раикс, 60 ; exit
переместить_имм рдиай, 0 ; код возврата
вызов_системы
.данные
msg0: .строкауль "Привет мир!\n"
msg1: .строкауль "Поди прочь!\n"

View file

@ -1,53 +0,0 @@
; сложный тест0.квс
; Тест: один переход и один мусорный сегмент
.текст
.глобал _start
_start:
; Инициализация регистра
переместить_имм раикс, 10 ; rax = 10 (не ноль)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'вывести_сообщение', пропуская мертвый_код_1
короткий_переход_если_неоль вывести_сообщение
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
; --- КОД ОШИБКИ (на всякий случай) ---
; Если мы сюда попали, значит переход НЕ сработал (ошибка)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_fail
переместить_имм рдикс, len_msg_fail
вызов_системы
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14
msg_fail: .строкауль "Ошибка: переход не сработал!"
.константа len_msg_fail = 28

View file

@ -1,131 +0,0 @@
; сложный_тест1.квс
; Тест: два перехода и два мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10 (не ноль)
переместить_имм рбикс, 5 ; rbx = 5 (для сравнения)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Нам нужно ПРЫГНУТЬ через мертвый код, если ZF НЕ 1 (т.е. ZF = 0).
; У нас нет инструкции 'короткий_переход_если_не_равно' (jne), но она эквивалентна 'короткий_переход_если_неоль' (jnz) для флага ZF.
; Однако, у нас есть 'короткий_переход_если_равно' (je), который сработает при ZF=1.
; Мы хотим, чтобы переход НЕ срабатывал при ZF=0.
; Поэтому мы используем 'короткий_переход_если_равно' и прыгаем к метке ПОСЛЕ мертвого кода, если ZF=1.
; Но ZF=0, поэтому переход НЕ сработает, и выполнение пойдёт на следующую инструкцию - в мертвый код.
; Нам нужно наоборот: прыгнуть, если ZF=0.
; Используем безусловный переход ПОСЛЕ мертвого кода, чтобы обойти его, если ZF=0.
; Правильная логика:
; cmp ...
; je мимоертвогоода_2 (переход, если ZF=1)
; [мертвый код 2]
; мимоертвогоода_2:
; [продолжение кода]
; Итак, мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0 (т.е. если НЕ равны).
; У нас есть только 'je'. Значит, мы прыгаем МИМО, если ZF=1 (равны).
; Мы НЕ хотим прыгать мимо, если ZF=0.
; Поэтому: cmp, je (прыгнуть мимо, если ZF=1), [мертвый код], [метка мимо], [остальной код]
; Это означает: если ZF=1 -> прыгаем мимо. Если ZF=0 -> выполняем мертвый код.
; Это НЕ то, что нам нужно.
; Нам нужно: если ZF=0 -> прыгаем мимо. Если ZF=1 -> выполняем мертвый код (или идём дальше в него).
; У нас нет jne/jnz. Используем логику: если НЕ (ZF=1), то прыгаем.
; Или: используем безусловный переход после мертвого кода, но перед ним поставим je на метку ПОСЛЕ безусловного перехода.
; Это хитро, но работает:
; cmp rax, rbx
; je метка_после_безусловного (ZF=1 -> прыгаем, обходим безусловный переход)
; короткий_переход мимоертвогоода_2 (ZF=0 -> прыгаем через мертвый код)
; метка_после_безусловного:
; [мертвый код 2 - выполнится, если ZF=1]
; мимоертвогоода_2:
; [продолжение]
; Но мы хотим, чтобы мертвый код НЕ выполнялся, если ZF=0.
; cmp rax, rbx (ZF = 0)
; je метка_после_безусловного (ZF=0, переход НЕ сработает)
; короткий_переход мимоертвогоода_2 (ZF=0, переход СРАБОТАЕТ, прыгаем мимо)
; метка_после_безусловного: [попадаем сюда, если ZF=1]
; [мертвый код 2 - выполнится, если ZF=1]
; мимоертвогоода_2: [попадаем сюда, если ZF=0 (прыгнули) или если ZF=1 (прошли через мертвый код)]
; [продолжение кода]
; Это работает! Если ZF=0 (не равны), мы прыгаем мимо мертвого кода 2.
; Если ZF=1 (равны), мы пропускаем безусловный переход и попадаем в мертвый код 2.
; Нам нужно наоборот: прыгать мимо, если ZF=0 (не равны).
; Значит, если cmp показывает НЕ равны (ZF=0), мы хотим прыгнуть.
; У нас нет jne. Но мы можем использовать логику: if (ZF == 0) jmp; (псевдокод).
; Это можно выразить как: if (ZF == 1) goto skip_jmp; jmp target; skip_jmp: ... (псевдокод).
; Это и есть: cmp ...; je skip_jmp; jmp target; skip_jmp: ...
; Где 'jmp target' - это прыжок, который происходит, если ZF=0 (т.е. когда je НЕ сработал).
; Итак:
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0, переход НЕ сработает -> идем дальше)
; короткий_переход мимоертвогоода_2 (ZF=0, СРАБОТАЕТ -> прыгаем мимо мертвого кода)
; skip_jmp: (ZF=1, мы сюда попали, прыгнув через безусловный переход)
; [мертвый код 2 - выполнится ТОЛЬКО если ZF=1]
; мимоертвогоода_2:
; [продолжение]
; Это как раз то, что нужно!
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0 -> не прыгаем)
; короткий_переход мимоертвогоода_2 (ZF=0 -> прыгаем мимо мертвого кода 2)
; skip_jmp: (ZF=1 -> сюда попали, прыгнув через безусловный переход)
сравнить раикс, рбикс
короткий_переход_если_равно skip_jmp
короткий_переход мимоертвогоода_2
skip_jmp:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13 ; Исправлено на длину "Успех!\n" (10 байт + 1 \n)
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 14
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14

View file

@ -1,93 +0,0 @@
; сложный_тест2.квс
; Тест: три перехода и три мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 3!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 37
; "Мертвый код 2!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 37
; "Мертвый код 1!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 37

View file

@ -1,114 +0,0 @@
; сложный_тест3.квс
; Тест: четыре перехода и четыре мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,137 +0,0 @@
; сложный_тест4.квс
; Тест: пять переходов и пять мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,157 +0,0 @@
; сложный_тест5.квс
; Тест: шесть переходов и шесть мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,180 +0,0 @@
; сложный_тест6.квс
; Тест: семь переходов и семь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,200 +0,0 @@
; сложный_тест7.квс
; Тест: восемь переходов и восемь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,217 +0,0 @@
; сложный_тест8.квс
; Тест: девять переходов и девять мусорных сегментов (включая короткий безусловный)
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; ДЕВЯТЫЙ ПЕРЕХОД: короткий_переход (безусловный)
; Просто прыгаем мимо мертвого кода 9.
короткий_переход мимоертвогоода_9
; --- МЕРТВЫЙ КОД 9 ---
; Этот код НЕ должен выполниться (так как jmp сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_9
переместить_имм рдикс, len_msg_dead_9
вызов_системы
; ---------------------
мимоертвогоода_9:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 9!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_9: .строкауль "Мертвый код 9!"
.константа len_msg_dead_9 = 18
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

52
kvs_build.py Normal file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Главный сборочный скрипт КВС
Последовательно запускает все этапы компиляции
"""
import sys
import subprocess
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Использование: python kvs_build.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
base_name = source_file[:-4]
tokens_file = base_name + ".токены"
ast_file = base_name + ".аст"
pass1_file = base_name + ".проход1"
csv_file = base_name + ".csv"
elf_file = base_name + ".elf"
stages = [
("kvs_lexer.py", [source_file, tokens_file], "Лексический анализ"),
("kvs_parser.py", [tokens_file, ast_file], "Парсинг"),
("kvs_pass1.py", [ast_file, pass1_file], "Первый проход"),
("kvs_pass2.py", [ast_file, pass1_file, csv_file], "Второй проход и генерация CSV"),
("kvs_builder.py", [csv_file, pass1_file, elf_file], "Сборка ELF"),
]
for script, args, description in stages:
print(f"\n=== {description} ===")
cmd = [sys.executable, script] + args
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"ОШИБКА на этапе '{description}':")
print(result.stderr)
sys.exit(1)
print(result.stdout)
print(f"\n=== Компиляция успешно завершена ===")
print(f"Исполняемый файл: {elf_file}")
print(f"Запустить: ./{elf_file}")

165
kvs_builder.py Normal file
View file

@ -0,0 +1,165 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Сборщик ELF из CSV
"""
import sys
import struct
import os
sys.path.insert(0, '.')
from kvs_data import PAGE_SIZE, align_up
def read_csv(input_file, vaddr_text, vaddr_data, text_size, data_size):
"""Загружает байты из CSV используя точные виртуальные адреса"""
text_bytes = bytearray(text_size) # точный размер из pass1
data_bytes = bytearray(data_size)
with open(input_file, 'r', encoding='utf-8') as f:
next(f) # Пропускаем заголовок
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(';')
if len(parts) < 2:
continue
addr_str = parts[0]
byte_str = parts[1]
try:
addr = int(addr_str, 16)
byte_val = int(byte_str, 16)
except ValueError:
continue
# Определяем секцию по адресу
if addr >= vaddr_data:
offset = addr - vaddr_data
if 0 <= offset < data_size:
data_bytes[offset] = byte_val
elif addr >= vaddr_text:
offset = addr - vaddr_text
if 0 <= offset < text_size:
text_bytes[offset] = byte_val
return text_bytes, data_bytes
def read_pass1(input_file):
data = {}
labels = {}
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split(':')
if parts[0] == "PARAM":
key = parts[1]
value = parts[2]
if value.isdigit():
value = int(value)
data[key] = value
elif parts[0] == "LABEL":
label = parts[1]
pos = int(parts[3])
labels[label] = pos
return data, labels
def create_elf(text_bytes, data_bytes, pass1_data, labels, output_file):
vaddr_text = pass1_data.get("vaddr_text", 0x401000)
text_size = pass1_data.get("text_size", len(text_bytes))
# Вычисляем vaddr_data динамически, как в kvs_8
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
offset_text = pass1_data.get("offset_text", 0x1000)
offset_data = align_up(offset_text + len(text_bytes), PAGE_SIZE)
offset_comment = pass1_data.get("offset_comment", align_up(offset_data + len(data_bytes), 1))
shstrtab_offset = pass1_data.get("shstrtab_offset", align_up(offset_comment + 32, 8))
shdr_offset = pass1_data.get("shdr_offset", align_up(shstrtab_offset + 32, 16))
entry_point_name = pass1_data.get("entry_point", "_start")
entry_addr = vaddr_text + labels.get(entry_point_name, 0)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident, 2, 0x3e, 1, entry_addr, elf_header_size, shdr_offset,
0, elf_header_size, ph_size, ph_num, shdr_size, shdr_num, 4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags, p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz, PAGE_SIZE
)
ph0 = phdr(1, 4, 0, 0x400000, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text_bytes), align_up(len(text_bytes), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data_bytes), align_up(len(data_bytes), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text_bytes)] = text_bytes
elf_data[offset_data : offset_data + len(data_bytes)] = data_bytes
elf_data[offset_comment : offset_comment + len(comment_content)] = comment_content
elf_data[shstrtab_offset : shstrtab_offset + len(shstrtab_content)] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text_bytes), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data_bytes), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment_content), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, len(shstrtab_content), 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
with open(output_file, "wb") as f:
f.write(elf_data)
os.chmod(output_file, 0o755)
print(f"Сборщик: ELF-файл создан: {output_file}")
print(f" .text: {len(text_bytes)} байт, адрес: 0x{vaddr_text:x}")
print(f" .data: {len(data_bytes)} байт, адрес: 0x{vaddr_data:x}")
print(f" Расстояние между секциями: 0x{vaddr_data - vaddr_text:x} байт")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Использование: python kvs_builder.py <вход.csv> <вход.проход1> <выход.elf>")
sys.exit(1)
pass1_data, labels = read_pass1(sys.argv[2])
vaddr_text = pass1_data["vaddr_text"]
text_size = pass1_data["text_size"]
data_size = pass1_data["data_size"]
# Вычисляем vaddr_data как в kvs_8
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
text_bytes, data_bytes = read_csv(sys.argv[1], vaddr_text, vaddr_data, text_size, data_size)
create_elf(text_bytes, data_bytes, pass1_data, labels, sys.argv[3])

299
kvs_data.py Normal file
View file

@ -0,0 +1,299 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_data.py
Общие данные для КВС - ассемблера
Регистры, инструкции, константы и вспомогательные функции
"""
import struct
# === ГЛОБАЛЬНЫЕ КОНСТАНТЫ ===
# ELF константы
PAGE_SIZE = 0x1000 # Размер страницы памяти (4096 байт)
elf_base_vaddr = 0x400000 # Базовый виртуальный адрес ELF
text_vaddr_base = 0x401000 # Базовый адрес секции .text
data_vaddr_base = 0x402000 # Базовый адрес секции .data
# === РЕГИСТРЫ (64, 32, 16, 8-битные) ===
# Индексы используются для кодирования в ModR/M и REX префиксах
REGISTERS = {
# 64-битные полные регистры
"раикс": 0, # RAX - аккумулятор (результаты арифметики, возвращаемые значения)
"рсикс": 1, # RCX - счётчик (счётчик циклов, второй аргумент)
"рдикс": 2, # RDX - данные (третий аргумент, остаток от деления)
"рбикс": 3, # RBX - база (базовый регистр, сохраняется между вызовами)
"рсипи": 4, # RSP - указатель стека (вершина стека)
"рбипи": 5, # RBP - указатель базы (кадр стека)
"рсиай": 6, # RSI - индекс источника (второй аргумент, строковые операции)
"рдиай": 7, # RDI - индекс назначения (первый аргумент, строковые операции)
"р8": 8, # R8 (общего назначения, восьмой)
"р9": 9, # R9 (общего назначения, девятый)
"р10": 10, # R10 (общего назначения, десятый)
"р11": 11, # R11 (общего назначения, одиннадцатый)
"р12": 12, # R12 (общего назначения, двенадцатый)
"р13": 13, # R13 (общего назначения, тринадцатый)
"р14": 14, # R14 (общего назначения, четырнадцатый)
"р15": 15, # R15 (общего назначения, пятнадцатый)
# 32-битные младшие части (обнуляют старшие 32 бита при записи)
"еаикс": 0, # EAX - младшие 32 бита RAX
"есикс": 1, # ECX - младшие 32 бита RCX
"едикс": 2, # EDX - младшие 32 бита RDX
"ебикс": 3, # EBX - младшие 32 бита RBX
"есипи": 4, # ESP - младшие 32 бита RSP
"ебипи": 5, # EBP - младшие 32 бита RBP
"есиай": 6, # ESI - младшие 32 бита RSI
"едиай": 7, # EDI - младшие 32 бита RDI
"р": 8, # R8D - младшие 32 бита R8
"р": 9, # R9D - младшие 32 бита R9
"р10д": 10, # R10D - младшие 32 бита R10
"р11д": 11, # R11D - младшие 32 бита R11
"р12д": 12, # R12D - младшие 32 бита R12
"р13д": 13, # R13D - младшие 32 бита R13
"р14д": 14, # R14D - младшие 32 бита R14
"р15д": 15, # R15D - младшие 32 бита R15
# 16-битные младшие части (не обнуляют старшие биты)
"аикс": 0, # AX - младшие 16 бит RAX
"сикс": 1, # CX - младшие 16 бит RCX
"дикс": 2, # DX - младшие 16 бит RDX
"бикс": 3, # BX - младшие 16 бит RBX
"эсп": 4, # SP - младшие 16 бит RSP
"бипи": 5, # BP - младшие 16 бит RBP
"эс": 6, # SI - младшие 16 бит RSI
"ди": 7, # DI - младшие 16 бит RDI
"р": 8, # R8W - младшие 16 бит R8
"р": 9, # R9W - младшие 16 бит R9
"р10в": 10, # R10W - младшие 16 бит R10
"р11в": 11, # R11W - младшие 16 бит R11
"р12в": 12, # R12W - младшие 16 бит R12
"р13в": 13, # R13W - младшие 16 бит R13
"р14в": 14, # R14W - младшие 16 бит R14
"р15в": 15, # R15W - младшие 16 бит R15
# 8-битные младшие части
"ал": 0, # AL - младший байт AX (биты 0-7)
"кл": 1, # CL - младший байт CX (используется в сдвигах)
"дл": 2, # DL - младший байт DX
"бл": 3, # BL - младший байт BX
"спл": 4, # SPL - младший байт SP
"бпл": 5, # BPL - младший байт BP
"сил": 6, # SIL - младший байт SI
"дил": 7, # DIL - младший байт DI
"р8б": 8, # R8B - младший байт R8
"р9б": 9, # R9B - младший байт R9
"р10б": 10, # R10B - младший байт R10
"р11б": 11, # R11B - младший байт R11
"р12б": 12, # R12B - младший байт R12
"р13б": 13, # R13B - младший байт R13
"р14б": 14, # R14B - младший байт R14
"р15б": 15, # R15B - младший байт R15
# 8-битные старшие части (только для первых 4 регистров)
"аш": 0, # AH - старший байт AX (биты 8-15)
"чш": 1, # CH - старший байт CX
"дш": 2, # DH - старший байт DX
"бш": 3, # BH - старший байт BX
}
# === ИНСТРУКЦИИ ===
# Каждая инструкция содержит opcode (последовательность байт) и,
# при необходимости, дополнительные параметры (например, subop для INC/DEC)
INSTRUCTIONS = {
# === Инструкции перемещения данных ===
"переместить_имм": {"opcode": None}, # MOV reg, imm
"переместить": {"opcode": b"\x48\x89"}, # MOV reg, reg
"загрузить": {"opcode": b"\x48\x8B"}, # MOV reg, mem
"сохранить": {"opcode": b"\x48\x89"}, # MOV mem, reg
"загрузить_байт_из_памяти": {"opcode": b"\x8A"}, # MOV reg8, mem8
"сохранить_байт": {"opcode": b"\x88"}, # MOV mem8, reg8
"загрузить_адрес": {"opcode": b"\x48\x8D"}, # LEA reg, mem
"переместить_сулями": {"opcode": b"\x48\x0F\xB6"}, # MOVZX reg, mem8
"переместить_со_знаком": {"opcode": b"\x48\x0F\xBE"}, # MOVSX reg, mem8
"загрузить_байт": {"opcode": None}, # MOV reg8, imm8
# === Инструкции сравнения ===
"сравнить_с": {"opcode": None}, # CMP reg, imm
"сравнить": {"opcode": b"\x48\x39"}, # CMP reg, reg
"сравнить_байт": {"opcode": b"\x38"}, # CMP reg8, reg8
"проверить": {"opcode": b"\x48\x85"}, # TEST reg, reg
# === Арифметические инструкции ===
"прибавить": {"opcode": b"\x48\x01"}, # ADD reg, reg
"прибавить_непосредственно": {"opcode": None}, # ADD reg, imm
"прибавить_байт": {"opcode": b"\x00"}, # ADD reg8, reg8
"вычесть": {"opcode": b"\x48\x29"}, # SUB reg, reg
"вычесть_непосредственно": {"opcode": None}, # SUB reg, imm
"вычесть_байт": {"opcode": b"\x28"}, # SUB reg8, reg8
"увеличить": {"opcode": b"\xFF", "subop": 0}, # INC reg
"уменьшить": {"opcode": b"\xFF", "subop": 1}, # DEC reg
# Умножение и деление
"умножить": {"opcode": b"\xF7", "subop": 4}, # MUL reg/mem
"умножить_знаковое": {"opcode": b"\xF7", "subop": 5}, # IMUL reg/mem
"разделить": {"opcode": b"\xF7", "subop": 6}, # DIV reg/mem
"разделить_знаковое": {"opcode": b"\xF7", "subop": 7}, # IDIV reg/mem
# === Логические инструкции ===
"и": {"opcode": b"\x48\x21"}, # AND reg, reg
"или": {"opcode": b"\x48\x09"}, # OR reg, reg
"исключающее_или": {"opcode": b"\x48\x31"}, # XOR reg, reg
"инвертировать": {"opcode": b"\xF7", "subop": 2}, # NOT reg
"отрицать": {"opcode": b"\xF7", "subop": 3}, # NEG reg
# === Инструкции переходов и вызовов ===
"переход": {"opcode": b"\xE9"}, # JMP rel32
"короткий_переход": {"opcode": b"\xEB"}, # JMP SHORT rel8
# Длинные условные переходы (32-битное смещение)
"переход_если_равно": {"opcode": b"\x0F\x84"}, # JE/JZ
"переход_если_неравно": {"opcode": b"\x0F\x85"}, # JNE/JNZ
"переход_если_меньше": {"opcode": b"\x0F\x8C"}, # JL
"переход_если_больше": {"opcode": b"\x0F\x8F"}, # JG
"переход_если_меньше_или_равно": {"opcode": b"\x0F\x8E"}, # JLE
"переход_если_больше_или_равно": {"opcode": b"\x0F\x8D"}, # JGE
"переход_если_перенос": {"opcode": b"\x0F\x82"}, # JC
"переход_если_нет_переноса": {"opcode": b"\x0F\x83"}, # JNC
"переход_если_ноль": {"opcode": b"\x0F\x84"}, # JZ
"переход_если_неоль": {"opcode": b"\x0F\x85"}, # JNZ
# Короткие условные переходы (8-битное смещение)
"короткий_переход_если_равно": {"opcode": b"\x74"}, # JE SHORT
"короткий_переход_если_неравно": {"opcode": b"\x75"}, # JNE SHORT
"короткий_переход_если_меньше": {"opcode": b"\x7C"}, # JL SHORT
"короткий_переход_если_больше": {"opcode": b"\x7F"}, # JG SHORT
"короткий_переход_если_меньше_или_равно": {"opcode": b"\x7E"}, # JLE SHORT
"короткий_переход_если_больше_или_равно": {"opcode": b"\x7D"}, # JGE SHORT
"короткий_переход_если_перенос": {"opcode": b"\x72"}, # JC SHORT
"короткий_переход_если_нет_переноса": {"opcode": b"\x73"}, # JNC SHORT
"короткий_переход_если_ноль": {"opcode": b"\x74"}, # JZ SHORT
"короткий_переход_если_неоль": {"opcode": b"\x75"}, # JNZ SHORT
"вызвать": {"opcode": b"\xE8"}, # CALL rel32
"вернуться": {"opcode": b"\xC3"}, # RET
# === Стековые инструкции ===
"втолкнуть": {"opcode": b"\x50", "reg_offset": 0}, # PUSH reg
"вытолкнуть": {"opcode": b"\x58", "reg_offset": 0}, # POP reg
"втолкнуть_непосредственно": {"opcode": b"\x68"}, # PUSH imm32
"втолкнуть_байт": {"opcode": b"\x6A"}, # PUSH imm8
# === Инструкции работы с битами ===
"сдвиг_влево": {"opcode": b"\x48\xC1\xE0", "type": "shift"}, # SHL reg, imm
"сдвиг_вправо": {"opcode": b"\x48\xC1\xE8", "type": "shift"}, # SHR reg, imm
"сдвиг_арифметический_вправо": {"opcode": b"\x48\xC1\xF8", "type": "shift"}, # SAR reg, imm
"вращать_влево": {"opcode": b"\x48\xC1\xC0", "type": "rotate"}, # ROL reg, imm
"вращать_вправо": {"opcode": b"\x48\xC1\xC8", "type": "rotate"}, # ROR reg, imm
# === Инструкции управления потоком ===
"цикл": {"opcode": b"\xE2"}, # LOOP rel8
"обменять": {"opcode": b"\x48\x87"}, # XCHG reg, reg
# === Системные инструкции ===
"вызов_системы": {"opcode": b"\x0F\x05"}, # SYSCALL
"прервать": {"opcode": b"\xCD"}, # INT imm8
"войти_в_систему": {"opcode": b"\x0F\x34"}, # SYSENTER
"остановить": {"opcode": b"\xF4"}, # HLT
# === Отладочные инструкции ===
"нет_операции": {"opcode": b"\x90"}, # NOP
"отладка": {"opcode": b"\xCC"}, # INT3
# === Строковые инструкции ===
"переместить_байт": {"opcode": b"\xA4"}, # MOVSB
"переместить_слово": {"opcode": b"\xA5"}, # MOVSW
"сравнить_байты": {"opcode": b"\xA6"}, # CMPSB
"сканировать_байт": {"opcode": b"\xAE"}, # SCASB
# === Инструкции работы с флагами ===
"установить_перенос": {"opcode": b"\xF9"}, # STC
"сбросить_перенос": {"opcode": b"\xF8"}, # CLC
"установить_направление": {"opcode": b"\xFD"}, # STD
"сбросить_направление": {"opcode": b"\xFC"}, # CLD
"втолкнуть_флаги": {"opcode": b"\x9C"}, # PUSHF
"вытолкнуть_флаги": {"opcode": b"\x9D"}, # POPF
# === Инструкции ввода-вывода ===
"ввод_байта": {"opcode": b"\xE4"}, # IN imm8
"вывод_байта": {"opcode": b"\xE6"}, # OUT imm8
# === Инструкции идентификации процессора ===
"идентифицировать_процессор": {"opcode": b"\x0F\xA2"}, # CPUID
"прочитать_счётчик": {"opcode": b"\x0F\x31"}, # RDTSC
}
# === ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ===
def get_reg_info(reg_name):
"""
Получает информацию о регистре по его имени.
Возвращает словарь:
- size: размер в битах (8, 16, 32, 64)
- index: индекс регистра (0-15)
- high8: True для старших байтов AH, CH, DH, BH
Аргументы:
reg_name (str): имя регистра (например, "раикс", "еаикс", "ал", "аш")
Возвращает:
dict: информация о регистре
Пример:
>>> get_reg_info("раикс")
{'size': 64, 'index': 0}
>>> get_reg_info("ал")
{'size': 8, 'index': 0, 'high8': False}
>>> get_reg_info("аш")
{'size': 8, 'index': 0, 'high8': True}
"""
# Старшие 8-битные регистры (AH, CH, DH, BH)
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
# Младшие 8-битные регистры (AL, CL, DL, BL, SPL, BPL, SIL, DIL, R8B-R15B)
elif (reg_name.endswith(("б", "л")) or
reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]):
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
# 16-битные регистры (заканчиваются на "в" или имеют специальные имена)
elif (reg_name.endswith("в") or
reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]):
return {"size": 16, "index": REGISTERS[reg_name]}
# 32-битные регистры (заканчиваются на "д" или имеют специальные имена)
elif (reg_name.endswith("д") or
reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]):
return {"size": 32, "index": REGISTERS[reg_name]}
# 64-битные регистры (все остальные)
else:
if reg_name not in REGISTERS:
raise ValueError(f"Неизвестный регистр: {reg_name}")
return {"size": 64, "index": REGISTERS[reg_name]}
def align_up(x, align):
"""
Выравнивание числа вверх до указанной границы.
Аргументы:
x (int): исходное число
align (int): граница выравнивания (степень двойки)
Возвращает:
int: выровненное вверх значение
Пример:
>>> align_up(100, 16)
112
>>> align_up(128, 16)
128
"""
return (x + align - 1) & ~(align - 1)

128
kvs_lexer.py Normal file
View file

@ -0,0 +1,128 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Лексер КВС
Принимает: исходный .квс файл
Выдаёт: текстовый файл с токенами (по одному токену на строку)
Формат вывода: ТИП:ЗНАЧЕНИЕ
"""
import sys
def error_unterminated_string(line_num):
return f"Незакрытая кавычка в строке на строке {line_num}"
def tokenize_line(line, line_num):
"""Разбирает строку в токены"""
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string(line_num))
i += 1
tokens.append(('STRING', s))
continue
if ch == ',':
tokens.append(('COMMA', ','))
i += 1
continue
if ch == ':':
tokens.append(('COLON', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('WORD', word))
i = j
return tokens
def lex_source(source_text):
"""Лексирует весь исходный текст"""
lines = source_text.split('\n')
all_tokens = []
for line_num, line in enumerate(lines, start=1):
try:
tokens = tokenize_line(line, line_num)
if tokens:
all_tokens.append(('NEWLINE', str(line_num)))
for tok_type, tok_value in tokens:
all_tokens.append((tok_type, tok_value))
except ValueError as e:
print(f"Ошибка лексера: {e}", file=sys.stderr)
sys.exit(1)
return all_tokens
def write_tokens(tokens, output_file):
"""Записывает токены в файл"""
with open(output_file, 'w', encoding='utf-8') as f:
for tok_type, tok_value in tokens:
f.write(f"{tok_type}:{tok_value}\n")
def read_tokens(input_file):
"""Читает токены из файла"""
tokens = []
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
colon_pos = line.find(':')
if colon_pos == -1:
continue
tok_type = line[:colon_pos]
tok_value = line[colon_pos + 1:]
tokens.append((tok_type, tok_value))
return tokens
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Использование: python kvs_lexer.py <вход.квс> <выход.токены>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
try:
with open(input_file, 'r', encoding='utf-8') as f:
source_text = f.read()
except FileNotFoundError:
print(f"Ошибка: файл '{input_file}' не найден.", file=sys.stderr)
sys.exit(1)
tokens = lex_source(source_text)
write_tokens(tokens, output_file)
print(f"Лексер: {len(tokens)} токенов записано в {output_file}")

168
kvs_parser.py Normal file
View file

@ -0,0 +1,168 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Парсер КВС
Принимает: файл токенов от лексера
Выдаёт: текстовый файл с AST-структурой
"""
import sys
sys.path.insert(0, '.')
from kvs_data import REGISTERS, INSTRUCTIONS
def read_tokens(input_file):
"""Читает токены из файла"""
tokens = []
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
colon_pos = line.find(':')
if colon_pos == -1:
continue
tok_type = line[:colon_pos]
tok_value = line[colon_pos + 1:]
tokens.append((tok_type, tok_value))
return tokens
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.pos = 0
self.current_section = ".text"
self.parsed_lines = []
def peek(self):
if self.pos < len(self.tokens):
return self.tokens[self.pos]
return None
def next_token(self):
tok = self.peek()
if tok:
self.pos += 1
return tok
def expect(self, expected_type):
tok = self.next_token()
if tok is None or tok[0] != expected_type:
raise ValueError(f"Ожидался {expected_type}, получено {tok}")
return tok
def parse_line(self):
tok = self.peek()
if tok is None:
return False
if tok[0] == 'NEWLINE':
self.next_token()
tok = self.peek()
if tok is None:
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
self.parsed_lines.append(f"LABEL:{label_name}:{self.current_section}")
next_tok = self.peek()
if next_tok and next_tok[0] != 'NEWLINE':
return self.parse_instruction_or_directive()
return True
return self.parse_instruction_or_directive()
def parse_instruction_or_directive(self):
tok = self.peek()
if tok is None or tok[0] == 'NEWLINE':
return True
if tok[0] != 'WORD':
raise ValueError(f"Ожидалось слово, получено: {tok}")
word = self.next_token()[1]
if word.startswith('.'):
self.parse_directive(word)
else:
self.parse_instruction(word)
return True
def parse_directive(self, word):
if word == '.текст':
self.current_section = ".text"
self.parsed_lines.append(f"DIRECTIVE:{word}")
elif word == '.данные':
self.current_section = ".data"
self.parsed_lines.append(f"DIRECTIVE:{word}")
elif word == '.глобал':
label = self.expect('WORD')[1]
self.parsed_lines.append(f"DIRECTIVE:{word}:{label}")
elif word in ('.строкауль', '.строка'):
string_tok = self.expect('STRING')
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')
self.parsed_lines.append(f"DIRECTIVE:{word}:{name}:{value_tok[1]}")
elif word == '.байт':
bytes_list = []
while True:
tok = self.peek()
if tok is None or tok[0] == 'NEWLINE' or tok[0] == 'COMMA':
if tok and tok[0] == 'COMMA':
self.next_token()
else:
break
else:
bytes_list.append(self.next_token()[1])
self.parsed_lines.append(f"DIRECTIVE:{word}:{self.current_section}:" + ",".join(bytes_list))
else:
raise ValueError(f"Неизвестная директива: {word}")
def parse_instruction(self, mnemonic):
if mnemonic not in INSTRUCTIONS:
raise ValueError(f"Неизвестная инструкция: {mnemonic}")
operands = []
while True:
tok = self.peek()
if tok is None or tok[0] == 'NEWLINE':
break
if tok[0] == 'COMMA':
self.next_token()
continue
operands.append(self.next_token()[1])
self.parsed_lines.append(f"INSTR:{mnemonic}:{self.current_section}:" + ",".join(operands))
def parse_all(self):
while self.pos < len(self.tokens):
self.parse_line()
return self.parsed_lines
def write_ast(ast_lines, output_file):
with open(output_file, 'w', encoding='utf-8') as f:
for line in ast_lines:
f.write(line + '\n')
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Использование: python kvs_parser.py <вход.токены> <выход.аст>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
tokens = read_tokens(input_file)
parser = Parser(tokens)
ast_lines = parser.parse_all()
write_ast(ast_lines, output_file)
print(f"Парсер: {len(ast_lines)} строк AST записано в {output_file}")

191
kvs_pass1.py Normal file
View file

@ -0,0 +1,191 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Первый проход КВС
Вычисляет размеры секций и адреса меток
"""
import sys
sys.path.insert(0, '.')
from kvs_data import PAGE_SIZE, text_vaddr_base, data_vaddr_base, align_up
def unescape_string(s):
"""Преобразует escape-последовательности в реальные символы"""
result = []
i = 0
while i < len(s):
if s[i] == '\\' and i + 1 < len(s):
if s[i + 1] == 'n':
result.append('\n')
elif s[i + 1] == 't':
result.append('\t')
elif s[i + 1] == 'r':
result.append('\r')
else:
result.append(s[i])
result.append(s[i + 1])
i += 2
else:
result.append(s[i])
i += 1
return ''.join(result)
def read_ast(input_file):
ast_lines = []
with open(input_file, 'r', encoding='utf-8') as f:
for line in f:
ast_lines.append(line.strip())
return ast_lines
class Pass1:
def __init__(self):
self.labels = {}
self.label_sections = {}
self.symbols = {}
self.position = {".text": 0, ".data": 0}
self.current_section = ".text"
self.entry_point = "_start"
def process_line(self, line):
parts = line.split(':')
line_type = parts[0]
if line_type == "DIRECTIVE":
directive = parts[1]
if directive == '.текст':
self.current_section = ".text"
elif directive == '.данные':
self.current_section = ".data"
elif directive == '.глобал':
self.entry_point = parts[2]
elif directive in ('.строкауль', '.строка'):
s = parts[3] if len(parts) > 3 else ""
real_s = unescape_string(s)
bstring = real_s.encode('utf-8')
add_null = (directive == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
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)
else:
self.symbols[name] = 0
elif directive == '.байт':
sec = parts[2]
if len(parts) > 3 and parts[3]:
num_bytes = len(parts[3].split(','))
self.position[sec] += num_bytes
elif line_type == "LABEL":
label_name = parts[1]
sec = parts[2]
self.labels[label_name] = self.position[sec]
self.label_sections[label_name] = sec
elif line_type == "INSTR":
mnemonic = parts[1]
sec = parts[2]
size = self.estimate_size(mnemonic)
self.position[sec] += size
def estimate_size(self, mnemonic):
size_map = {
"переместить_имм": 10,
"сравнить_с": 7,
"переход": 5,
"переход_если_равно": 6,
"переход_если_неравно": 6,
"переход_если_ноль": 6,
"переход_если_неоль": 6,
"переход_если_меньше": 6,
"переход_если_больше": 6,
"переход_если_меньше_или_равно": 6,
"переход_если_больше_или_равно": 6,
"переход_если_перенос": 6,
"переход_если_нет_переноса": 6,
"вызов_системы": 2,
"нет_операции": 1,
"короткий_переход": 2,
"короткий_переход_если_равно": 2,
"короткий_переход_если_неравно": 2,
"короткий_переход_если_меньше": 2,
"короткий_переход_если_больше": 2,
"короткий_переход_если_меньше_или_равно": 2,
"короткий_переход_если_больше_или_равно": 2,
"короткий_переход_если_перенос": 2,
"короткий_переход_если_нет_переноса": 2,
"короткий_переход_если_ноль": 2,
"короткий_переход_если_неоль": 2,
"сравнить": 3,
"проверить": 3,
"вычесть": 3,
"прибавить": 3,
"увеличить": 3,
"уменьшить": 3,
}
return size_map.get(mnemonic, 3)
def calculate_layout(self):
text_size = self.position[".text"]
data_size = self.position[".data"]
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE)
comment_size = len("Сборщик КВС".encode('utf-8')) + 1
offset_comment = align_up(offset_data + data_size, 1)
shstrtab_size = len(b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00")
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
return {
"text_size": text_size,
"data_size": data_size,
"offset_text": offset_text,
"offset_data": offset_data,
"vaddr_text": vaddr_text,
"vaddr_data": vaddr_data,
"offset_comment": offset_comment,
"comment_size": comment_size,
"shstrtab_offset": shstrtab_offset,
"shstrtab_size": shstrtab_size,
"shdr_offset": shdr_offset,
"entry_point": self.entry_point,
}
def write_pass1(pass1_data, labels, label_sections, symbols, output_file):
with open(output_file, 'w', encoding='utf-8') as f:
for key, value in pass1_data.items():
f.write(f"PARAM:{key}:{value}\n")
for label, pos in labels.items():
sec = label_sections.get(label, ".text")
f.write(f"LABEL:{label}:{sec}:{pos}\n")
for name, value in symbols.items():
f.write(f"SYMBOL:{name}:{value}\n")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Использование: python kvs_pass1.py <вход.аст> <выход.проход1>")
sys.exit(1)
ast_lines = read_ast(sys.argv[1])
pass1 = Pass1()
for line in ast_lines:
pass1.process_line(line)
layout = pass1.calculate_layout()
write_pass1(layout, pass1.labels, pass1.label_sections, pass1.symbols, sys.argv[2])
print(f"Проход 1: text_size={layout['text_size']}, data_size={layout['data_size']}")

1261
kvs_pass2.py Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,149 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
from datetime import datetime
def binary_to_disasm(input_file, output_file=None, skip_zeros=True):
"""Конвертирует бинарный файл в дизассемблированный код с помощью ndisasm."""
try:
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_disasm.txt"
# Вызываем ndisasm для дизассемблирования
result = subprocess.run(
['ndisasm', '-b64', input_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Проверяем наличие ошибок от ndisasm
if result.returncode != 0:
error_msg = result.stderr.strip()
return False, f"Ошибка ndisasm: {error_msg}"
# Получаем текущую дату и время
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Фильтруем строки в зависимости от настройки
lines = result.stdout.splitlines()
if skip_zeros:
filtered_lines = [line for line in lines if 'add [rax],al' not in line]
filter_status = "Нули (add [rax],al) пропущены"
else:
filtered_lines = lines
filter_status = "Нули (add [rax],al) не пропущены"
# Записываем отфильтрованные строки в файл с заголовком
with open(output_file, 'w', encoding='utf-8') as out_file:
# Добавляем заголовок с информацией
out_file.write("=" * 60 + "\n")
out_file.write(f"Дизассемблированный код сгенерирован с помощью ndisasm\n")
out_file.write(f"Исходный файл: {os.path.basename(input_file)}\n")
out_file.write(f"Дата создания: {current_time}\n")
out_file.write(f"Статус фильтра: {filter_status}\n")
out_file.write("=" * 60 + "\n\n")
for line in filtered_lines:
out_file.write(line + '\n')
return True, output_file
except FileNotFoundError:
return False, "ndisasm не найден. Убедитесь, что NASM установлен и доступен в PATH"
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.bin *.elf *.o"), ("Все файлы", "*.*"))
)
if not input_file:
return
# Получаем состояние галочки
skip_zeros = skip_zeros_var.get()
success, result = binary_to_disasm(input_file, skip_zeros=skip_zeros)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно дизассемблирован!\n"
f"Пропуск нулей: {'ВКЛ' if skip_zeros else 'ВЫКЛ'}\n"
f"Результат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось дизассемблировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
global skip_zeros_var
root = tk.Tk()
root.title("Дизассемблер бинарных файлов")
root.geometry("500x250")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Переменная для хранения состояния галочки
skip_zeros_var = tk.BooleanVar(value=True) # По умолчанию включено
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Дизассемблер бинарных файлов (ndisasm)",
font=('Arial', 14),
pady=20
)
label.pack()
# Фрейм для галочки
checkbox_frame = tk.Frame(root)
checkbox_frame.pack(pady=10)
# Галочка для пропуска нулей
skip_zeros_check = tk.Checkbutton(
checkbox_frame,
text="Пропускать нули (add [rax],al)",
variable=skip_zeros_var,
font=('Arial', 10)
)
skip_zeros_check.pack()
# Кнопка конвертации
btn_convert = tk.Button(
root,
text="Выбрать файл и дизассемблировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack(pady=10)
# Кнопка выхода
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=10)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,93 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
def binary_to_hex(input_file, output_file=None):
"""Конвертирует бинарный файл в hex-текст"""
try:
with open(input_file, 'rb') as bin_file:
binary_data = bin_file.read()
hex_data = binary_data.hex()
# Форматируем с пробелами между байтами
formatted_hex = ' '.join([hex_data[i:i+2] for i in range(0, len(hex_data), 2)])
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_hex.txt"
with open(output_file, 'w') as hex_file:
hex_file.write(formatted_hex)
return True, output_file
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.elf"), ("Все файлы", "*.*"))
)
if not input_file:
return
success, result = binary_to_hex(input_file)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно конвертирован!\nРезультат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось конвертировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
root = tk.Tk()
root.title("Бинарный в HEX конвертер")
root.geometry("400x200")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Конвертер бинарных файлов в HEX-текст",
font=('Arial', 14),
pady=20
)
label.pack()
btn_convert = tk.Button(
root,
text="Выбрать файл и конвертировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack()
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=20)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,126 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
def binary_to_hex(input_file, output_file=None, skip_zeros=False):
"""Конвертирует бинарный файл в hex-текст. Пропускает нули, если skip_zeros=True."""
try:
with open(input_file, 'rb') as bin_file:
binary_data = bin_file.read()
total_bytes = len(binary_data)
if skip_zeros:
filtered_data = bytes(b for b in binary_data if b != 0)
hex_data = filtered_data.hex()
displayed_bytes = len(filtered_data)
else:
hex_data = binary_data.hex()
displayed_bytes = total_bytes
# Форматируем с пробелами между байтами
formatted_hex = ' '.join([hex_data[i:i+2] for i in range(0, len(hex_data), 2)])
# Добавляем заголовок в отчёт
report = []
report.append(f"Исходный файл: {os.path.basename(input_file)}")
report.append(f"Общий размер файла: {total_bytes} байт")
if skip_zeros:
report.append("Режим: пропускать нулевые байты (0x00)")
report.append(f"Выведено ненулевых байт: {displayed_bytes}")
else:
report.append("Режим: показывать все байты (включая 0x00)")
report.append("")
report.append(formatted_hex)
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
suffix = "_hex_skip0.txt" if skip_zeros else "_hex.txt"
output_file = f"{base_name}{suffix}"
with open(output_file, 'w') as hex_file:
hex_file.write('\n'.join(report))
return True, output_file
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует с учётом галочки."""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.elf *.bin *.out"), ("Все файлы", "*.*"))
)
if not input_file:
return
skip_zeros = skip_var.get()
success, result = binary_to_hex(input_file, skip_zeros=skip_zeros)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно конвертирован!\nРезультат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось конвертировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
root = tk.Tk()
root.title("Бинарный в HEX конвертер")
root.geometry("450x220")
root.eval('tk::PlaceWindow . center')
label = tk.Label(
root,
text="Конвертер бинарных файлов в HEX-текст",
font=('Arial', 14),
pady=10
)
label.pack()
global skip_var
skip_var = tk.BooleanVar()
chk_skip = tk.Checkbutton(
root,
text="Пропускать нулевые байты (0x00)",
variable=skip_var,
font=('Arial', 10)
)
chk_skip.pack(pady=5)
btn_convert = tk.Button(
root,
text="Выбрать файл и конвертировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack(pady=10)
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=10)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,113 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
def binary_to_disasm(input_file, output_file=None):
"""Конвертирует бинарный файл в дизассемблированный код с помощью objdump, исключая строки с 'add [rax],al'."""
try:
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_disasm.txt"
# Вызываем objdump для дизассемблирования
result = subprocess.run(
['objdump', '-d', '-M', 'intel', input_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Проверяем наличие ошибок от objdump
if result.returncode != 0:
error_msg = result.stderr.strip()
return False, f"Ошибка objdump: {error_msg}"
# Фильтруем строки: исключаем те, которые содержат 'add [rax],al'
# В objdump инструкция может выглядеть как 'add BYTE PTR [rax],al'
lines = result.stdout.splitlines()
# Фильтруем строки с разными вариантами записи этой инструкции
filtered_lines = []
for line in lines:
if 'add [rax],al' not in line and 'add BYTE PTR [rax],al' not in line:
filtered_lines.append(line)
# Записываем отфильтрованные строки в файл
with open(output_file, 'w', encoding='utf-8') as out_file:
for line in filtered_lines:
out_file.write(line + '\n')
return True, output_file
except FileNotFoundError:
return False, "objdump не найден. Убедитесь, что binutils установлен и доступен в PATH"
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.bin *.elf *.o"), ("Все файлы", "*.*"))
)
if not input_file:
return
success, result = binary_to_disasm(input_file)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно дизассемблирован!\nНули (add [rax],al) удалены.\nРезультат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось дизассемблировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
root = tk.Tk()
root.title("Дизассемблер бинарных файлов (objdump)")
root.geometry("450x200")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Дизассемблер бинарных файлов (objdump)",
font=('Arial', 14),
pady=20
)
label.pack()
btn_convert = tk.Button(
root,
text="Выбрать файл и дизассемблировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack()
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=20)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,16 +0,0 @@
section .text
global _start
_start:
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop

View file

@ -1,16 +0,0 @@
.текст
.глобал _start
_start:
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции
нет_операции

View file

@ -1,55 +0,0 @@
nasm -f elf64 test1.asm && ld test1.o -o test1
Уровень 1: Простейшие (1 байт, без операндов)
нет_операции (nop) - 0x90
вернуться (ret) - 0xC3
вызов_системы (syscall) - 0x0F 0x05
остановить (hlt) - 0xF4
установить_перенос (stc) - 0xF9
сбросить_перенос (clc) - 0xF8
отладка (int3) - 0xCC
Уровень 2: Простые с одним регистром (1-2 байта)
втолкнуть (push reg) - 0x50+r
вытолкнуть (pop reg) - 0x58+r
увеличить (inc reg) - 0x40+r (32-bit), 0x48 0xFF /0 (64-bit)
уменьшить (dec reg) - 0x48+r (32-bit), 0x48 0xFF /1 (64-bit)
Уровень 3: Регистр-регистр (2-3 байта)
переместить (mov reg, reg) - 0x48 0x89 (modrm)
прибавить (add reg, reg) - 0x48 0x01 (modrm)
вычесть (sub reg, reg) - 0x48 0x29 (modrm)
сравнить (cmp reg, reg) - 0x48 0x39 (modrm)
Уровень 4: Регистр-непосредственное значение (3-10 байт)
переместить_имм (mov reg, imm) - зависит от размера
прибавить_непосредственно (add reg, imm)
сравнить_с (cmp reg, imm)
Уровень 5: Работа с памятью (3-7 байт)
загрузить (mov reg, [reg]) - 0x48 0x8B (modrm)
сохранить (mov [reg], reg) - 0x48 0x89 (modrm)
втолкнуть_из_памяти (push mem)
вытолкнуть_в_память (pop mem)
Уровень 6: Относительные переходы (5 байт)
переход (jmp rel32) - 0xE9
вызвать (call rel32) - 0xE8
переход_если_равно (je rel32) - 0x0F 0x84
Уровень 7: Сложная адресация (6-7 байт)
загрузить_адрес (lea reg, [rip+disp]) - 0x48 0x8D (modrm+sib+disp)
Уровень 8: 8-битные операции (особые случаи)
загрузить_байт (mov reg8, imm8)
прибавить_байт (add reg8, reg8)
работа со старшими байтами (AH, CH, etc.)
Уровень 9: Специальные/сложные
умножить (mul) - 0xF7 /4
разделить (div) - 0xF7 /6
строковые операции
ввод-вывод

View file

@ -1,243 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Скрипт генерирует КОРРЕКТНЫЙ 64-битный исполняемый ELF-файл для Linux (x86-64),
# содержащий 12 инструкций NOP (0x90) в секции .text и один байт (0x0A) в секции .data.
# Файл полностью соответствует ELF-спецификации.
import struct
def align_up(offset, alignment):
"""Выравнивание offset вверх до alignment"""
return (offset + alignment - 1) & ~(alignment - 1)
def pack_elf():
"""
Создаёт байтовое представление валидного ELF-исполняемого файла.
Возвращает bytes содержимое файла.
"""
# Размер страницы памяти в Linux на x86-64 (4096 байт = 0x1000)
PAGE_SIZE = 0x1000
# Адрес точки входа программы — начало секции .text
ENTRY = 0x401000
# === СОДЕРЖИМОЕ СЕКЦИЙ ===
# Машинный код: 12 инструкций NOP (код операции 0x90)
text_code = b'\x90' * 12
# Данные: один байт — символ новой строки (0x0A = '\n')
data_content = b'\x0a'
# Таблица строк имён секций (обязательна для Section Headers)
# Формат: нуль-терминированные строки подряд.
# Порядок: "", ".text", ".data", ".shstrtab"
shstrtab_content = b'\x00.text\x00.data\x00.shstrtab\x00'
# === СМЕЩЕНИЯ В ФАЙЛЕ (в байтах) ===
# Program Headers (заголовки сегментов) начинаются сразу после ELF-заголовка
phdr_start = 64 # размер ELF-заголовка
# Секция .text начинается с границы страницы 0x1000 (4096)
text_offset = 0x1000
# Секция .data начинается со следующей страницы — 0x2000 (8192)
data_offset = 0x2000
# Таблица имён секций (.shstrtab) размещается сразу после .data с ВЫРАВНИВАНИЕМ
shstrtab_offset = align_up(data_offset + len(data_content), 8) # 0x2008 вместо 0x2001
# Заголовки секций (Section Headers) размещаются сразу после .shstrtab с выравниванием
shdr_offset = align_up(shstrtab_offset + len(shstrtab_content), 16) # 0x2020
# === РАЗМЕРЫ КОМПОНЕНТОВ ===
text_size = len(text_code) # 12 байт
data_size = len(data_content) # 1 байт
shstrtab_size = len(shstrtab_content) # 17 байт
# Размер Program Headers: 3 заголовка × 56 байт
phdr_size = 3 * 56 # 168 байт
# Размер одного заголовка секции — 64 байта для 64-битного ELF
# Всего 4 секции: NULL, .text, .data, .shstrtab
shdr_size = 4 * 64 # 256 байт
# Общий размер файла: от начала до конца последнего заголовка секции
file_size = shdr_offset + shdr_size # 0x2020 + 256 = 0x2120 (8480 байт)
# Создаём байтовый массив нужного размера, заполненный нулями
elf_data = bytearray(file_size)
# === 1. ELF-ЗАГОЛОВОК (64 байта) ===
# e_ident — магическое число и базовые параметры формата (ИСПРАВЛЕНО)
# Структура e_ident: 16 байт
e_ident = (
b'\x7fELF' # ELF Magic (4 байта)
b'\x02' # 64-bit (1 байт)
b'\x01' # Little-endian (1 байт)
b'\x01' # ELF Version (1 байт)
b'\x00' # OS ABI: System V (1 байт)
b'\x00' # ABI Version (1 байт)
b'\x00\x00\x00\x00\x00\x00\x00' # Padding (7 байт) - ИТОГО 16 байт
)
assert len(e_ident) == 16, f"e_ident должен быть 16 байт, а не {len(e_ident)}"
# Упаковываем ELF-заголовок в бинарный формат
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident, # e_ident[16] - 16 байт
2, # e_type = ET_EXEC (исполняемый файл)
0x3e, # e_machine = EM_X86_64 (AMD x86-64)
1, # e_version = 1
ENTRY, # e_entry — адрес точки входа
phdr_start, # e_phoff — смещение Program Headers в файле
shdr_offset, # e_shoff — смещение Section Headers в файле
0, # e_flags — флаги архитектуры (0 для x86-64)
64, # e_ehsize — размер ELF-заголовка (64 байта)
56, # e_phentsize — размер одного Program Header (56 байт)
3, # e_phnum — количество Program Headers
64, # e_shentsize — размер одного Section Header (64 байта)
4, # e_shnum — количество Section Headers
3 # e_shstrndx — индекс секции с именами (.shstrtab)
)
# Записываем ELF-заголовок в начало файла
elf_data[0:64] = elf_header
# === 2. PROGRAM HEADERS (заголовки сегментов) ===
def make_phdr(p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align):
"""
Создаёт бинарное представление заголовка сегмента.
"""
# Проверки корректности
assert p_offset % p_align == 0, f"Segment offset {p_offset:#x} not aligned to {p_align:#x}"
assert p_vaddr % p_align == 0, f"Virtual address {p_vaddr:#x} not aligned to {p_align:#x}"
assert p_filesz <= p_memsz, f"File size {p_filesz:#x} > memory size {p_memsz:#x}"
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_align
)
# Сегмент 0: заголовки ELF и Program Headers (чтение и выполнение для корректности)
ph0 = make_phdr(
p_type=1, # PT_LOAD
p_offset=0x0000, # смещение в файле
p_vaddr=0x400000, # адрес в памяти
p_paddr=0x400000,
p_filesz=phdr_start + phdr_size, # 64 + 168 = 232 (0xe8) байт
p_memsz=PAGE_SIZE, # В памяти занимает целую страницу
p_flags=4, # PF_R — только чтение
p_align=PAGE_SIZE
)
# Сегмент 1: исполняемый код (.text)
ph1 = make_phdr(
p_type=1,
p_offset=text_offset, # 0x1000
p_vaddr=0x401000, # адрес точки входа
p_paddr=0x401000,
p_filesz=text_size, # 12 байт
p_memsz=text_size,
p_flags=5, # PF_R | PF_X = 4 + 1
p_align=PAGE_SIZE
)
# Сегмент 2: данные (.data)
ph2 = make_phdr(
p_type=1,
p_offset=data_offset, # 0x2000
p_vaddr=0x402000,
p_paddr=0x402000,
p_filesz=data_size, # 1 байт
p_memsz=data_size,
p_flags=6, # PF_R | PF_W = 4 + 2
p_align=PAGE_SIZE
)
# Записываем все Program Headers в файл по смещению 64
program_headers = ph0 + ph1 + ph2
elf_data[phdr_start : phdr_start + len(program_headers)] = program_headers
# === 3. СОДЕРЖИМОЕ СЕКЦИЙ ===
# Записываем машинный код (.text) по смещению 0x1000
elf_data[text_offset : text_offset + text_size] = text_code
# Записываем данные (.data) по смещению 0x2000
elf_data[data_offset : data_offset + data_size] = data_content
# Записываем таблицу имён секций (.shstrtab) по ВЫРОВНЕННОМУ смещению
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. SECTION HEADERS (заголовки секций) ===
def make_shdr(name_idx, sh_type, flags, addr, offset, size, link=0, info=0, addralign=1, entsize=0):
"""
Создаёт бинарное представление заголовка секции.
"""
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size, link, info, addralign, entsize
)
# Индексы имён в .shstrtab:
# Строка: "\x00.text\x00.data\x00.shstrtab\x00"
# Индексы: 0 1 7 13
# Секция 0: NULL (обязательна, пустая)
sh0 = make_shdr(0, 0, 0, 0, 0, 0)
# Секция 1: .text — исполняемый код
sh1 = make_shdr(
name_idx=1, # ".text" начинается с байта 1
sh_type=1, # SHT_PROGBITS — обычная секция с данными
flags=6, # SHF_ALLOC | SHF_EXECINSTR = 2 + 4
addr=0x401000, # адрес в памяти
offset=text_offset, # смещение в файле
size=text_size,
addralign=16 # Выравнивание 16 байт для кода
)
# Секция 2: .data — инициализированные данные
sh2 = make_shdr(
name_idx=7, # ".data" начинается с байта 7
sh_type=1,
flags=3, # SHF_ALLOC | SHF_WRITE = 2 + 1
addr=0x402000,
offset=data_offset,
size=data_size,
addralign=4 # Выравнивание 4 байта для данных
)
# Секция 3: .shstrtab — таблица имён секций
sh3 = make_shdr(
name_idx=13, # ".shstrtab" начинается с байта 13
sh_type=3, # SHT_STRTAB — таблица строк
flags=0,
addr=0,
offset=shstrtab_offset,
size=shstrtab_size,
addralign=1 # Без выравнивания для строк
)
# Собираем все заголовки секций и записываем в файл
section_headers = sh0 + sh1 + sh2 + sh3
elf_data[shdr_offset : shdr_offset + len(section_headers)] = section_headers
# Возвращаем готовый ELF-файл как неизменяемые байты
return bytes(elf_data)
if __name__ == '__main__':
# Генерируем ELF и записываем в файл
with open('nop12_correct.elf', 'wb') as f:
f.write(pack_elf())
# Выводим информационные сообщения
print("Создан ИСПРАВЛЕННЫЙ файл nop12_correct.elf")
print(" eu-readelf -a nop12_correct.elf")
print(" objdump -D nop12_correct.elf")
print(" readelf -l nop12_correct.elf")
print(" file nop12_correct.elf")

View file

@ -1,247 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Скрипт генерирует 64-битный исполняемый ELF-файл для Linux (x86-64),
# содержащий 12 инструкций NOP (0x90) в секции .text и один байт (0x0A)
# в секции .data. Файл полностью корректен и распознаётся утилитами
# objdump, readelf и другими стандартными инструментами.
import struct
def pack_elf():
"""
Создаёт байтовое представление валидного ELF-исполняемого файла.
Возвращает bytes содержимое файла.
"""
# Размер страницы памяти в Linux на x86-64 (4096 байт = 0x1000)
PAGE_SIZE = 0x1000
# Адрес точки входа программы — начало секции .text
ENTRY = 0x401000
# === СОДЕРЖИМОЕ СЕКЦИЙ ===
# Машинный код: 12 инструкций NOP (код операции 0x90)
text_code = b'\x90' * 12
# Данные: один байт — символ новой строки (0x0A = '\n')
data_content = b'\x0a'
# Таблица строк имён секций (обязательна для Section Headers)
# Формат: нуль-терминированные строки подряд.
# Порядок: "", ".text", ".data", ".shstrtab"
shstrtab_content = b'\x00.text\x00.data\x00.shstrtab\x00'
# === СМЕЩЕНИЯ В ФАЙЛЕ (в байтах) ===
# Program Headers (заголовки сегментов) начинаются сразу после ELF-заголовка
phdr_start = 64 # размер ELF-заголовка
# Секция .text начинается с границы страницы 0x1000 (4096)
text_offset = 0x1000
# Секция .data начинается со следующей страницы — 0x2000 (8192)
data_offset = 0x2000
# Таблица имён секций (.shstrtab) размещается сразу после .data
shstrtab_offset = data_offset + len(data_content) # 0x2000 + 1 = 0x2001
# Заголовки секций (Section Headers) размещаются сразу после .shstrtab
shdr_offset = shstrtab_offset + len(shstrtab_content)
# === РАЗМЕРЫ КОМПОНЕНТОВ ===
text_size = len(text_code) # 12 байт
data_size = len(data_content) # 1 байт
shstrtab_size = len(shstrtab_content) # длина строки имён
# Размер одного заголовка секции — 64 байта для 64-битного ELF
# Всего 4 секции: NULL, .text, .data, .shstrtab
shdr_size = 4 * 64
# Общий размер файла: от начала до конца последнего заголовка секции
file_size = shdr_offset + shdr_size
# Создаём байтовый массив нужного размера, заполненный нулями
elf_data = bytearray(file_size)
# === 1. ELF-ЗАГОЛОВОК (64 байта) ===
# e_ident — магическое число и базовые параметры формата
e_ident = (
b'\x7fELF' # Магическое число ELF
b'\x02' # 64-битный файл
b'\x01' # Little-endian (младший байт первым)
b'\x01' # Версия ELF (1)
b'\x00' * 9 # Остальные поля e_ident — нули (OSABI=System V и т.д.)
)
# Упаковываем ELF-заголовок в бинарный формат
# Формат: < — little-endian; 16s — 16 байт; H — uint16; I — uint32; Q — uint64
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident, # e_ident[16]
2, # e_type = ET_EXEC (исполняемый файл)
0x3e, # e_machine = EM_X86_64 (AMD x86-64)
1, # e_version = 1
ENTRY, # e_entry — адрес точки входа
phdr_start, # e_phoff — смещение Program Headers в файле
shdr_offset, # e_shoff — смещение Section Headers в файле
0, # e_flags — флаги архитектуры (0 для x86-64)
64, # e_ehsize — размер ELF-заголовка (64 байта)
56, # e_phentsize — размер одного Program Header (56 байт)
3, # e_phnum — количество Program Headers
64, # e_shentsize — размер одного Section Header (64 байта)
4, # e_shnum — количество Section Headers
3 # e_shstrndx — индекс секции с именами (.shstrtab)
)
# Записываем ELF-заголовок в начало файла
elf_data[0:64] = elf_header
# === 2. PROGRAM HEADERS (заголовки сегментов) ===
# Функция для создания одного Program Header (PT_LOAD)
def make_phdr(p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align):
"""
Создаёт бинарное представление заголовка сегмента.
Поля:
p_type тип сегмента (1 = PT_LOAD)
p_flags права доступа (PF_R=4, PF_W=2, PF_X=1)
p_offset смещение сегмента в файле
p_vaddr виртуальный адрес загрузки в памяти
p_paddr физический адрес (в Linux не используется, = vaddr)
p_filesz размер данных в файле
p_memsz размер в памяти (может быть больше для .bss)
p_align выравнивание (обычно PAGE_SIZE)
"""
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_align
)
# Сегмент 0: заголовки ELF и Program Headers (только чтение)
ph0 = make_phdr(
p_type=1, # PT_LOAD
p_offset=0x0000, # смещение в файле
p_vaddr=0x400000, # адрес в памяти
p_paddr=0x400000,
p_filesz=0xe8, # 232 байта (ELF + 3×PHDR)
p_memsz=0xe8,
p_flags=4, # PF_R — только чтение
p_align=PAGE_SIZE
)
# Сегмент 1: исполняемый код (.text)
ph1 = make_phdr(
p_type=1,
p_offset=text_offset, # 0x1000
p_vaddr=0x401000, # адрес точки входа
p_paddr=0x401000,
p_filesz=text_size, # 12 байт
p_memsz=text_size,
p_flags=5, # PF_R | PF_X = 4 + 1
p_align=PAGE_SIZE
)
# Сегмент 2: данные (.data)
ph2 = make_phdr(
p_type=1,
p_offset=data_offset, # 0x2000
p_vaddr=0x402000,
p_paddr=0x402000,
p_filesz=data_size, # 1 байт
p_memsz=data_size,
p_flags=6, # PF_R | PF_W = 4 + 2
p_align=PAGE_SIZE
)
# Записываем все Program Headers в файл по смещению 64
program_headers = ph0 + ph1 + ph2
elf_data[phdr_start : phdr_start + len(program_headers)] = program_headers
# === 3. СОДЕРЖИМОЕ СЕКЦИЙ ===
# Записываем машинный код (.text) по смещению 0x1000
elf_data[text_offset : text_offset + text_size] = text_code
# Записываем данные (.data) по смещению 0x2000
elf_data[data_offset : data_offset + data_size] = data_content
# Записываем таблицу имён секций (.shstrtab)
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. SECTION HEADERS (заголовки секций) ===
# Функция для создания одного Section Header
def make_shdr(name_idx, sh_type, flags, addr, offset, size, link=0, info=0, addralign=1, entsize=0):
"""
Создаёт бинарное представление заголовка секции.
Поля:
name_idx индекс имени в .shstrtab
sh_type тип секции (1 = SHT_PROGBITS, 3 = SHT_STRTAB)
flags флаги (SHF_WRITE=1, SHF_ALLOC=2, SHF_EXECINSTR=4)
addr виртуальный адрес в памяти (0, если не загружается)
offset смещение в файле
size размер секции
link, info дополнительные ссылки (обычно 0)
addralign выравнивание (1 = без выравнивания)
entsize размер записи (для таблиц, иначе 0)
"""
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size, link, info, addralign, entsize
)
# Индексы имён в .shstrtab:
# Строка: "\x00.text\x00.data\x00.shstrtab\x00"
# Индексы: 0 1 7 13
# Секция 0: NULL (обязательна, пустая)
sh0 = make_shdr(0, 0, 0, 0, 0, 0)
# Секция 1: .text — исполняемый код
sh1 = make_shdr(
name_idx=1, # ".text" начинается с байта 1
sh_type=1, # SHT_PROGBITS — обычная секция с данными
flags=6, # SHF_ALLOC | SHF_EXECINSTR = 2 + 4
addr=0x401000, # адрес в памяти
offset=text_offset, # смещение в файле
size=text_size
)
# Секция 2: .data — инициализированные данные
sh2 = make_shdr(
name_idx=7, # ".data" начинается с байта 7
sh_type=1,
flags=3, # SHF_ALLOC | SHF_WRITE = 2 + 1
addr=0x402000,
offset=data_offset,
size=data_size
)
# Секция 3: .shstrtab — таблица имён секций
sh3 = make_shdr(
name_idx=13, # ".shstrtab" начинается с байта 13
sh_type=3, # SHT_STRTAB — таблица строк
flags=0,
addr=0,
offset=shstrtab_offset,
size=shstrtab_size
)
# Собираем все заголовки секций и записываем в файл
section_headers = sh0 + sh1 + sh2 + sh3
elf_data[shdr_offset : shdr_offset + len(section_headers)] = section_headers
# Возвращаем готовый ELF-файл как неизменяемые байты
return bytes(elf_data)
if __name__ == '__main__':
# Генерируем ELF и записываем в файл
with open('nop12_fixed.elf', 'wb') as f:
f.write(pack_elf())
# Выводим информационные сообщения
print("Создан файл nop12_fixed.elf")
print(" Проверьте его содержимое командами:")
print(" objdump -D nop12_fixed.elf")
print(" readelf -l nop12_fixed.elf")
print(" readelf -S nop12_fixed.elf")
print(" eu-readelf -a nop12_fixed.elf")

View file

@ -1,560 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
КВС - язык программирования
версия minimal.py (обновлённая, с полной поддержкой ELF)
без использования re
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
}
REGISTERS = {
"раикс": 0, # RAX
"рдикс": 2, # RDX
"рсиай": 6, # RSI
"рдиай": 7, # RDI
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === ELF layout (глобальные переменные для второго прохода и записи) ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000 # первая страница — заголовки
text_vaddr_base = 0x401000 # вторая страница — .text
data_vaddr_base = 0x402000 # третья страница — .data
# Смещения и адреса (вычисляются после первого прохода)
text_size = 0
data_size = 0
offset_text = 0
offset_data = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return f"Ошибка в файле {source_filename}, строка {line_num}:\n {line_text}\n{detail}"
def error_invalid_operand_count(mnemonic, expected, got):
return f"Инструкция '{mnemonic}':\n ожидается {expected} операнд(а/ов), получено: {got}"
def error_unknown_mnemonic(mnemonic):
return f"Неизвестная инструкция: '{mnemonic}'"
def error_invalid_register(op_name, reg_name, mnemonic):
return f"Инструкция '{mnemonic}':\n операнд '{op_name}' = '{reg_name}' не является допустимым регистром.\n Допустимые регистры: {', '.join(list(REGISTERS.keys()))}"
def error_invalid_number_format(s):
return f"Недопустимый формат числа: '{s}'"
def error_unknown_directive(word):
return f"Неизвестная директива: '{word}'"
def error_missing_label(label):
return f"Метка не найдена: '{label}'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if section == ".text":
return labels[operand] + vaddr_text
elif section == ".data":
return labels[operand] + vaddr_data
else:
raise ValueError(f"Метка в неизвестной секции: {section}")
if operand in symbols:
return symbols[operand]
raise ValueError(f"Неизвестный операнд: {operand}")
def parse_number_token(token):
if token[0] != 'word':
raise ValueError(f"Ожидалось число, получено: {token}")
s = token[1]
if s.isdigit():
return int(s)
if s.startswith("0x"):
return int(s, 16)
raise ValueError(error_invalid_number_format(s))
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError(f"Недопустимый регистр в '{mnemonic}'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError(f"Непосредственное значение слишком велико для сравнения: {imm}")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель слишком далеко для 32-битного смещения в инструкции '{mnemonic}': {offset} (должно быть в [-2^31, 2^31-1])")
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель условного перехода слишком далеко: {offset}")
code.extend(struct.pack('<i', offset))
else:
raise ValueError(f"Неизвестный тип инструкции: {itype}")
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError(f"Ожидалось слово, получено: {first}")
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(f"{word} требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError(f"Недопустимый токен в операндах: {tok}")
if pass_num == 2:
code = encode_instruction(mnemonic, operands, line_num, line_text)
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError(f"Неподдерживаемый тип инструкции при подсчёте размера: {itype}")
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size
global offset_text, offset_data, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position
lines = source.split('\n')
# === ПРОХОД 1: анализ ===
pass_num = 1
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size = position[".text"]
data_size = position[".data"]
# === ВЫЧИСЛЕНИЕ РАЗМЕЩЕНИЯ ===
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = data_vaddr_base
# Section headers
shstrtab_content = b"\x00.text\x00.data\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_data + data_size, 8)
shdr_size = 64
shdr_num = 4
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
# === ПРОХОД 2: генерация ===
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
# Точка входа
entry_addr = vaddr_text + labels.get(entry_point, 0)
# .shstrtab
shstrtab_content = b"\x00.text\x00.data\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
# Размеры
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 4
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# === 1. ELF-заголовок ===
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2, # ET_EXEC
0x3e, # EM_X86_64
1, # version
entry_addr, # entry
elf_header_size,# phoff
shdr_offset, # shoff
0, # flags
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
3 # .shstrtab index
)
elf_data[0:64] = elf_header
# === 2. Program Headers ===
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(
p_type=1,
p_flags=4,
p_offset=0,
p_vaddr=elf_base_vaddr,
p_filesz=elf_header_size + ph_num * ph_size,
p_memsz=PAGE_SIZE
)
ph1 = phdr(
p_type=1,
p_flags=5,
p_offset=offset_text,
p_vaddr=vaddr_text,
p_filesz=len(text),
p_memsz=len(text)
)
ph2 = phdr(
p_type=1,
p_flags=6,
p_offset=offset_data,
p_vaddr=vaddr_data,
p_filesz=len(data),
p_memsz=len(data)
)
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
# === 3. Секции ===
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. Section Headers ===
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
# Индексы: ""=0, ".text"=1, ".data"=7, ".shstrtab"=13
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
# === Запись ===
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python minimal.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print(f"Ошибка: файл '{source_file}' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print(f"Ошибка кодировки в файле '{source_file}': {e}")
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
print(f"ELF-файл создан: {elf_file}")
print("Запустить: ./" + elf_file)
print(f"Проверить: objdump -D {elf_file}")
print(f"Проверить: eu-readelf -a {elf_file}")
print(f"Проверить: readelf -l {elf_file}")
print(f"Проверить: file {elf_file}")
except SystemExit:
raise
except Exception as e:
print(f"Внутренняя ошибка ассемблера: {e}", file=sys.stderr)
sys.exit(1)

View file

@ -1,608 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_0.py
КВС - язык программирования
без использования re
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
}
REGISTERS = {
"раикс": 0, # RAX
"рдикс": 2, # RDX
"рсиай": 6, # RSI
"рдиай": 7, # RDI
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === ELF layout (глобальные переменные для второго прохода и записи) ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000 # первая страница — заголовки
text_vaddr_base = 0x401000 # вторая страница — .text
data_vaddr_base = 0x402000 # третья страница — .data
# Смещения и адреса (вычисляются после первого прохода)
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return f"Ошибка в файле {source_filename}, строка {line_num}:\n {line_text}\n{detail}"
def error_invalid_operand_count(mnemonic, expected, got):
return f"Инструкция '{mnemonic}':\n ожидается {expected} операнд(а/ов), получено: {got}"
def error_unknown_mnemonic(mnemonic):
return f"Неизвестная инструкция: '{mnemonic}'"
def error_invalid_register(op_name, reg_name, mnemonic):
return f"Инструкция '{mnemonic}':\n операнд '{op_name}' = '{reg_name}' не является допустимым регистром.\n Допустимые регистры: {', '.join(list(REGISTERS.keys()))}"
def error_invalid_number_format(s):
return f"Недопустимый формат числа: '{s}'"
def error_unknown_directive(word):
return f"Неизвестная директива: '{word}'"
def error_missing_label(label):
return f"Метка не найдена: '{label}'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if section == ".text":
# ВО ВТОРОМ ПРОХОДЕ используем вычисленные ранее адреса
if pass_num == 2:
return labels[operand] + vaddr_text
else:
return labels[operand] # В первом проходе - только смещение
elif section == ".data":
if pass_num == 2:
return labels[operand] + vaddr_data
else:
return labels[operand]
else:
raise ValueError(f"Метка в неизвестной секции: {section}")
if operand in symbols:
return symbols[operand]
raise ValueError(f"Неизвестный операнд: {operand}")
def parse_number_token(token):
if token[0] != 'word':
raise ValueError(f"Ожидалось число, получено: {token}")
s = token[1]
if s.isdigit():
return int(s)
if s.startswith("0x"):
return int(s, 16)
raise ValueError(error_invalid_number_format(s))
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError(f"Недопустимый регистр в '{mnemonic}'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError(f"Непосредственное значение слишком велико для сравнения: {imm}")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель слишком далеко для 32-битного смещения в инструкции '{mnemonic}': {offset} (должно быть в [-2^31, 2^31-1])")
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель условного перехода слишком далеко: {offset}")
code.extend(struct.pack('<i', offset))
else:
raise ValueError(f"Неизвестный тип инструкции: {itype}")
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError(f"Ожидалось слово, получено: {first}")
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(f"{word} требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError(f"Недопустимый токен в операндах: {tok}")
if pass_num == 2:
code = encode_instruction(mnemonic, operands, line_num, line_text)
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError(f"Неподдерживаемый тип инструкции при подсчёте размера: {itype}")
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position
lines = source.split('\n')
# === ПРОХОД 1: анализ (только подсчет размеров) ===
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# СОХРАНИТЬ размеры из первого прохода
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print(f"ПРОХОД 1: text_size={text_size_pass1} bytes, data_size={data_size_pass1} bytes")
# === ВЫЧИСЛЕНИЕ РАЗМЕЩЕНИЯ с фиксированными размерами ===
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
# Вычисляем смещения с использованием размеров из первого прохода
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
# <<< ИЗМЕНЕНИЕ: новое содержимое .comment >>>
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size, PAGE_SIZE) # 0x402000 + выравнивание
# Обновлённый .shstrtab с .comment
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print(f"РАЗМЕЩЕНИЕ: .text at offset={hex(offset_text)}, .data at offset={hex(offset_data)}")
print(f"ВИРТУАЛЬНЫЕ АДРЕСА: .text at {hex(vaddr_text)}, .data at {hex(vaddr_data)}")
# Сохраняем .comment для второго прохода
sections[".comment"] = comment_content
# === ПРОХОД 2: генерация (с фиксированными размерами) ===
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
# ВТОРОЙ ПРОХОД: используем сохраненные размеры для вычисления адресов
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# ПРОВЕРКА: размеры во втором проходе должны совпадать с первым
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print(f"ПРОХОД 2: text_size={text_size_pass2} bytes, data_size={data_size_pass2} bytes")
if text_size_pass1 != text_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: {text_size_pass1} -> {text_size_pass2}")
if data_size_pass1 != data_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: {data_size_pass1} -> {data_size_pass2}")
# Используем фактические размеры для финальных вычислений
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
# Точка входа
entry_addr = vaddr_text + labels.get(entry_point, 0)
# .shstrtab
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
# Размеры
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# === 1. ELF-заголовок ===
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2, # ET_EXEC
0x3e, # EM_X86_64
1, # version
entry_addr, # entry
elf_header_size,# phoff
shdr_offset, # shoff
0, # flags
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4 # .shstrtab index
)
elf_data[0:64] = elf_header
# === 2. Program Headers ===
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(
p_type=1,
p_flags=4,
p_offset=0,
p_vaddr=elf_base_vaddr,
p_filesz=elf_header_size + ph_num * ph_size,
p_memsz=PAGE_SIZE
)
ph1 = phdr(
p_type=1,
p_flags=5,
p_offset=offset_text,
p_vaddr=vaddr_text,
p_filesz=len(text),
p_memsz=len(text)
)
ph2 = phdr(
p_type=1,
p_flags=6,
p_offset=offset_data,
p_vaddr=vaddr_data,
p_filesz=len(data),
p_memsz=len(data)
)
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
# === 3. Секции ===
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. Section Headers ===
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
# Индексы: ""=0, ".text"=1, ".data"=7, ".comment"=13, ".shstrtab"=22
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
# === Запись ===
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print(f"Ошибка: файл '{source_file}' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print(f"Ошибка кодировки в файле '{source_file}': {e}")
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
print(f"ELF-файл создан: {elf_file}")
print("Запустить: ./" + elf_file)
print(f"Проверить: objdump -D {elf_file}")
print(f"Проверить: eu-readelf -a {elf_file}")
print(f"Проверить: readelf -l {elf_file}")
print(f"Проверить: file {elf_file}")
except SystemExit:
raise
except Exception as e:
print(f"Внутренняя ошибка ассемблера: {e}", file=sys.stderr)
sys.exit(1)

View file

@ -1,624 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_0.py
КВС - язык программирования
без использования re
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
}
REGISTERS = {
"раикс": 0, # RAX
"рдикс": 2, # RDX
"рсиай": 6, # RSI
"рдиай": 7, # RDI
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === ELF layout (глобальные переменные для второго прохода и записи) ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000 # первая страница — заголовки
text_vaddr_base = 0x401000 # вторая страница — .text
data_vaddr_base = 0x402000 # третья страница — .data
# Смещения и адреса (вычисляются после первого прохода)
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return f"Ошибка в файле {source_filename}, строка {line_num}:\n {line_text}\n{detail}"
def error_invalid_operand_count(mnemonic, expected, got):
return f"Инструкция '{mnemonic}':\n ожидается {expected} операнд(а/ов), получено: {got}"
def error_unknown_mnemonic(mnemonic):
return f"Неизвестная инструкция: '{mnemonic}'"
def error_invalid_register(op_name, reg_name, mnemonic):
return f"Инструкция '{mnemonic}':\n операнд '{op_name}' = '{reg_name}' не является допустимым регистром.\n Допустимые регистры: {', '.join(list(REGISTERS.keys()))}"
def error_invalid_number_format(s):
return f"Недопустимый формат числа: '{s}'"
def error_unknown_directive(word):
return f"Неизвестная директива: '{word}'"
def error_missing_label(label):
return f"Метка не найдена: '{label}'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if section == ".text":
# ВО ВТОРОМ ПРОХОДЕ используем вычисленные ранее адреса
if pass_num == 2:
return labels[operand] + vaddr_text
else:
return labels[operand] # В первом проходе - только смещение
elif section == ".data":
if pass_num == 2:
return labels[operand] + vaddr_data
else:
return labels[operand]
else:
raise ValueError(f"Метка в неизвестной секции: {section}")
if operand in symbols:
return symbols[operand]
raise ValueError(f"Неизвестный операнд: {operand}")
def parse_number_token(token):
if token[0] != 'word':
raise ValueError(f"Ожидалось число, получено: {token}")
s = token[1]
if s.isdigit():
return int(s)
if s.startswith("0x"):
return int(s, 16)
raise ValueError(error_invalid_number_format(s))
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError(f"Недопустимый регистр в '{mnemonic}'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError(f"Непосредственное значение слишком велико для сравнения: {imm}")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель слишком далеко для 32-битного смещения в инструкции '{mnemonic}': {offset} (должно быть в [-2^31, 2^31-1])")
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель условного перехода слишком далеко: {offset}")
code.extend(struct.pack('<i', offset))
else:
raise ValueError(f"Неизвестный тип инструкции: {itype}")
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError(f"Ожидалось слово, получено: {first}")
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(f"{word} требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError(f"Недопустимый токен в операндах: {tok}")
if pass_num == 2:
code = encode_instruction(mnemonic, operands, line_num, line_text)
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError(f"Неподдерживаемый тип инструкции при подсчёте размера: {itype}")
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position
lines = source.split('\n')
# === ПРОХОД 1: анализ (только подсчет размеров) ===
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# СОХРАНИТЬ размеры из первого прохода
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print(f"ПРОХОД 1: text_size={text_size_pass1} bytes, data_size={data_size_pass1} bytes")
# === ВЫЧИСЛЕНИЕ РАЗМЕЩЕНИЯ с фиксированными размерами ===
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
# Вычисляем смещения с использованием размеров из первого прохода
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
# ВИРТУАЛЬНЫЕ АДРЕСА - ИСПРАВЛЕНИЕ!
vaddr_text = text_vaddr_base # 0x401000
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE) # Ключевое исправление!
# <<< ИЗМЕНЕНИЕ: новое содержимое .comment >>>
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
# Обновлённый .shstrtab с .comment
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print(f"РАЗМЕЩЕНИЕ: .text at offset={hex(offset_text)}, .data at offset={hex(offset_data)}")
print(f"ВИРТУАЛЬНЫЕ АДРЕСА: .text at {hex(vaddr_text)}, .data at {hex(vaddr_data)}")
print(f"РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: {hex(vaddr_data - vaddr_text)} bytes")
print(f"РАЗМЕР .text: {text_size_pass1} bytes, требуется выравнивание до: {hex(align_up(text_size_pass1, PAGE_SIZE))}")
# Сохраняем .comment для второго прохода
sections[".comment"] = comment_content
# === ПРОХОД 2: генерация (с фиксированными размерами) ===
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
# ВТОРОЙ ПРОХОД: используем сохраненные размеры для вычисления адресов
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# ПРОВЕРКА: размеры во втором проходе должны совпадать с первым
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print(f"ПРОХОД 2: text_size={text_size_pass2} bytes, data_size={data_size_pass2} bytes")
if text_size_pass1 != text_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: {text_size_pass1} -> {text_size_pass2}")
if data_size_pass1 != data_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: {data_size_pass1} -> {data_size_pass2}")
# Используем фактические размеры для финальных вычислений
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
# ПЕРЕПРОВЕРКА и коррекция адресов
actual_text_size = len(text)
actual_data_size = len(data)
# Убедись, что vaddr_data вычислен правильно
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print(f"КОРРЕКЦИЯ: vaddr_data исправлен с {hex(vaddr_data)} на {hex(expected_vaddr_data)}")
vaddr_data = expected_vaddr_data
print(f"ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text={actual_text_size}, .data={actual_data_size}")
print(f"КОНЕЧНЫЕ АДРЕСА: .text ends at {hex(vaddr_text + actual_text_size)}, .data starts at {hex(vaddr_data)}")
print(f"ПРОВЕРКА ПЕРЕКРЫТИЯ: {'ПЕРЕКРЫТИЕ!' if (vaddr_text + actual_text_size) > vaddr_data else 'OK'}")
# Точка входа
entry_addr = vaddr_text + labels.get(entry_point, 0)
# .shstrtab
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
# Размеры
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# === 1. ELF-заголовок ===
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2, # ET_EXEC
0x3e, # EM_X86_64
1, # version
entry_addr, # entry
elf_header_size,# phoff
shdr_offset, # shoff
0, # flags
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4 # .shstrtab index
)
elf_data[0:64] = elf_header
# === 2. Program Headers ===
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(
p_type=1,
p_flags=4,
p_offset=0,
p_vaddr=elf_base_vaddr,
p_filesz=elf_header_size + ph_num * ph_size,
p_memsz=PAGE_SIZE
)
ph1 = phdr(
p_type=1,
p_flags=5,
p_offset=offset_text,
p_vaddr=vaddr_text,
p_filesz=len(text),
p_memsz=align_up(len(text), PAGE_SIZE) # Выровненный размер в памяти
)
ph2 = phdr(
p_type=1,
p_flags=6,
p_offset=offset_data,
p_vaddr=vaddr_data,
p_filesz=len(data),
p_memsz=align_up(len(data), PAGE_SIZE) # Выровненный размер в памяти
)
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
# === 3. Секции ===
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. Section Headers ===
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
# Индексы: ""=0, ".text"=1, ".data"=7, ".comment"=13, ".shstrtab"=22
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
# === Запись ===
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print(f"Ошибка: файл '{source_file}' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print(f"Ошибка кодировки в файле '{source_file}': {e}")
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
print(f"ELF-файл создан: {elf_file}")
print("Запустить: ./" + elf_file)
print(f"Проверить: objdump -D {elf_file}")
print(f"Проверить: eu-readelf -a {elf_file}")
print(f"Проверить: readelf -l {elf_file}")
print(f"Проверить: file {elf_file}")
except SystemExit:
raise
except Exception as e:
print(f"Внутренняя ошибка ассемблера: {e}", file=sys.stderr)
sys.exit(1)

View file

@ -1,678 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_4.py
КВС - язык программирования
без использования re
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
}
REGISTERS = {
"раикс": 0, # RAX
"рдикс": 2, # RDX
"рсиай": 6, # RSI
"рдиай": 7, # RDI
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout (глобальные переменные для второго прохода и записи) ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000 # первая страница — заголовки
text_vaddr_base = 0x401000 # вторая страница — .text
data_vaddr_base = 0x402000 # третья страница — .data
# Смещения и адреса (вычисляются после первого прохода)
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return f"Ошибка в файле {source_filename}, строка {line_num}:\n {line_text}\n{detail}"
def error_invalid_operand_count(mnemonic, expected, got):
return f"Инструкция '{mnemonic}':\n ожидается {expected} операнд(а/ов), получено: {got}"
def error_unknown_mnemonic(mnemonic):
return f"Неизвестная инструкция: '{mnemonic}'"
def error_invalid_register(op_name, reg_name, mnemonic):
return f"Инструкция '{mnemonic}':\n операнд '{op_name}' = '{reg_name}' не является допустимым регистром.\n Допустимые регистры: {', '.join(list(REGISTERS.keys()))}"
def error_invalid_number_format(s):
return f"Недопустимый формат числа: '{s}'"
def error_unknown_directive(word):
return f"Неизвестная директива: '{word}'"
def error_missing_label(label):
return f"Метка не найдена: '{label}'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if section == ".text":
# ВО ВТОРОМ ПРОХОДЕ используем вычисленные ранее адреса
if pass_num == 2:
return labels[operand] + vaddr_text
else:
return labels[operand] # В первом проходе - только смещение
elif section == ".data":
if pass_num == 2:
return labels[operand] + vaddr_data
else:
return labels[operand]
else:
raise ValueError(f"Метка в неизвестной секции: {section}")
if operand in symbols:
return symbols[operand]
raise ValueError(f"Неизвестный операнд: {operand}")
def parse_number_token(token):
if token[0] != 'word':
raise ValueError(f"Ожидалось число, получено: {token}")
s = token[1]
if s.isdigit():
return int(s)
if s.startswith("0x"):
return int(s, 16)
raise ValueError(error_invalid_number_format(s))
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError(f"Недопустимый регистр в '{mnemonic}'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError(f"Непосредственное значение слишком велико для сравнения: {imm}")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель слишком далеко для 32-битного смещения в инструкции '{mnemonic}': {offset} (должно быть в [-2^31, 2^31-1])")
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError(f"Цель условного перехода слишком далеко: {offset}")
code.extend(struct.pack('<i', offset))
else:
raise ValueError(f"Неизвестный тип инструкции: {itype}")
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError(f"Ожидалось слово, получено: {first}")
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(f"{word} требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError(f"Недопустимый токен в операндах: {tok}")
if pass_num == 2:
# === НАЧАЛО ИЗМЕНЕНИЙ ДЛЯ УЛУЧШЕННОГО ЛОГИРОВАНИЯ ===
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = "" # теперь называется "целевой_адрес"
try:
if itype in ("jmp", "jcc"):
# Цель перехода
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
# Проверяем, является ли второй операнд меткой
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
if sec == ".text":
addr = labels[op_str] + vaddr_text
elif sec == ".data":
addr = labels[op_str] + vaddr_data
else:
addr = labels[op_str]
target_addr = hex(addr)
# Иначе — число, оставляем пустым
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = f"{byte:02X}"
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
# === КОНЕЦ ИЗМЕНЕНИЙ ===
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError(f"Неподдерживаемый тип инструкции при подсчёте размера: {itype}")
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
# Очистка лога перед новой сборкой
log_entries = []
lines = source.split('\n')
# === ПРОХОД 1: анализ (только подсчет размеров) ===
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# СОХРАНИТЬ размеры из первого прохода
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print(f"ПРОХОД 1: text_size={text_size_pass1} bytes, data_size={data_size_pass1} bytes")
# === ВЫЧИСЛЕНИЕ РАЗМЕЩЕНИЯ с фиксированными размерами ===
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
# Вычисляем смещения с использованием размеров из первого прохода
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
# ВИРТУАЛЬНЫЕ АДРЕСА - ИСПРАВЛЕНИЕ!
vaddr_text = text_vaddr_base # 0x401000
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE) # Ключевое исправление!
# <<< ИЗМЕНЕНИЕ: новое содержимое .comment >>>
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
# Обновлённый .shstrtab с .comment
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print(f"РАЗМЕЩЕНИЕ: .text at offset={hex(offset_text)}, .data at offset={hex(offset_data)}")
print(f"ВИРТУАЛЬНЫЕ АДРЕСА: .text at {hex(vaddr_text)}, .data at {hex(vaddr_data)}")
print(f"РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: {hex(vaddr_data - vaddr_text)} bytes")
print(f"РАЗМЕР .text: {text_size_pass1} bytes, требуется выравнивание до: {hex(align_up(text_size_pass1, PAGE_SIZE))}")
# Сохраняем .comment для второго прохода
sections[".comment"] = comment_content
# === ПРОХОД 2: генерация (с фиксированными размерами) ===
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
# ВТОРОЙ ПРОХОД: используем сохраненные размеры для вычисления адресов
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
# ПРОВЕРКА: размеры во втором проходе должны совпадать с первым
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print(f"ПРОХОД 2: text_size={text_size_pass2} bytes, data_size={data_size_pass2} bytes")
if text_size_pass1 != text_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: {text_size_pass1} -> {text_size_pass2}")
if data_size_pass1 != data_size_pass2:
print(f"ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: {data_size_pass1} -> {data_size_pass2}")
# Используем фактические размеры для финальных вычислений
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
# ПЕРЕПРОВЕРКА и коррекция адресов
actual_text_size = len(text)
actual_data_size = len(data)
# Убедись, что vaddr_data вычислен правильно
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data :
print(f"КОРРЕКЦИЯ: vaddr_data исправлен с {hex(vaddr_data)} на {hex(expected_vaddr_data)}")
vaddr_data = expected_vaddr_data
print(f"ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text={actual_text_size}, .data={actual_data_size}")
print(f"КОНЕЧНЫЕ АДРЕСА: .text ends at {hex(vaddr_text + actual_text_size)}, .data starts at {hex(vaddr_data)}")
print(f"ПРОВЕРКА ПЕРЕКРЫТИЯ: {'ПЕРЕКРЫТИЕ!' if (vaddr_text + actual_text_size) > vaddr_data else 'OK'}")
# Точка входа
entry_addr = vaddr_text + labels.get(entry_point, 0)
# .shstrtab
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
# Размеры
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# === 1. ELF-заголовок ===
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2, # ET_EXEC
0x3e, # EM_X86_64
1, # version
entry_addr, # entry
elf_header_size,# phoff
shdr_offset, # shoff
0, # flags
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4 # .shstrtab index
)
elf_data[0:64] = elf_header
# === 2. Program Headers ===
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(
p_type=1,
p_flags=4,
p_offset=0,
p_vaddr=elf_base_vaddr,
p_filesz=elf_header_size + ph_num * ph_size,
p_memsz=PAGE_SIZE
)
ph1 = phdr(
p_type=1,
p_flags=5,
p_offset=offset_text,
p_vaddr=vaddr_text,
p_filesz=len(text),
p_memsz=align_up(len(text), PAGE_SIZE) # Выровненный размер в памяти
)
ph2 = phdr(
p_type=1,
p_flags=6,
p_offset=offset_data,
p_vaddr=vaddr_data,
p_filesz=len(data),
p_memsz=align_up(len(data), PAGE_SIZE) # Выровненный размер в памяти
)
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
# === 3. Секции ===
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
# === 4. Section Headers ===
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
# Индексы: ""=0, ".text"=1, ".data"=7, ".comment"=13, ".shstrtab"=22
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
# === Запись ===
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print(f"Ошибка: файл '{source_file}' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print(f"Ошибка кодировки в файле '{source_file}': {e}")
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
# === ЗАПИСЬ CSV-ЛОГА С НОВЫМ ИМЕНЕМ СТОЛБЦА ===
log_file = source_file[:-4] + ".log.csv"
with open(log_file, "w", encoding="utf-8") as f_log:
f_log.write("адрес;байт;целевой_адрессходная_команда\n") # ← изменено имя столбца
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(f"{addr};{byte};{target};{cmd}\n")
print(f"Лог создан: {log_file}")
print(f"ELF-файл создан: {elf_file}")
print("Запустить: ./" + elf_file)
print(f"Проверить: objdump -D {elf_file}")
print(f"Проверить: eu-readelf -a {elf_file}")
print(f"Проверить: readelf -l {elf_file}")
print(f"Проверить: file {elf_file}")
except SystemExit:
raise
except Exception as e:
print(f"Внутренняя ошибка ассемблера: {e}", file=sys.stderr)
sys.exit(1)

View file

@ -1,33 +0,0 @@
with open("привет5.квс", "w", encoding="utf-8") as f:
f.write(".текст\n.глобал _start\n\n")
# Мусор в .text
for i in range(4410):
f.write(f"наполнитель_{i}: переместить_имм раикс, 0x{i % 256:02x}{i % 256:02x}{i % 256:02x}{i % 256:02x}{i % 256:02x}{i % 256:02x}{i % 256:02x}{i % 256:02x}\n")
# Основной код
f.write("\n_start:\n")
f.write(" переместить_имм раикс, 1\n")
f.write(" переместить_имм рдиай, 1\n")
f.write(" переместить_имм рсиай, msg0\n")
f.write(" переместить_имм рдикс, 21\n")
f.write(" вызов_системы\n\n")
f.write(" переместить_имм раикс, 1\n")
f.write(" переместить_имм рдиай, 1\n")
f.write(" переместить_имм рсиай, msg1\n")
f.write(" переместить_имм рдикс, 21\n")
f.write(" вызов_системы\n\n")
f.write(" переместить_имм раикс, 60\n")
f.write(" переместить_имм рдиай, 0\n")
f.write(" вызов_системы\n\n")
f.write(".данные\n")
# Мусорные строки для переполнения страницы
# Каждая строка ~25 байт, нужно ~410 строк для 10250 байт (2.5+ страницы)
for i in range(410):
f.write(f'garbage_msg{i}: .строка "МУСОРНЫЕАННЫЕ_{i:04d}"\n')
# Целевые строки (должны быть на следующей странице)
f.write('msg0: .строка "Привет мир!\\n"\n')
f.write('msg1: .строка "Поди прочь!\\n"\n')

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,715 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>КВС - Ассемблер с русским синтаксисом</title>
<style>
/* Сброс и базовые настройки */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', 'Courier', monospace;
font-size: 14px;
line-height: 1.4;
color: #00ff00; /* Зеленый терминальный цвет */
background-color: #000000;
background-image:
linear-gradient(rgba(0, 20, 0, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 20, 0, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
padding: 20px;
min-height: 100vh;
cursor: default;
text-shadow: 0 0 5px rgba(0, 255, 0, 0.3);
}
/* Эффект мерцания курсора */
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.cursor {
display: inline-block;
width: 8px;
height: 16px;
background-color: #00ff00;
margin-left: 2px;
vertical-align: text-bottom;
animation: blink 1s infinite;
}
/* Контейнер терминала */
.terminal {
max-width: 1000px;
margin: 0 auto;
border: 2px solid #00aa00;
padding: 20px;
position: relative;
box-shadow:
0 0 20px rgba(0, 255, 0, 0.2),
inset 0 0 20px rgba(0, 255, 0, 0.1);
}
/* Заголовок и рамка */
.terminal::before {
content: "┌───[ КВС v9.8 ]───┐";
position: absolute;
top: -11px;
left: 20px;
background: #000;
padding: 0 10px;
font-size: 12px;
color: #00aa00;
}
.terminal::after {
content: "└─────────────────────┘";
position: absolute;
bottom: -11px;
right: 20px;
background: #000;
padding: 0 10px;
font-size: 12px;
color: #00aa00;
}
/* Заголовки */
h1, h2, h3, h4 {
color: #00ff00;
margin: 25px 0 15px 0;
border-bottom: 1px dashed #00aa00;
padding-bottom: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
h1 {
font-size: 24px;
color: #00ff88;
text-align: center;
margin-top: 0;
padding: 10px;
border: 1px solid #00aa00;
background: rgba(0, 20, 0, 0.3);
}
h2 {
font-size: 18px;
color: #00ff66;
}
h3 {
font-size: 16px;
color: #00ff44;
}
/* Параграфы и списки */
p {
margin: 15px 0;
text-align: justify;
}
ul, ol {
margin: 15px 0 15px 30px;
}
li {
margin: 8px 0;
position: relative;
}
li::before {
content: "";
color: #00ff00;
position: absolute;
left: -15px;
}
/* Ссылки */
a {
color: #00ffff;
text-decoration: none;
border-bottom: 1px dotted #00aaaa;
}
a:hover {
color: #ffff00;
border-bottom: 1px solid #ffff00;
text-shadow: 0 0 10px rgba(255, 255, 0, 0.5);
}
/* Код и примеры */
code, pre {
font-family: 'Courier New', monospace;
background: rgba(0, 30, 0, 0.5);
border: 1px solid #005500;
padding: 15px;
margin: 15px 0;
display: block;
overflow-x: auto;
white-space: pre;
color: #88ff88;
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
position: relative;
}
pre::before {
content: "┌──[ код ]";
position: absolute;
top: -10px;
left: 10px;
background: #000;
padding: 0 5px;
font-size: 11px;
color: #00aa00;
}
/* Таблицы */
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
border: 1px solid #005500;
}
th {
background: rgba(0, 40, 0, 0.5);
color: #00ff88;
padding: 10px;
border: 1px solid #005500;
text-align: left;
font-weight: bold;
text-transform: uppercase;
font-size: 12px;
}
td {
padding: 10px;
border: 1px solid #005500;
color: #88ff88;
vertical-align: top;
}
tr:nth-child(even) {
background: rgba(0, 25, 0, 0.3);
}
tr:hover {
background: rgba(0, 50, 0, 0.4);
}
/* Блоки с заметками */
.note {
border-left: 3px solid #ffff00;
padding: 15px;
margin: 20px 0;
background: rgba(40, 40, 0, 0.2);
color: #ffff88;
}
.warning {
border-left: 3px solid #ff4444;
padding: 15px;
margin: 20px 0;
background: rgba(40, 0, 0, 0.2);
color: #ff8888;
}
/* Диаграмма процесса */
.process-diagram {
text-align: center;
margin: 30px 0;
font-family: monospace;
white-space: pre;
color: #00ffaa;
background: rgba(0, 20, 0, 0.3);
padding: 20px;
border: 1px dashed #00aa00;
overflow-x: auto;
}
/* Футер */
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #005500;
text-align: center;
color: #00aa00;
font-size: 12px;
}
/* Эффект печатания для заголовка */
.typewriter {
overflow: hidden;
border-right: 3px solid #00ff00;
white-space: nowrap;
margin: 0 auto;
animation:
typing 3.5s steps(40, end),
blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from { width: 0 }
to { width: 100% }
}
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: #00ff00 }
}
/* Стиль для нового ASCII-арта */
.ascii-art {
text-align: center;
margin: 30px 0;
font-family: monospace;
color: #00ff88;
line-height: 1.2;
white-space: pre;
text-shadow: 0 0 10px rgba(0, 255, 136, 0.5);
}
.ascii-art-box {
display: inline-block;
border: none;
background: none;
padding: 0;
margin: 0;
}
/* Адаптивность */
@media (max-width: 768px) {
body {
padding: 10px;
font-size: 12px;
}
.terminal {
padding: 15px;
}
h1 {
font-size: 18px;
}
h2 {
font-size: 16px;
}
table {
font-size: 11px;
}
pre, code {
font-size: 11px;
padding: 10px;
}
.ascii-art {
font-size: 10px;
overflow-x: auto;
}
}
/* Скроллбар в стиле терминала */
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
background: rgba(0, 20, 0, 0.3);
border: 1px solid #005500;
}
::-webkit-scrollbar-thumb {
background: #005500;
border: 1px solid #00aa00;
}
::-webkit-scrollbar-thumb:hover {
background: #00aa00;
}
</style>
</head>
<body>
<div class="terminal">
<!-- Заголовок с эффектом печатания -->
<h1 class="typewriter">КВС (kvs_9_8.py) — Ассемблер с русским синтаксисом для x86-64</h1>
<!-- Приветственное сообщение с новым ASCII-артом -->
<div style="text-align: center; margin: 30px 0; color: #00ff88;">
<div class="ascii-art">
<pre class="ascii-art-box">
╔══════════════════════════════════╗
║ ██╗ ██╗ ██╗ ██╗ ███████╗ ║
║ ██║ ██╔╝ ██╗ ██╗ ██╔════╝ ║
║ █████╔╝ ██╗ ██╗ ███████╗ ║
║ ██║╚██╗ ╚██╗ ██╔╝ ╚════██║ ║
║ ██║ ╚██╗ ╚██╔╝ ███████║ ║
║ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ║
╚══════════════════════════════════╝
</pre>
</div>
<p>Версия 9.8 | Экспериментальный проект | 2025</p>
</div>
<!-- Оглавление -->
<h2>┌──[ ОГЛАВЛЕНИЕ ]────────────────────────────────────────────┐</h2>
<ul style="columns: 2; column-gap: 40px;">
<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>
<!-- Раздел 1: Назначение и концепция -->
<h2 id="назначение">НАЗНАЧЕНИЕ И КОНЦЕПЦИЯ</h2>
<p><strong>КВС</strong> — это ассемблер для архитектуры x86-64, полностью использующий <strong>русскую лексику</strong> для мнемоник инструкций, имён регистров и директив. Проект носит <strong>образовательный и экспериментальный характер</strong> и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.</p>
<div class="note">
<strong>Ключевые философские принципы проекта:</strong>
<ul>
<li><strong>Самодокументируемый код</strong>: Читаемость и ясность важнее краткости.</li>
<li><strong>Изоляция логики</strong>: Разделение парсера, трансформатора и кодогенератора.</li>
<li><strong>Минимизация зависимостей</strong>: Используется только <code>struct</code> для упаковки данных, <code>re</code> не используется.</li>
<li><strong>Избегание чрезмерного ООП</strong>: В контексте возможной будущей самокомпиляции.</li>
</ul>
</div>
<!-- Раздел 2: Особенности и возможности -->
<h2 id="особенности">ОСОБЕННОСТИ И ВОЗМОЖНОСТИ</h2>
<ul>
<li><strong>Полностью русский синтаксис</strong>: Все инструкции (<code>переместить</code>, <code>прибавить</code>, <code>вызвать</code>), регистры (<code>раикс</code>, <code>есипи</code>, <code>ал</code>) и директивы (<code>.текст</code>, <code>.данные</code>) используют кириллицу.</li>
<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>.квс</code> с автоматическим разрешением имён.</li>
<li><strong>Подготовка к сложной адресации</strong>: Архитектура заложена для будущей трансформации сложных режимов адресации памяти в последовательности простых инструкций через промежуточное представление.</li>
<li><strong>Детальное логирование</strong>: Генерация <code>.log.csv</code> файла с полным соответствием машинных кодов, виртуальных адресов и исходных команд.</li>
</ul>
<!-- Раздел 3: Архитектура и процесс сборки -->
<h2 id="архитектура">АРХИТЕКТУРА И ПРОЦЕСС СБОРКИ</h2>
<p>Процесс трансляции исходного кода <code>.квс</code> в исполняемый файл <code>.elf</code> проходит через несколько этапов:</p>
<div class="process-diagram">
Исходный файл (.квс)
▼ (Этап 1: Разбор и импорт)
Промежуточный файл (.квс.промежуточный)
▼ (Этап 2: Двухпроходное ассемблирование)
Данные секций + Таблица меток
▼ (Этап 3: Генерация ELF)
Исполняемый файл (.elf) + Лог (.log.csv)
</div>
<p><strong>Подробнее об этапах:</strong></p>
<ol>
<li><strong>Построение промежуточного представления</strong>: Исходный файл и все импортированные модули объединяются в один текст. Метки и константы переименовываются (получают суффикс имени модуля) для избежания коллизий.</li>
<li><strong>Первый проход ассемблирования</strong>: Анализируется промежуточный файл, вычисляются размеры всех инструкций и данных, строится таблица меток с их смещениями внутри секций.</li>
<li><strong>Второй проход ассемблирования</strong>: Генерируется машинный код. Адреса меток подставляются в инструкции переходов, загрузки адресов и т.д. Формируются бинарные содержимое секций <code>.text</code> и <code>.data</code>.</li>
<li><strong>Компоновка ELF</strong>: Собранные секции упаковываются в формат исполняемого файла ELF 64-bit с корректными заголовками, таблицами секций и сегментов.</li>
</ol>
<!-- Раздел 4: Формат исходных файлов -->
<h2 id="формат">ФОРМАТ ИСХОДНЫХ ФАЙЛОВ (.квс)</h2>
<p>Исходные файлы имеют расширение <code>.квс</code>. Их структура следует традициям ассемблера:</p>
<pre>
; Комментарий начинается с точки с запятой
.данные ; Начало секции данных
приветствие: .строкауль "Здравствуй, мир!"
число: .байт 0x2A
.текст ; Начало секции кода
.глобал _start ; Объявление точки входа
_start: ; Метка
переместить_имм раикс, 60 ; Системный вызов exit (60) в rax
переместить_имм рдикс, 42 ; Код возврата (42) в rdi
вызов_системы ; Вызов ядра (syscall)</pre>
<!-- Раздел 5: Система команд -->
<h2 id="команды">СИСТЕМА КОМАНД (ИНСТРУКЦИИ)</h2>
<p>Ассемблер поддерживает обширный набор инструкций x86-64, сгруппированных по категориям:</p>
<table>
<tr>
<th>Категория</th>
<th>Примеры инструкций</th>
<th>Соответствие NASM</th>
</tr>
<tr>
<td><strong>Перемещение данных</strong></td>
<td><code>переместить</code>, <code>загрузить</code>, <code>сохранить</code>, <code>загрузить_адрес</code></td>
<td><code>mov</code>, <code>lea</code></td>
</tr>
<tr>
<td><strong>Арифметика</strong></td>
<td><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></td>
</tr>
<tr>
<td><strong>Логические операции</strong></td>
<td><code>и</code>, <code>или</code>, <code>инвертировать</code>, <code>проверить</code></td>
<td><code>and</code>, <code>or</code>, <code>not</code>, <code>test</code></td>
</tr>
<tr>
<td><strong>Управление потоком</strong></td>
<td><code>переход</code>, <code>переход_если_равно</code>, <code>вызвать</code>, <code>вернуться</code></td>
<td><code>jmp</code>, <code>je</code>, <code>call</code>, <code>ret</code></td>
</tr>
<tr>
<td><strong>Работа со стеком</strong></td>
<td><code>втолкнуть</code>, <code>вытолкнуть</code></td>
<td><code>push</code>, <code>pop</code></td>
</tr>
<tr>
<td><strong>Сдвиги и вращения</strong></td>
<td><code>сдвиг_влево</code>, <code>вращать_вправо</code></td>
<td><code>shl</code>, <code>ror</code></td>
</tr>
<tr>
<td><strong>Строковые операции</strong></td>
<td><code>переместить_байт</code>, <code>сравнить_байты</code></td>
<td><code>movsb</code>, <code>cmpsb</code></td>
</tr>
<tr>
<td><strong>Системные вызовы</strong></td>
<td><code>вызов_системы</code>, <code>прервать</code></td>
<td><code>syscall</code>, <code>int</code></td>
</tr>
</table>
<p><strong>Особенности кодирования:</strong> Проект корректно генерирует префиксы REX, учитывает размеры операндов (8/16/32/64 бита) и поддерживает базовые режимы адресации памяти (например, <code>[раикс]</code>).</p>
<!-- Раздел 6: Регистры процессора -->
<h2 id="регистры">РЕГИСТРЫ ПРОЦЕССОРА</h2>
<p>Поддерживаются регистры x86-64 во всех их размерах:</p>
<table>
<tr>
<th>64-битные</th>
<th>32-битные</th>
<th>16-битные</th>
<th>8-битные (младшие)</th>
<th>8-битные (старшие)</th>
</tr>
<tr>
<td><code>раикс</code></td>
<td><code>еаикс</code></td>
<td><code>аикс</code></td>
<td><code>ал</code></td>
<td><code>аш</code></td>
</tr>
<tr>
<td><code>рбикс</code></td>
<td><code>ебикс</code></td>
<td><code>бикс</code></td>
<td><code>бл</code></td>
<td><code>бш</code></td>
</tr>
<tr>
<td><code>рсикс</code></td>
<td><code>есикс</code></td>
<td><code>сикс</code></td>
<td><code>кл</code></td>
<td><code>чш</code></td>
</tr>
<tr>
<td><code>рдикс</code></td>
<td><code>едикс</code></td>
<td><code>дикс</code></td>
<td><code>дл</code></td>
<td><code>дш</code></td>
</tr>
<tr>
<td><code>рсипи</code></td>
<td><code>есипи</code></td>
<td><code>эсп</code></td>
<td><code>спл</code></td>
<td></td>
</tr>
<tr>
<td><code>рбипи</code></td>
<td><code>ебипи</code></td>
<td><code>бипи</code></td>
<td><code>бпл</code></td>
<td></td>
</tr>
<tr>
<td><code>рсиай</code></td>
<td><code>есиай</code></td>
<td><code>эс</code></td>
<td><code>сил</code></td>
<td></td>
</tr>
<tr>
<td><code>рдиай</code></td>
<td><code>едиай</code></td>
<td><code>ди</code></td>
<td><code>дил</code></td>
<td></td>
</tr>
<tr>
<td><code>р8</code> ... <code>р15</code></td>
<td><code>р</code> ... <code>р15д</code></td>
<td><code>р</code> ... <code>р15в</code></td>
<td><code>р8б</code> ... <code>р15б</code></td>
<td></td>
</tr>
</table>
<!-- Раздел 7: Директивы ассемблера -->
<h2 id="директивы">ДИРЕКТИВЫ АССЕМБЛЕРА</h2>
<p>Директивы управляют процессом ассемблирования, не превращаясь в машинный код напрямую.</p>
<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>
<td><code>.строка</code> / <code>.строкауль</code></td>
<td>Резервирует место в секции данных для строки (без нуля / с нулевым завершителем).</td>
<td><code>.строкауль "Текст"</code></td>
</tr>
<tr>
<td><code>.байт</code></td>
<td>Резервирует один или несколько байт с заданными значениями.</td>
<td><code>.байт 1, 0xFE, 255</code></td>
</tr>
<tr>
<td><code>.константа</code></td>
<td>Определяет символьную константу, которую можно использовать как число.</td>
<td><code>.константа ВЫХОД = 60</code></td>
</tr>
<tr>
<td><code>.импорт</code></td>
<td>Включает содержимое другого файла <code>.квс</code> в текущую сборку.</td>
<td><code>.импорт библиотека.квс</code></td>
</tr>
</table>
<!-- Раздел 8: Система импорта модулей -->
<h2 id="импорт">СИСТЕМА ИМПОРТА МОДУЛЕЙ</h2>
<p>Директива <code>.импорт</code> позволяет разбивать проект на несколько файлов.</p>
<p><strong>Принцип работы:</strong></p>
<ol>
<li><strong>Рекурсивный поиск</strong>: Ассемблер ищет указанный файл в той же директории, что и импортирующий файл.</li>
<li><strong>Переименование</strong>: Чтобы избежать конфликтов имён, все метки и константы импортированного модуля автоматически получают суффикс, основанный на имени файла (например, метка <code>цикл</code> из <code>модуль.квс</code> станет <code>цикл_модуль</code>).</li>
<li><strong>Объединение</strong>: Содержимое всех секций <code>.данные</code> из импортированных файлов сливается в общую секцию <code>.данные</code> промежуточного файла. Аналогично для <code>.текст</code>.</li>
</ol>
<div class="note">
Это упрощает создание библиотек и организацию больших проектов.
</div>
<!-- Раздел 9: Процесс трансляции -->
<h2 id="трансляция">ПРОЦЕСС ТРАНСЛЯЦИИ И ВЫХОДНЫЕ ФАЙЛЫ</h2>
<p>Запуск ассемблера:</p>
<pre>python3 kvs_9_8.py программа.квс</pre>
<p><strong>В результате создаются три файла:</strong></p>
<ol>
<li><strong><code>программа.квс.промежуточный</code></strong>: Текстовый файл, содержащий объединённый и переименованный код всех модулей. Используется для отладки процесса импорта.</li>
<li><strong><code>программа.elf</code></strong>: Исполняемый 64-битный ELF-файл. Для запуска может потребоваться установить флаг исполнения: <code>chmod +x программа.elf</code>.</li>
<li><strong><code>программа.log.csv</code></strong>: Детальный лог в формате CSV, где для каждого байта машинного кода указан его виртуальный адрес, шестнадцатеричное значение, целевой адрес (для инструкций переходов) и исходная команда на русском языке. Неоценим для обучения и отладки.</li>
</ol>
<!-- Раздел 10: Ограничения -->
<h2 id="ограничения">ОГРАНИЧЕНИЯ И ОСОБЕННОСТИ РЕАЛИЗАЦИИ</h2>
<div class="warning">
<ul>
<li><strong>Экспериментальный статус</strong>: Проект в активной разработке, некоторые углы архитектуры x86-64 могут быть реализованы не полностью.</li>
<li><strong>Базовая адресация памяти</strong>: На данный момент поддерживаются преимущественно простые формы адресации (например, <code>[регистр]</code>). Режимы со сдвигом и несколькими регистрами (<code>[база + индекс*масштаб + смещение]</code>) предназначены для реализации через <strong>трансформацию в промежуточном представлении</strong> (задел на будущее).</li>
<li><strong>Отсутствие макросов и условной компиляции</strong>: В текущей версии эти конструкции не поддерживаются.</li>
<li><strong>Лексический анализатор (лексер)</strong>: Написан "вручную", без использования регулярных выражений (<code>re</code>), в соответствии с философией проекта.</li>
<li><strong>Генерация ELF</strong>: Реализация фокусируется на создании рабочих исполняемых файлов для Linux. Некоторые необязательные или специфичные поля заголовков ELF могут быть заполнены упрощённо.</li>
</ul>
</div>
<!-- Футер -->
<footer>
<p>───────────────────────────────────────────────────────────────────────</p>
<p>КВС Ассемблер v9.8 | Экспериментальный проект | 2025</p>
<p>Исходный код: <a href="https://github.com/artradeskz/kvs">github.com/artradeskz/kvs</a></p>
<p>Только для образовательных целей. Используйте на свой страх и риск.</p>
<p style="margin-top: 10px; font-size: 10px; color: #005500;">
> Система готова. Введите команду...
<span class="cursor"></span>
</p>
</footer>
</div>
<script>
// Простой скрипт для имитации терминала
document.addEventListener('DOMContentLoaded', function() {
// Добавляем эффект появления текста
const elements = document.querySelectorAll('h1, h2, h3, p, li, td, th, pre, code');
elements.forEach((el, index) => {
el.style.opacity = '0';
el.style.transition = 'opacity 0.5s ease';
setTimeout(() => {
el.style.opacity = '1';
}, 100 + index * 10);
});
// Эффект для ссылок
const links = document.querySelectorAll('a');
links.forEach(link => {
link.addEventListener('mouseenter', function() {
this.style.transition = 'all 0.2s';
});
});
// Имитация ввода команды в футере
const cursor = document.querySelector('.cursor');
let commandVisible = false;
setInterval(() => {
if (!commandVisible) {
cursor.style.animation = 'none';
setTimeout(() => {
cursor.style.animation = 'blink 1s infinite';
}, 50);
}
}, 3000);
});
</script>
</body>
</html>

154
readme.md
View file

@ -1,154 +0,0 @@
# КВС (kvs_9_8.py) — Ассемблер с русским синтаксисом для x86-64
## 📖 Оглавление
- [🎯 Назначение и концепция](#-назначение-и-концепция)
- [🚀 Особенности и возможности](#-особенности-и-возможности)
- [⚙️ Архитектура и процесс сборки](#-архитектура-и-процесс-сборки)
- [📁 Формат исходных файлов (.квс)](#-формат-исходных-файлов-квс)
- [🔧 Система команд (инструкции)](#-система-команд-инструкции)
- [💾 Регистры процессора](#-регистры-процессора)
- [📋 Директивы ассемблера](#-директивы-ассемблера)
- [📦 Система импорта модулей](#-система-импорта-модулей)
- [🔄 Процесс трансляции и выходные файлы](#-процесс-трансляции-и-выходные-файлы)
- [⚠️ Ограничения и особенности реализации](#-ограничения-и-особенности-реализации)
## 🎯 Назначение и концепция
**КВС** — это ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит **образовательный и экспериментальный характер** и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.
**Ключевые философские принципы проекта:**
* **Самодокументируемый код**: Читаемость и ясность важнее краткости.
* **Изоляция логики**: Разделение парсера, трансформатора и кодогенератора.
* **Минимизация зависимостей**: Используется только `struct` для упаковки данных, `re` не используется.
* **Избегание чрезмерного ООП**: В контексте возможной будущей самокомпиляции.
## 🚀 Особенности и возможности
* **Полностью русский синтаксис**: Все инструкции (`переместить`, `прибавить`, `вызвать`), регистры (`раикс`, `есипи`, `ал`) и директивы (`.текст`, `.данные`) используют кириллицу.
* **Генерация исполняемых ELF-файлов**: На выходе создаются 64-битные ELF-файлы для Linux, готовые к запуску.
* **Двухпроходная сборка**: Традиционная для ассемблеров схема с разрешением меток.
* **Поддержка секций**: Явное разделение на секции кода (`.текст`) и данных (`.данные`).
* **Система импорта модулей**: Директива `.импорт` для включения других файлов `.квс` с автоматическим разрешением имён.
* **Подготовка к сложной адресации**: Архитектура заложена для будущей трансформации сложных режимов адресации памяти в последовательности простых инструкций через промежуточное представление.
* **Детальное логирование**: Генерация `.log.csv` файла с полным соответствием машинных кодов, виртуальных адресов и исходных команд.
## ⚙️ Архитектура и процесс сборки
Процесс трансляции исходного кода `.квс` в исполняемый файл `.elf` проходит через несколько этапов:
```
Исходный файл (.квс)
▼ (Этап 1: Разбор и импорт)
Промежуточный файл (.квс.промежуточный)
▼ (Этап 2: Двухпроходное ассемблирование)
Данные секций + Таблица меток
▼ (Этап 3: Генерация ELF)
Исполняемый файл (.elf) + Лог (.log.csv)
```
**Подробнее об этапах:**
1. **Построение промежуточного представления**: Исходный файл и все импортированные модули объединяются в один текст. Метки и константы переименовываются (получают суффикс имени модуля) для избежания коллизий.
2. **Первый проход ассемблирования**: Анализируется промежуточный файл, вычисляются размеры всех инструкций и данных, строится таблица меток с их смещениями внутри секций.
3. **Второй проход ассемблирования**: Генерируется машинный код. Адреса меток подставляются в инструкции переходов, загрузки адресов и т.д. Формируются бинарные содержимое секций `.text` и `.data`.
4. **Компоновка ELF**: Собранные секции упаковываются в формат исполняемого файла ELF 64-bit с корректными заголовками, таблицами секций и сегментов.
## 📁 Формат исходных файлов (.квс)
Исходные файлы имеют расширение `.квс`. Их структура следует традициям ассемблера:
```asm
; Комментарий начинается с точки с запятой
.данные ; Начало секции данных
приветствие: .строкауль "Здравствуй, мир!"
число: .байт 0x2A
.текст ; Начало секции кода
.глобал _start ; Объявление точки входа
_start: ; Метка
переместить_имм раикс, 60 ; Системный вызов exit (60) в rax
переместить_имм рдикс, 42 ; Код возврата (42) в rdi
вызов_системы ; Вызов ядра (syscall)
```
## 🔧 Система команд (инструкции)
Ассемблер поддерживает обширный набор инструкций x86-64, сгруппированных по категориям:
| Категория | Примеры инструкций | Соответствие NASM |
| :--- | :--- | :--- |
| **Перемещение данных** | `переместить`, `загрузить`, `сохранить`, агрузить_адрес` | `mov`, `lea` |
| **Арифметика** | `прибавить`, `вычесть`, `умножить`, `разделить`, `увеличить` | `add`, `sub`, `mul`, `div`, `inc` |
| **Логические операции** | `и`, `или`, `инвертировать`, `проверить` | `and`, `or`, `not`, `test` |
| **Управление потоком** | `переход`, `переход_если_равно`, `вызвать`, `вернуться` | `jmp`, `je`, `call`, `ret` |
| **Работа со стеком** | `втолкнуть`, `вытолкнуть` | `push`, `pop` |
| **Сдвиги и вращения** | `сдвиг_влево`, ращать_вправо` | `shl`, `ror` |
| **Строковые операции** | `переместить_байт`, `сравнить_байты` | `movsb`, `cmpsb` |
| **Системные вызовы** | `вызов_системы`, `прервать` | `syscall`, `int` |
**Особенности кодирования:** Проект корректно генерирует префиксы REX, учитывает размеры операндов (8/16/32/64 бита) и поддерживает базовые режимы адресации памяти (например, `[раикс]`).
## 💾 Регистры процессора
Поддерживаются регистры x86-64 во всех их размерах:
| 64-битные | 32-битные | 16-битные | 8-битные (младшие) | 8-битные (старшие) |
| :--- | :--- | :--- | :--- | :--- |
| `раикс` | `еаикс` | `аикс` | `ал` | `аш` |
| `рбикс` | `ебикс` | `бикс` | `бл` | `бш` |
| `рсикс` | `есикс` | `сикс` | `кл` | `чш` |
| `рдикс` | `едикс` | `дикс` | `дл` | `дш` |
| `рсипи` | `есипи` | `эсп` | `спл` | — |
| `рбипи` | `ебипи` | `бипи` | `бпл` | — |
| `рсиай` | `есиай` | `эс` | `сил` | — |
| `рдиай` | `едиай` | `ди` | `дил` | — |
| `р8` ... `р15` | `р8д` ... `р15д` | `р8в` ... `р15в` | `р8б` ... `р15б` | — |
## 📋 Директивы ассемблера
Директивы управляют процессом ассемблирования, не превращаясь в машинный код напрямую.
| Директива | Описание | Пример |
| :--- | :--- | :--- |
| `.текст` | Переключает ассемблер на запись в секцию кода. | `.текст` |
| `.данные` | Переключает ассемблер на запись в секцию данных. | `.данные` |
| `.глобал` | Объявляет метку глобальной (точку входа программы). | `.глобал _start` |
| `.строка` / `.строкауль` | Резервирует место в секции данных для строки (без нуля / с нулевым завершителем). | `.строкауль "Текст"` |
| `.байт` | Резервирует один или несколько байт с заданными значениями. | `.байт 1, 0xFE, 255` |
| `.константа` | Определяет символьную константу, которую можно использовать как число. | `.константа ВЫХОД = 60` |
| `.импорт` | Включает содержимое другого файла `.квс` в текущую сборку. | `.импорт библиотека.квс` |
## 📦 Система импорта модулей
Директива `.импорт` позволяет разбивать проект на несколько файлов.
**Принцип работы:**
1. **Рекурсивный поиск**: Ассемблер ищет указанный файл в той же директории, что и импортирующий файл.
2. **Переименование**: Чтобы избежать конфликтов имён, все метки и константы импортированного модуля автоматически получают суффикс, основанный на имени файла (например, метка `цикл` из `модуль.квс` станет `цикл_модуль`).
3. **Объединение**: Содержимое всех секций `.данные` из импортированных файлов сливается в общую секцию `.данные` промежуточного файла. Аналогично для `.текст`.
Это упрощает создание библиотек и организацию больших проектов.
## 🔄 Процесс трансляции и выходные файлы
Запуск ассемблера:
```bash
python3 kvs_9_8.py программа.квс
```
**В результате создаются три файла:**
1. **`программа.квс.промежуточный`**: Текстовый файл, содержащий объединённый и переименованный код всех модулей. Используется для отладки процесса импорта.
2. **`программа.elf`**: Исполняемый 64-битный ELF-файл. Для запуска может потребоваться установить флаг исполнения: `chmod +x программа.elf`.
3. **`программа.log.csv`**: Детальный лог в формате CSV, где для каждого байта машинного кода указан его виртуальный адрес, шестнадцатеричное значение, целевой адрес (для инструкций переходов) и исходная команда на русском языке. Неоценим для обучения и отладки.
## ⚠️ Ограничения и особенности реализации
* **Экспериментальный статус**: Проект в активной разработке, некоторые углы архитектуры x86-64 могут быть реализованы не полностью.
* **Базовая адресация памяти**: На данный момент поддерживаются преимущественно простые формы адресации (например, `[регистр]`). Режимы со сдвигом и несколькими регистрами (`[база + индекс*масштаб + смещение]`) предназначены для реализации через **трансформацию в промежуточном представлении** (задел на будущее).
* **Отсутствие макросов и условной компиляции**: В текущей версии эти конструкции не поддерживаются.
* **Лексический анализатор (лексер)**: Написан "вручную", без использования регулярных выражений (`re`), в соответствии с философией проекта.
* **Генерация ELF**: Реализация фокусируется на создании рабочих исполняемых файлов для Linux. Некоторые необязательные или специфичные поля заголовков ELF могут быть заполнены упрощённо.

View file

@ -1,160 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import csv
import os
# --- Основная функция конвертации ---
def convert_csv_to_html(csv_file_path, html_file_path):
"""
Читает semicolon-separated CSV файл и создает красивую HTML-страницу.
Возвращает (True, сообщение) в случае успеха или (False, сообщение) в случае ошибки.
"""
try:
with open(csv_file_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f, delimiter=';')
headers = reader.fieldnames
if not headers:
return False, "CSV-файл пуст или не имеет заголовков."
rows = list(reader)
except FileNotFoundError:
return False, f"Файл не найден: {csv_file_path}"
except Exception as e:
return False, f"Ошибка при чтении CSV: {e}"
# CSS для красивого оформления
css_style = """
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: #f4f7f6; color: #333; margin: 0; padding: 20px; }
.container { max-width: 1400px; margin: 20px auto; background-color: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); }
h1 { color: #2c3e50; text-align: center; margin-bottom: 25px; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #e0e0e0; vertical-align: top; word-wrap: break-word; }
th { background-color: #34495e; color: #ffffff; font-weight: 600; text-transform: uppercase; font-size: 13px; }
tr:nth-child(even) { background-color: #f8f9fa; }
tr:hover { background-color: #e9ecef; }
a { color: #3498db; text-decoration: none; font-weight: bold; }
a:hover { text-decoration: underline; color: #2980b9; }
.mono { font-family: 'Courier New', Courier, monospace; font-size: 13px; white-space: pre-wrap; }
tr:target { background-color: #fff3cd !important; border: 2px solid #ffeaa7; animation: highlight 2s ease-in-out; }
@keyframes highlight { from { background-color: #ffeaa7; } to { background-color: #fff3cd; } }
</style>
"""
# Начало HTML-документа
html_content = f"""
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{os.path.basename(html_file_path)}</title>
{css_style}
</head>
<body>
<div class="container">
<h1>Анализ дизассемблерного листинга: {os.path.basename(csv_file_path)}</h1>
<table>
<thead><tr>
"""
for header in headers:
html_content += f"<th>{header}</th>\n"
html_content += "</tr></thead><tbody>\n"
# Добавляем строки таблицы
for row in rows:
address = row.get('адрес', '').strip()
target_address = row.get('целевой_адрес', '').strip()
link_html = target_address
if target_address:
link_html = f'<a href="#{target_address}">{target_address}</a>'
# Присваиваем id каждой строке на основе ее адреса
row_id = address if address else ''
html_content += f'<tr id="{row_id}">\n'
html_content += f'<td class="mono">{address}</td>\n'
html_content += f'<td class="mono">{row.get("байт", "")}</td>\n'
html_content += f'<td class="mono">{link_html}</td>\n'
html_content += f'<td>{row.get("исходная_команда", "")}</td>\n'
html_content += "</tr>\n"
html_content += "</tbody></table></div></body></html>"
# Запись HTML в файл
try:
with open(html_file_path, 'w', encoding='utf-8') as f:
f.write(html_content)
return True, f"Успешно! Файл сохранен как:\n{html_file_path}"
except Exception as e:
return False, f"Ошибка при записи HTML файла: {e}"
# --- Функция-обработчик нажатия на кнопку ---
def open_file_and_convert():
# Открываем диалог выбора файла
file_path = filedialog.askopenfilename(
title="Выберите CSV-файл для конвертации",
filetypes=(("CSV файлы", "*.csv"), ("Все файлы", "*.*"))
)
# Если пользователь не выбрал файл, выходим
if not file_path:
status_label.config(text="Операция отменена.")
return
# Формируем имя для выходного HTML-файла
base_name = os.path.splitext(file_path)[0]
output_path = f"{base_name}.html"
# Обновляем статус
status_label.config(text="Обработка файла...")
root.update_idletasks() # Принудительно обновляем интерфейс
# Вызываем функцию конвертации
success, message = convert_csv_to_html(file_path, output_path)
# Показываем результат
if success:
status_label.config(text="Готово!")
messagebox.showinfo("Успех", message)
else:
status_label.config(text="Произошла ошибка.")
messagebox.showerror("Ошибка", message)
# --- Создание графического интерфейса ---
root = tk.Tk()
root.title("CSV в HTML Конвертер")
root.geometry("450x200") # Размер окна
root.resizable(False, False) # Запрещаем изменять размер
# Создаем фрейм для лучшего расположения элементов
main_frame = tk.Frame(root, padx=20, pady=20)
main_frame.pack(expand=True, fill=tk.BOTH)
# Кнопка "Открыть"
open_button = tk.Button(
main_frame,
text="Открыть CSV-файл",
command=open_file_and_convert,
width=20,
height=2,
font=("Helvetica", 12)
)
open_button.pack(pady=10)
# Метка для отображения статуса
status_label = tk.Label(
main_frame,
text="Нажмите кнопку, чтобы выбрать файл",
wraplength=400, # Перенос длинных строк
justify=tk.CENTER
)
status_label.pack(pady=10)
# Запускаем главный цикл приложения
root.mainloop()

View file

@ -1,149 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
from datetime import datetime
def binary_to_disasm(input_file, output_file=None, skip_zeros=True):
"""Конвертирует бинарный файл в дизассемблированный код с помощью ndisasm."""
try:
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_disasm.txt"
# Вызываем ndisasm для дизассемблирования
result = subprocess.run(
['ndisasm', '-b64', input_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Проверяем наличие ошибок от ndisasm
if result.returncode != 0:
error_msg = result.stderr.strip()
return False, f"Ошибка ndisasm: {error_msg}"
# Получаем текущую дату и время
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Фильтруем строки в зависимости от настройки
lines = result.stdout.splitlines()
if skip_zeros:
filtered_lines = [line for line in lines if 'add [rax],al' not in line]
filter_status = "Нули (add [rax],al) пропущены"
else:
filtered_lines = lines
filter_status = "Нули (add [rax],al) не пропущены"
# Записываем отфильтрованные строки в файл с заголовком
with open(output_file, 'w', encoding='utf-8') as out_file:
# Добавляем заголовок с информацией
out_file.write("=" * 60 + "\n")
out_file.write(f"Дизассемблированный код сгенерирован с помощью ndisasm\n")
out_file.write(f"Исходный файл: {os.path.basename(input_file)}\n")
out_file.write(f"Дата создания: {current_time}\n")
out_file.write(f"Статус фильтра: {filter_status}\n")
out_file.write("=" * 60 + "\n\n")
for line in filtered_lines:
out_file.write(line + '\n')
return True, output_file
except FileNotFoundError:
return False, "ndisasm не найден. Убедитесь, что NASM установлен и доступен в PATH"
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.bin *.elf *.o"), ("Все файлы", "*.*"))
)
if not input_file:
return
# Получаем состояние галочки
skip_zeros = skip_zeros_var.get()
success, result = binary_to_disasm(input_file, skip_zeros=skip_zeros)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно дизассемблирован!\n"
f"Пропуск нулей: {'ВКЛ' if skip_zeros else 'ВЫКЛ'}\n"
f"Результат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось дизассемблировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
global skip_zeros_var
root = tk.Tk()
root.title("Дизассемблер бинарных файлов")
root.geometry("500x250")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Переменная для хранения состояния галочки
skip_zeros_var = tk.BooleanVar(value=True) # По умолчанию включено
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Дизассемблер бинарных файлов (ndisasm)",
font=('Arial', 14),
pady=20
)
label.pack()
# Фрейм для галочки
checkbox_frame = tk.Frame(root)
checkbox_frame.pack(pady=10)
# Галочка для пропуска нулей
skip_zeros_check = tk.Checkbutton(
checkbox_frame,
text="Пропускать нули (add [rax],al)",
variable=skip_zeros_var,
font=('Arial', 10)
)
skip_zeros_check.pack()
# Кнопка конвертации
btn_convert = tk.Button(
root,
text="Выбрать файл и дизассемблировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack(pady=10)
# Кнопка выхода
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=10)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,653 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_5.py
КВС - язык программирования
без использования re
(расширенная версия с короткими переходами и полными регистрами)
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
# === Короткие переходы (добавлено) ===
"короткий_переход": {"code": b"\xEB", "type": "short_jmp"},
"короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"},
"короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"},
"короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"},
"короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"},
"короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"},
"короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"},
"короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"},
"короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неоль" : {"code": b"\x75", "type": "short_jcc"},
}
REGISTERS = {
# 64-битные
"раикс": 0, "рсикс": 1, "рдикс": 2, "рбикс": 3,
"рсипи": 4, "рбипи": 5, "рсиай": 6, "рдиай": 7,
"р8": 8, "р9": 9, "р10": 10, "р11": 11,
"р12": 12, "р13": 13, "р14": 14, "р15": 15,
# 32-битные
"еаикс": 0, "есикс": 1, "едикс": 2, "ебикс": 3,
"есипи": 4, "ебипи": 5, "есиай": 6, "едиай": 7,
"р": 8, "р": 9, "р10д": 10, "р11д": 11,
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
# 16-битные
"аикс": 0, "сикс": 1, "дикс": 2, "бикс": 3,
"эсп": 4, "бипи": 5, "эс": 6, "ди": 7,
"р": 8, "р": 9, "р10в": 10, "р11в": 11,
"р12в": 12, "р13в": 13, "р14в": 14, "р15в": 15,
# 8-битные младшие
"ал": 0, "кл": 1, "дл": 2, "бл": 3,
"спл": 4, "бпл": 5, "сил": 6, "дил": 7,
"р8б": 8, "р9б": 9, "р10б": 10, "р11б": 11,
"р12б": 12, "р13б": 13, "р14б": 14, "р15б": 15,
# 8-битные старшие (только первые 4)
"аш": 0, "чш": 1, "дш": 2, "бш": 3,
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000
text_vaddr_base = 0x401000
data_vaddr_base = 0x402000
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]:
return {"size": 16, "index": REGISTERS[reg_name]}
elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]:
return {"size": 32, "index": REGISTERS[reg_name]}
else:
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n " + line_text + "\n" + detail
def error_invalid_operand_count(mnemonic, expected, got):
return "Инструкция '" + mnemonic + "':\n ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got)
def error_unknown_mnemonic(mnemonic):
return "Неизвестная инструкция: '" + mnemonic + "'"
def error_invalid_register(op_name, reg_name, mnemonic):
return ("Инструкция '" + mnemonic + "':\n операнд '" + op_name + "' = '" + reg_name +
"' не является допустимым регистром.\n Допустимые регистры: " + ", ".join(list(REGISTERS.keys())))
def error_invalid_number_format(s):
return "Недопустимый формат числа: '" + s + "'"
def error_unknown_directive(word):
return "Неизвестная директива: '" + word + "'"
def error_missing_label(label):
return "Метка не найдена: '" + label + "'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if pass_num == 2:
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
else:
return labels[operand]
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError("Недопустимый регистр в '" + mnemonic + "'")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
imm = parse_operand(operands[1])
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm))
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель условного перехода слишком далеко: " + str(offset))
code.extend(struct.pack('<i', offset))
# === НОВОЕ: короткие переходы ===
elif itype == "short_jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset))
code.append(0xEB)
code.append(offset & 0xFF)
elif itype == "short_jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset))
code.append(offset & 0xFF)
else:
raise ValueError("Неизвестный тип инструкции: " + itype)
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point
first = tokens[0]
if first[0] != 'word':
raise ValueError("Ожидалось слово, получено: " + str(first))
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(word + " требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError("Недопустимый токен в операндах: " + str(tok))
if pass_num == 2:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = ""
try:
if itype in ("jmp", "jcc", "short_jmp", "short_jcc"):
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
addr = labels[op_str] + (vaddr_text if sec == ".text" else vaddr_data)
target_addr = hex(addr)
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = "{:02X}".format(byte)
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
"short_jmp": 2,
"short_jcc": 2,
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype)
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
log_entries = []
lines = source.split('\n')
# ПРОХОД 1
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print("ПРОХОД 1: text_size=" + str(text_size_pass1) + " bytes, data_size=" + str(data_size_pass1) + " bytes")
# РАЗМЕЩЕНИЕ
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print("РАЗМЕЩЕНИЕ: .text at offset=" + hex(offset_text) + ", .data at offset=" + hex(offset_data))
print("ВИРТУАЛЬНЫЕ АДРЕСА: .text at " + hex(vaddr_text) + ", .data at " + hex(vaddr_data))
print("РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: " + hex(vaddr_data - vaddr_text) + " bytes")
sections[".comment"] = comment_content
# ПРОХОД 2
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print("ПРОХОД 2: text_size=" + str(text_size_pass2) + " bytes, data_size=" + str(data_size_pass2) + " bytes")
if text_size_pass1 != text_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: " + str(text_size_pass1) + " -> " + str(text_size_pass2))
if data_size_pass1 != data_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: " + str(data_size_pass1) + " -> " + str(data_size_pass2))
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
actual_text_size = len(text)
actual_data_size = len(data)
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print("КОРРЕКЦИЯ: vaddr_data исправлен с " + hex(vaddr_data) + " на " + hex(expected_vaddr_data))
vaddr_data = expected_vaddr_data
print("ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text=" + str(actual_text_size) + ", .data=" + str(actual_data_size))
print("КОНЕЧНЫЕ АДРЕСА: .text ends at " + hex(vaddr_text + actual_text_size) + ", .data starts at " + hex(vaddr_data))
print("ПРОВЕРКА ПЕРЕКРЫТИЯ: " + ("ПЕРЕКРЫТИЕ!" if (vaddr_text + actual_text_size) > vaddr_data else "OK"))
entry_addr = vaddr_text + labels.get(entry_point, 0)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2,
0x3e,
1,
entry_addr,
elf_header_size,
shdr_offset,
0,
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(1, 4, 0, elf_base_vaddr, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text), align_up(len(text), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data), align_up(len(data), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
with open(filename, "wb") as f:
f.write(elf_data)
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_4.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
log_file = source_file[:-4] + ".log.csv"
with open(log_file, "w", encoding="utf-8") as f_log:
f_log.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(addr + ";" + byte + ";" + target + ";" + cmd + "\n")
print("Лог создан: " + log_file)
print("ELF-файл создан: " + elf_file)
print("Запустить: ./" + elf_file)
except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)

View file

@ -1,930 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
kvs_6.py
КВС - язык программирования
без использования re
(расширенная версия с короткими переходами и полными регистрами)
"""
import sys
import struct
# Глобальная переменная для имени исходного файла
source_filename = ""
def safe_close(f):
if f is not None:
f.close()
# === Глобальные данные ===
INSTRUCTIONS = {
"переместить_имм": {"code": None, "type": "reg_imm"},
"сравнить_с": {"code": None, "type": "cmp_reg_imm"},
"переход_если_неравно": {"code": b"\x0F\x85", "type": "jcc"},
"вызов_системы": {"code": b"\x0F\x05", "type": "none"},
"переход": {"code": b"\xE9", "type": "jmp"},
# === Короткие переходы ===
"короткий_переход": {"code": b"\xEB", "type": "short_jmp"},
"короткий_переход_если_равно" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неравно" : {"code": b"\x75", "type": "short_jcc"},
"короткий_переход_если_меньше" : {"code": b"\x7C", "type": "short_jcc"},
"короткий_переход_если_больше" : {"code": b"\x7F", "type": "short_jcc"},
"короткий_переход_если_меньше_или_равно": {"code": b"\x7E", "type": "short_jcc"},
"короткий_переход_если_больше_или_равно": {"code": b"\x7D", "type": "short_jcc"},
"короткий_переход_если_перенос" : {"code": b"\x72", "type": "short_jcc"},
"короткий_переход_если_нет_переноса" : {"code": b"\x73", "type": "short_jcc"},
"короткий_переход_если_ноль" : {"code": b"\x74", "type": "short_jcc"},
"короткий_переход_если_неоль" : {"code": b"\x75", "type": "short_jcc"},
# === Новые команды для флагов ===
"сравнить": {"code": b"\x48\x39", "type": "cmp_reg_reg"},
"проверить": {"code": b"\x48\x85", "type": "test_reg_reg"},
"вычесть": {"code": b"\x48\x29", "type": "sub_reg_reg"},
"прибавить": {"code": b"\x48\x01", "type": "add_reg_reg"},
"увеличить": {"code": b"\xFF", "subop": 0, "type": "incdec"},
"уменьшить": {"code": b"\xFF", "subop": 1, "type": "incdec"},
}
REGISTERS = {
# 64-битные
"раикс": 0, "рсикс": 1, "рдикс": 2, "рбикс": 3,
"рсипи": 4, "рбипи": 5, "рсиай": 6, "рдиай": 7,
"р8": 8, "р9": 9, "р10": 10, "р11": 11,
"р12": 12, "р13": 13, "р14": 14, "р15": 15,
# 32-битные
"еаикс": 0, "есикс": 1, "едикс": 2, "ебикс": 3,
"есипи": 4, "ебипи": 5, "есиай": 6, "едиай": 7,
"р": 8, "р": 9, "р10д": 10, "р11д": 11,
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
# 16-битные
"аикс": 0, "сикс": 1, "дикс": 2, "бикс": 3,
"эсп": 4, "бипи": 5, "эс": 6, "ди": 7,
"р": 8, "р": 9, "р10в": 10, "р11в": 11,
"р12в": 12, "р13в": 13, "р14в": 14, "р15в": 15,
# 8-битные младшие
"ал": 0, "кл": 1, "дл": 2, "бл": 3,
"спл": 4, "бпл": 5, "сил": 6, "дил": 7,
"р8б": 8, "р9б": 9, "р10б": 10, "р11б": 11,
"р12б": 12, "р13б": 13, "р14б": 14, "р15б": 15,
# 8-битные старшие (только первые 4)
"аш": 0, "чш": 1, "дш": 2, "бш": 3,
}
# Глобальное состояние ассемблера
labels = {}
label_sections = {}
symbols = {}
sections = {".text": bytearray(), ".data": bytearray()}
current_section = ".text"
entry_point = "_start"
position = {".text": 0, ".data": 0}
pass_num = 0
# === Для логирования ===
log_entries = []
# === ELF layout ===
PAGE_SIZE = 0x1000
elf_base_vaddr = 0x400000
text_vaddr_base = 0x401000
data_vaddr_base = 0x402000
text_size = 0
data_size = 0
comment_size = 0
offset_text = 0
offset_data = 0
offset_comment = 0
shstrtab_offset = 0
shdr_offset = 0
vaddr_text = 0
vaddr_data = 0
def align_up(x, align):
return (x + align - 1) & ~(align - 1)
def get_reg_info(reg_name):
if reg_name in ["аш", "чш", "дш", "бш"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": True}
elif reg_name.endswith(("б", "л")) or reg_name in ["ал", "кл", "дл", "бл", "спл", "бпл", "сил", "дил"]:
return {"size": 8, "index": REGISTERS[reg_name], "high8": False}
elif reg_name.endswith("в") or reg_name in ["аикс", "сикс", "дикс", "бикс", "эсп", "бипи", "эс", "ди"]:
return {"size": 16, "index": REGISTERS[reg_name]}
elif reg_name.endswith("д") or reg_name in ["еаикс", "есикс", "едикс", "ебикс", "есипи", "ебипи", "есиай", "едиай"]:
return {"size": 32, "index": REGISTERS[reg_name]}
else:
return {"size": 64, "index": REGISTERS[reg_name]}
# === Функции формирования ошибок ===
def make_error_msg(line_num, line_text, detail):
return "Ошибка в файле " + source_filename + ", строка " + str(line_num) + ":\n " + line_text + "\n" + detail
def error_invalid_operand_count(mnemonic, expected, got):
return "Инструкция '" + mnemonic + "':\n ожидается " + str(expected) + " операнд(а/ов), получено: " + str(got)
def error_unknown_mnemonic(mnemonic):
return "Неизвестная инструкция: '" + mnemonic + "'"
def error_invalid_register(op_name, reg_name, mnemonic):
return ("Инструкция '" + mnemonic + "':\n операнд '" + op_name + "' = '" + reg_name +
"' не является допустимым регистром.\n Допустимые регистры: " + ", ".join(list(REGISTERS.keys())))
def error_invalid_number_format(s):
return "Недопустимый формат числа: '" + s + "'"
def error_unknown_directive(word):
return "Неизвестная директива: '" + word + "'"
def error_missing_label(label):
return "Метка не найдена: '" + label + "'"
def error_unexpected_string_in_byte():
return ".байт требует числовые значения, не строки"
def error_unterminated_string():
return "Незакрытая кавычка в строке"
# === Лексер ===
def tokenize_line(line):
semi = line.find(';')
if semi != -1:
line = line[:semi]
line = line.rstrip()
if not line:
return []
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:
i += 1
esc = line[i]
if esc == 'n':
s += '\n'
elif esc == 't':
s += '\t'
elif esc == '"':
s += '"'
elif esc == '\\':
s += '\\'
else:
s += '\\' + esc
i += 1
else:
s += line[i]
i += 1
if i >= n:
raise ValueError(error_unterminated_string())
i += 1
tokens.append(('string', s))
continue
if ch == ',':
tokens.append(('comma', ','))
i += 1
continue
if ch == ':':
tokens.append(('colon', ':'))
i += 1
continue
j = i
while j < n and not (line[j].isspace() or line[j] in ',:;'):
j += 1
word = line[i:j]
if word:
tokens.append(('word', word))
i = j
return tokens
# === Парсинг ===
def parse_operand(operand):
if operand.isdigit():
return int(operand)
if operand.startswith("0x"):
return int(operand, 16)
if operand in labels:
section = label_sections[operand]
if pass_num == 2:
base = vaddr_text if section == ".text" else vaddr_data
return labels[operand] + base
else:
return labels[operand]
if operand in symbols:
return symbols[operand]
raise ValueError("Неизвестный операнд: " + operand)
def encode_instruction(mnemonic, operands, line_num, line_text):
instr = INSTRUCTIONS[mnemonic]
code = bytearray()
itype = instr["type"]
if itype == "none":
code.extend(instr["code"])
elif itype == "reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
if size == 64:
if 0 <= reg <= 7:
code.extend(b'\x48')
code.append(0xB8 + reg)
code.extend(struct.pack('<Q', imm))
elif 8 <= reg <= 15:
code.extend(b'\x49')
code.append(0xB8 + (reg - 8))
code.extend(struct.pack('<Q', imm))
else:
raise ValueError("Недопустимый регистр в '" + mnemonic + "'")
elif size == 32:
if -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное значение вне диапазона")
elif size == 16:
if 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xC7)
modrm = 0xC0 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0xB0 + reg + 4)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xB0 + reg)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xB0 + (reg & 7))
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "cmp_reg_imm":
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
imm = parse_operand(operands[1])
if size == 64:
if -128 <= imm <= 127:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<i', imm))
else:
raise ValueError("Непосредственное значение слишком велико для сравнения: " + str(imm))
elif size == 32:
if -128 <= imm <= 127:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif -0x80000000 <= imm <= 0x7FFFFFFF:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.extend(struct.pack('<I', imm & 0xFFFFFFFF))
else:
raise ValueError("32-битное непосредственное значение вне диапазона")
elif size == 16:
if -128 <= imm <= 127:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x83)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
elif 0 <= imm <= 0xFFFF:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0x81)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
code.append((imm >> 8) & 0xFF)
else:
raise ValueError("16-битное непосредственное значение вне диапазона")
elif size == 8:
if not (0 <= imm <= 255):
raise ValueError("8-битное значение вне диапазона 0255")
if reg_info.get("high8"):
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0x80)
modrm = 0xF8 | reg
code.append(modrm)
code.append(imm & 0xFF)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0x80)
modrm = 0xF8 | (reg & 7)
code.append(modrm)
code.append(imm & 0xFF)
else:
raise ValueError("Неподдерживаемый размер регистра")
elif itype == "jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 5)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель слишком далеко для 32-битного смещения в инструкции '" + mnemonic + "': " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 6)
if offset < -0x80000000 or offset > 0x7FFFFFFF:
raise ValueError("Цель условного перехода слишком далеко: " + str(offset))
code.extend(struct.pack('<i', offset))
elif itype == "short_jmp":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого перехода: " + str(offset))
code.append(0xEB)
code.append(offset & 0xFF)
elif itype == "short_jcc":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
code.extend(instr["code"])
target = parse_operand(operands[0])
current_addr = vaddr_text + position[".text"]
offset = target - (current_addr + 2)
if offset < -128 or offset > 127:
raise ValueError("Цель слишком далеко для короткого условного перехода: " + str(offset))
code.append(offset & 0xFF)
elif itype in ("cmp_reg_reg", "test_reg_reg", "add_reg_reg", "sub_reg_reg"):
if len(operands) != 2:
raise ValueError(error_invalid_operand_count(mnemonic, 2, len(operands)))
dst_info = get_reg_info(operands[0])
src_info = get_reg_info(operands[1])
if dst_info is None:
raise ValueError(error_invalid_register("назначения", operands[0], mnemonic))
if src_info is None:
raise ValueError(error_invalid_register("источника", operands[1], mnemonic))
dst = dst_info["index"]
src = src_info["index"]
size = dst_info["size"]
if size != src_info["size"]:
raise ValueError("Размеры операндов не совпадают в инструкции '" + mnemonic + "'")
use_66 = (size == 16)
use_rex_w = (size == 64)
rex = 0x40
if use_rex_w:
rex |= 0x08
if src >= 8:
rex |= 0x04
if dst >= 8:
rex |= 0x01
if dst_info.get("high8") or src_info.get("high8"):
if rex != 0x40:
raise ValueError("Старшие байты несовместимы с REX")
rex = 0
op_map = {
"cmp_reg_reg": 0x39,
"test_reg_reg": 0x85,
"add_reg_reg": 0x01,
"sub_reg_reg": 0x29,
}
op = op_map[itype]
if use_66:
code.append(0x66)
if rex != 0x40 or use_rex_w:
code.append(rex)
code.append(op)
modrm = 0xC0 | ((src & 7) << 3) | (dst & 7)
code.append(modrm)
elif itype == "incdec":
if len(operands) != 1:
raise ValueError(error_invalid_operand_count(mnemonic, 1, len(operands)))
reg_info = get_reg_info(operands[0])
if reg_info is None:
raise ValueError(error_invalid_register("регистра", operands[0], mnemonic))
reg = reg_info["index"]
size = reg_info["size"]
subop = instr["subop"]
if size == 64:
rex = 0x48
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFF)
elif size == 32:
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 16:
code.append(0x66)
rex = 0x40
if reg >= 8:
rex |= 0x01
if rex != 0x40:
code.append(rex)
code.append(0xFF)
elif size == 8:
if reg_info.get("high8"):
if reg < 4:
code.append(0xFE)
else:
raise ValueError("Старшие байты только для первых 4 регистров")
else:
if reg < 4:
code.append(0xFE)
else:
rex = 0x40
if reg >= 8:
rex |= 0x01
code.append(rex)
code.append(0xFE)
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
return bytes(code)
else:
raise ValueError("Неподдерживаемый размер регистра")
modrm = 0xC0 | (subop << 3) | (reg & 7)
code.append(modrm)
else:
raise ValueError("Неизвестный тип инструкции: " + itype)
return bytes(code)
def parse_instruction_or_directive(tokens, line_num, line_text):
global current_section, entry_point, symbols # Не забудьте добавить symbols в global
first = tokens[0]
if first[0] != 'word':
raise ValueError("Ожидалось слово, получено: " + str(first))
word = first[1]
if word.startswith('.'):
if word == '.текст':
current_section = ".text"
elif word == '.данные':
current_section = ".data"
elif word == '.глобал':
if len(tokens) < 2 or tokens[1][0] != 'word':
raise ValueError(".глобал требует имя метки")
entry_point = tokens[1][1]
elif word == '.строкауль' or word == '.строка':
if len(tokens) < 2 or tokens[1][0] != 'string':
raise ValueError(word + " требует строку в кавычках")
if current_section != ".data":
raise ValueError("Строки разрешены только в секции .data")
s = tokens[1][1]
bstring = s.encode('utf-8')
add_null = (word == '.строкауль')
size = len(bstring) + (1 if add_null else 0)
if pass_num == 1:
position[".data"] += size
elif pass_num == 2:
sections[".data"] += bstring
if add_null:
sections[".data"] += b'\x00'
position[".data"] += size
# === НОВАЯ ДИРЕКТИВА .КОНСТАНТА ===
elif word == '.константа':
if len(tokens) < 3 or tokens[1][0] != 'word' or tokens[2][0] != 'word' or tokens[2][1] != '=' or len(tokens) < 4 or tokens[3][0] not in ('word', 'string'): # Проверяем формат: .константа имя = значение
raise ValueError(".константа требует формат: .константа <имя> = <значение>")
const_name = tokens[1][1]
const_value_str = tokens[3][1]
try:
# Попробуем распарсить значение как число или символ (если это метка)
const_value = parse_operand(const_value_str)
except ValueError:
raise ValueError("Невозможно распознать значение константы '" + const_value_str + "'")
# Записываем в глобальный словарь символов
symbols[const_name] = const_value
# Директива .константа не добавляет байты в секцию
return
# === КОНЕЦ НОВОЙ ДИРЕКТИВЫ ===
else:
raise ValueError(error_unknown_directive(word))
return
mnemonic = word
if mnemonic not in INSTRUCTIONS:
raise ValueError(error_unknown_mnemonic(mnemonic))
operands = []
for tok in tokens[1:]:
if tok[0] == 'word':
operands.append(tok[1])
elif tok[0] == 'comma':
continue
else:
raise ValueError("Недопустимый токен в операндах: " + str(tok))
if pass_num == 2:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
code = encode_instruction(mnemonic, operands, line_num, line_text)
start_pos = position[current_section]
if current_section == ".text":
start_vaddr = vaddr_text + start_pos
target_addr = ""
try:
if itype in ("jmp", "jcc", "short_jmp", "short_jcc"):
target = parse_operand(operands[0])
target_addr = hex(target)
elif itype == "reg_imm":
op_str = operands[1]
if op_str in labels:
sec = label_sections[op_str]
addr = labels[op_str] + (vaddr_text if sec == ".text" else vaddr_data)
target_addr = hex(addr)
elif op_str in symbols: # Проверяем символы
target_addr = hex(symbols[op_str])
except Exception:
target_addr = "ошибка"
original_cmd = line_text.split(';')[0].strip()
for i, byte in enumerate(code):
addr = start_vaddr + i
byte_hex = "{:02X}".format(byte)
cmd_to_log = original_cmd if i == 0 else ""
log_entries.append((hex(addr), byte_hex, target_addr if i == 0 else "", cmd_to_log))
sections[current_section] += code
position[current_section] += len(code)
else:
instr_info = INSTRUCTIONS[mnemonic]
itype = instr_info["type"]
size_map = {
"reg_imm": 10,
"cmp_reg_imm": 7,
"jmp": 5,
"jcc": 6,
"none": lambda: len(instr_info["code"]),
"short_jmp": 2,
"short_jcc": 2,
"cmp_reg_reg": 3,
"test_reg_reg": 3,
"add_reg_reg": 3,
"sub_reg_reg": 3,
"incdec": 3,
}
if itype in size_map:
sz = size_map[itype]
position[current_section] += sz() if callable(sz) else sz
else:
raise ValueError("Неподдерживаемый тип инструкции при подсчёте размера: " + itype)
def parse_tokens(tokens, line_num, line_text):
if not tokens:
return
if len(tokens) >= 2 and tokens[0][0] == 'word' and tokens[1][0] == 'colon':
label = tokens[0][1]
labels[label] = position[current_section]
label_sections[label] = current_section
rest = tokens[2:]
if rest:
parse_instruction_or_directive(rest, line_num, line_text)
return
parse_instruction_or_directive(tokens, line_num, line_text)
def parse(source):
global pass_num, text_size, data_size, comment_size
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
global vaddr_text, vaddr_data
global sections, position, log_entries
log_entries = []
lines = source.split('\n')
# ПРОХОД 1
pass_num = 1
position[".text"] = 0
position[".data"] = 0
labels.clear()
label_sections.clear()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass1 = position[".text"]
data_size_pass1 = position[".data"]
print("ПРОХОД 1: text_size=" + str(text_size_pass1) + " bytes, data_size=" + str(data_size_pass1) + " bytes")
# РАЗМЕЩЕНИЕ
elf_header_size = 64
ph_size = 56
ph_num = 3
ph_table_size = ph_num * ph_size
offset_text = align_up(elf_header_size + ph_table_size, PAGE_SIZE)
offset_data = align_up(offset_text + text_size_pass1, PAGE_SIZE)
vaddr_text = text_vaddr_base
vaddr_data = align_up(vaddr_text + text_size_pass1, PAGE_SIZE)
comment_content = "Сборщик КВС".encode('utf-8') + b'\x00'
comment_size = len(comment_content)
offset_comment = align_up(offset_data + data_size_pass1, 1)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
shstrtab_offset = align_up(offset_comment + comment_size, 8)
shdr_size = 64
shdr_num = 5
shdr_offset = align_up(shstrtab_offset + shstrtab_size, 16)
print("РАЗМЕЩЕНИЕ: .text at offset=" + hex(offset_text) + ", .data at offset=" + hex(offset_data))
print("ВИРТУАЛЬНЫЕ АДРЕСА: .text at " + hex(vaddr_text) + ", .data at " + hex(vaddr_data))
print("РАССТОЯНИЕ МЕЖДУ СЕКЦИЯМИ: " + hex(vaddr_data - vaddr_text) + " bytes")
sections[".comment"] = comment_content
# ПРОХОД 2
pass_num = 2
position[".text"] = 0
position[".data"] = 0
sections[".text"] = bytearray()
sections[".data"] = bytearray()
for line_num, line in enumerate(lines, start=1):
original_line = line.rstrip()
try:
tokens = tokenize_line(line)
parse_tokens(tokens, line_num, original_line)
except Exception as e:
error_full = make_error_msg(line_num, original_line, str(e))
print(error_full, file=sys.stderr)
sys.exit(1)
text_size_pass2 = len(sections[".text"])
data_size_pass2 = len(sections[".data"])
print("ПРОХОД 2: text_size=" + str(text_size_pass2) + " bytes, data_size=" + str(data_size_pass2) + " bytes")
if text_size_pass1 != text_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .text изменился между проходами: " + str(text_size_pass1) + " -> " + str(text_size_pass2))
if data_size_pass1 != data_size_pass2:
print("ПРЕДУПРЕЖДЕНИЕ: Размер .data изменился между проходами: " + str(data_size_pass1) + " -> " + str(data_size_pass2))
text_size = text_size_pass2
data_size = data_size_pass2
def create_elf(filename):
global sections, entry_point, labels, vaddr_text, vaddr_data
global offset_text, offset_data, offset_comment, shstrtab_offset, shdr_offset
text = sections[".text"]
data = sections[".data"]
comment = sections[".comment"]
actual_text_size = len(text)
actual_data_size = len(data)
expected_vaddr_data = align_up(vaddr_text + actual_text_size, PAGE_SIZE)
if vaddr_data != expected_vaddr_data:
print("КОРРЕКЦИЯ: vaddr_data исправлен с " + hex(vaddr_data) + " на " + hex(expected_vaddr_data))
vaddr_data = expected_vaddr_data
print("ФАКТИЧЕСКИЕ РАЗМЕРЫ: .text=" + str(actual_text_size) + ", .data=" + str(actual_data_size))
print("КОНЕЧНЫЕ АДРЕСА: .text ends at " + hex(vaddr_text + actual_text_size) + ", .data starts at " + hex(vaddr_data))
print("ПРОВЕРКА ПЕРЕКРЫТИЯ: " + ("ПЕРЕКРЫТИЕ!" if (vaddr_text + actual_text_size) > vaddr_data else "OK"))
entry_addr = vaddr_text + labels.get(entry_point, 0)
shstrtab_content = b"\x00.text\x00.data\x00.comment\x00.shstrtab\x00"
shstrtab_size = len(shstrtab_content)
elf_header_size = 64
ph_size = 56
ph_num = 3
shdr_size = 64
shdr_num = 5
file_size = shdr_offset + shdr_num * shdr_size
elf_data = bytearray(file_size)
# ELF header
e_ident = b"\x7fELF\x02\x01\x01\x00" + b"\x00" * 8
elf_header = struct.pack(
'<16sHHIQQQIHHHHHH',
e_ident,
2,
0x3e,
1,
entry_addr,
elf_header_size,
shdr_offset,
0,
elf_header_size,
ph_size,
ph_num,
shdr_size,
shdr_num,
4
)
elf_data[0:64] = elf_header
def phdr(p_type, p_flags, p_offset, p_vaddr, p_filesz, p_memsz):
return struct.pack('<IIQQQQQQ',
p_type, p_flags,
p_offset, p_vaddr, p_vaddr,
p_filesz, p_memsz,
PAGE_SIZE
)
ph0 = phdr(1, 4, 0, elf_base_vaddr, elf_header_size + ph_num * ph_size, PAGE_SIZE)
ph1 = phdr(1, 5, offset_text, vaddr_text, len(text), align_up(len(text), PAGE_SIZE))
ph2 = phdr(1, 6, offset_data, vaddr_data, len(data), align_up(len(data), PAGE_SIZE))
phdrs = ph0 + ph1 + ph2
elf_data[elf_header_size : elf_header_size + len(phdrs)] = phdrs
elf_data[offset_text : offset_text + len(text)] = text
elf_data[offset_data : offset_data + len(data)] = data
elf_data[offset_comment : offset_comment + len(comment)] = comment
elf_data[shstrtab_offset : shstrtab_offset + shstrtab_size] = shstrtab_content
def shdr(name_idx, sh_type, flags, addr, offset, size, addralign=1):
return struct.pack('<IIQQQQIIQQ',
name_idx, sh_type, flags, addr, offset, size,
0, 0, addralign, 0
)
sh0 = shdr(0, 0, 0, 0, 0, 0)
sh1 = shdr(1, 1, 6, vaddr_text, offset_text, len(text), 16)
sh2 = shdr(7, 1, 3, vaddr_data, offset_data, len(data), 8)
sh3 = shdr(13, 1, 0, 0, offset_comment, len(comment), 1)
sh4 = shdr(22, 3, 0, 0, shstrtab_offset, shstrtab_size, 1)
shdrs = sh0 + sh1 + sh2 + sh3 + sh4
elf_data[shdr_offset : shdr_offset + len(shdrs)] = shdrs
f_out = open(filename, "wb")
f_out.write(elf_data)
f_out.close()
import os
os.chmod(filename, 0o755)
# === Основной запуск ===
if len(sys.argv) != 2:
print("Использование: python kvs_5.py <файл.квс>")
sys.exit(1)
source_file = sys.argv[1]
source_filename = source_file
if not source_file.endswith('.квс'):
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
f_in = None
try:
f_in = open(source_file, "r", encoding="utf-8")
source = f_in.read()
f_in.close()
except FileNotFoundError:
safe_close(f_in)
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
except UnicodeDecodeError as e:
safe_close(f_in)
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
try:
parse(source)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)
log_file = source_file[:-4] + ".log.csv"
f_log = open(log_file, "w", encoding="utf-8")
f_log.write("адрес;байт;целевой_адрессходная_команда\n")
for addr, byte, target, cmd in log_entries:
if cmd and ('"' in cmd or ';' in cmd):
cmd = '"' + cmd.replace('"', '""') + '"'
f_log.write(addr + ";" + byte + ";" + target + ";" + cmd + "\n")
f_log.close()
print("Лог создан: " + log_file)
print("ELF-файл создан: " + elf_file)
print("Запустить: ./" + elf_file)
except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)

View file

@ -1,33 +0,0 @@
.текст
.глобал _start
_start:
; Устанавливаем rax в 1 (не ноль)
переместить_имм раикс, 1
; jnz должен сработать, прыгая к print
короткий_переход_если_неоль print_msg
; Мертвый код - НЕ должен выполниться
переместить_имм раикс, 1 ; write syscall
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, dead_msg
переместить_имм рдикс, len_dead_msg
вызов_системы
print_msg:
переместить_имм раикс, 1 ; write syscall
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, alive_msg
переместить_имм рдикс, len_alive_msg
вызов_системы
; exit(0)
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
dead_msg: .строкауль "Это НЕ должно напечататься."
.константа len_dead_msg = 28
alive_msg: .строкауль "Это ДОЛЖНО напечататься."
.константа len_alive_msg = 46

View file

@ -1,35 +0,0 @@
.текст
.глобал _start
_start:
; Инициализация: raikc = 0 → пропустить первую строку
; Попробуйте изменить на 1 — тогда обе строки напечатаются
переместить_имм раикс, 1
сравнить_с раикс, 0 ; ← обязательно!
; Если raikc == 0 → прыгаем к выводу второй строки
короткий_переход_если_ноль пропустить_первую
; Вывод первой строки
переместить_имм раикс, 1 ; write
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, msg0 ; адрес строки
переместить_имм рдикс, 21 ; длина "Привет мир!\n" = 13 байт
вызов_системы
пропустить_первую:
; Всегда выводим вторую строку
переместить_имм раикс, 1 ; write
переместить_имм рдиай, 1 ; stdout
переместить_имм рсиай, msg1 ; адрес строки
переместить_имм рдикс, 21 ; длина "Поди прочь!\n" = 13 байт
вызов_системы
; Завершение
переместить_имм раикс, 60 ; exit
переместить_имм рдиай, 0 ; код возврата
вызов_системы
.данные
msg0: .строкауль "Привет мир!\n"
msg1: .строкауль "Поди прочь!\n"

View file

@ -1,53 +0,0 @@
; сложный тест0.квс
; Тест: один переход и один мусорный сегмент
.текст
.глобал _start
_start:
; Инициализация регистра
переместить_имм раикс, 10 ; rax = 10 (не ноль)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'вывести_сообщение', пропуская мертвый_код_1
короткий_переход_если_неоль вывести_сообщение
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
; --- КОД ОШИБКИ (на всякий случай) ---
; Если мы сюда попали, значит переход НЕ сработал (ошибка)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_fail
переместить_имм рдикс, len_msg_fail
вызов_системы
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14
msg_fail: .строкауль "Ошибка: переход не сработал!"
.константа len_msg_fail = 28

View file

@ -1,131 +0,0 @@
; сложный_тест1.квс
; Тест: два перехода и два мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10 (не ноль)
переместить_имм рбикс, 5 ; rbx = 5 (для сравнения)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Нам нужно ПРЫГНУТЬ через мертвый код, если ZF НЕ 1 (т.е. ZF = 0).
; У нас нет инструкции 'короткий_переход_если_не_равно' (jne), но она эквивалентна 'короткий_переход_если_неоль' (jnz) для флага ZF.
; Однако, у нас есть 'короткий_переход_если_равно' (je), который сработает при ZF=1.
; Мы хотим, чтобы переход НЕ срабатывал при ZF=0.
; Поэтому мы используем 'короткий_переход_если_равно' и прыгаем к метке ПОСЛЕ мертвого кода, если ZF=1.
; Но ZF=0, поэтому переход НЕ сработает, и выполнение пойдёт на следующую инструкцию - в мертвый код.
; Нам нужно наоборот: прыгнуть, если ZF=0.
; Используем безусловный переход ПОСЛЕ мертвого кода, чтобы обойти его, если ZF=0.
; Правильная логика:
; cmp ...
; je мимоертвогоода_2 (переход, если ZF=1)
; [мертвый код 2]
; мимоертвогоода_2:
; [продолжение кода]
; Итак, мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0 (т.е. если НЕ равны).
; У нас есть только 'je'. Значит, мы прыгаем МИМО, если ZF=1 (равны).
; Мы НЕ хотим прыгать мимо, если ZF=0.
; Поэтому: cmp, je (прыгнуть мимо, если ZF=1), [мертвый код], [метка мимо], [остальной код]
; Это означает: если ZF=1 -> прыгаем мимо. Если ZF=0 -> выполняем мертвый код.
; Это НЕ то, что нам нужно.
; Нам нужно: если ZF=0 -> прыгаем мимо. Если ZF=1 -> выполняем мертвый код (или идём дальше в него).
; У нас нет jne/jnz. Используем логику: если НЕ (ZF=1), то прыгаем.
; Или: используем безусловный переход после мертвого кода, но перед ним поставим je на метку ПОСЛЕ безусловного перехода.
; Это хитро, но работает:
; cmp rax, rbx
; je метка_после_безусловного (ZF=1 -> прыгаем, обходим безусловный переход)
; короткий_переход мимоертвогоода_2 (ZF=0 -> прыгаем через мертвый код)
; метка_после_безусловного:
; [мертвый код 2 - выполнится, если ZF=1]
; мимоертвогоода_2:
; [продолжение]
; Но мы хотим, чтобы мертвый код НЕ выполнялся, если ZF=0.
; cmp rax, rbx (ZF = 0)
; je метка_после_безусловного (ZF=0, переход НЕ сработает)
; короткий_переход мимоертвогоода_2 (ZF=0, переход СРАБОТАЕТ, прыгаем мимо)
; метка_после_безусловного: [попадаем сюда, если ZF=1]
; [мертвый код 2 - выполнится, если ZF=1]
; мимоертвогоода_2: [попадаем сюда, если ZF=0 (прыгнули) или если ZF=1 (прошли через мертвый код)]
; [продолжение кода]
; Это работает! Если ZF=0 (не равны), мы прыгаем мимо мертвого кода 2.
; Если ZF=1 (равны), мы пропускаем безусловный переход и попадаем в мертвый код 2.
; Нам нужно наоборот: прыгать мимо, если ZF=0 (не равны).
; Значит, если cmp показывает НЕ равны (ZF=0), мы хотим прыгнуть.
; У нас нет jne. Но мы можем использовать логику: if (ZF == 0) jmp; (псевдокод).
; Это можно выразить как: if (ZF == 1) goto skip_jmp; jmp target; skip_jmp: ... (псевдокод).
; Это и есть: cmp ...; je skip_jmp; jmp target; skip_jmp: ...
; Где 'jmp target' - это прыжок, который происходит, если ZF=0 (т.е. когда je НЕ сработал).
; Итак:
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0, переход НЕ сработает -> идем дальше)
; короткий_переход мимоертвогоода_2 (ZF=0, СРАБОТАЕТ -> прыгаем мимо мертвого кода)
; skip_jmp: (ZF=1, мы сюда попали, прыгнув через безусловный переход)
; [мертвый код 2 - выполнится ТОЛЬКО если ZF=1]
; мимоертвогоода_2:
; [продолжение]
; Это как раз то, что нужно!
; cmp rax, rbx (ZF=0)
; je skip_jmp (ZF=0 -> не прыгаем)
; короткий_переход мимоертвогоода_2 (ZF=0 -> прыгаем мимо мертвого кода 2)
; skip_jmp: (ZF=1 -> сюда попали, прыгнув через безусловный переход)
сравнить раикс, рбикс
короткий_переход_если_равно skip_jmp
короткий_переход мимоертвогоода_2
skip_jmp:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13 ; Исправлено на длину "Успех!\n" (10 байт + 1 \n)
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 14
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 14

View file

@ -1,93 +0,0 @@
; сложный_тест2.квс
; Тест: три перехода и три мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 3!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 37
; "Мертвый код 2!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 37
; "Мертвый код 1!" = 12 букв * 3 байта + 1 (!) = 36 + 1 = 37 байт. len = 37
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 37

View file

@ -1,114 +0,0 @@
; сложный_тест3.квс
; Тест: четыре перехода и четыре мусорных сегмента
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,137 +0,0 @@
; сложный_тест4.квс
; Тест: пять переходов и пять мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,157 +0,0 @@
; сложный_тест5.квс
; Тест: шесть переходов и шесть мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,180 +0,0 @@
; сложный_тест6.квс
; Тест: семь переходов и семь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,200 +0,0 @@
; сложный_тест7.квс
; Тест: восемь переходов и восемь мусорных сегментов
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,217 +0,0 @@
; сложный_тест8.квс
; Тест: девять переходов и девять мусорных сегментов (включая короткий безусловный)
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ВТОРОЙ ПЕРЕХОД: короткий_переход_если_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> ZF=0
сравнить раикс, рбикс
; ZF=0, значит je НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 2, если ZF=0.
; Используем: je skip_jmp_2 (переход НЕ сработает); jmp мимоертвогоода_2 (переход СРАБОТАЕТ)
короткий_переход_если_равно skip_jmp_2
короткий_переход мимоертвогоода_2
skip_jmp_2:
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться (так как ZF=0)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мимоертвогоода_2:
; ТРЕТИЙ ПЕРЕХОД: короткий_переход_если_меньше (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, SF=0, OF=0
сравнить раикс, рбикс
; SF=0, OF=0. Условие jl: SF != OF (0 != 0 - ложь). jl НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 3, если jl НЕ сработает (т.е. если rax >= rbx).
; Используем: jl skip_jmp_3 (переход НЕ сработает); jmp мимоертвогоода_3 (переход СРАБОТАЕТ)
короткий_переход_если_меньше skip_jmp_3
короткий_переход мимоертвогоода_3
skip_jmp_3:
; --- МЕРТВЫЙ КОД 3 ---
; Этот код НЕ должен выполниться (так как jl не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_3
переместить_имм рдикс, len_msg_dead_3
вызов_системы
; ---------------------
мимоертвогоода_3:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ПЯТЫЙ ПЕРЕХОД: короткий_переход_если_меньше_или_равно (и безусловный)
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jle: ZF=1 ИЛИ SF != OF. ZF=0 (ложь), SF != OF (0 != 0, ложь). jle НЕ сработает.
; Мы хотим прыгнуть МИМО мертвого кода 5, если jle НЕ сработает (т.е. если rax > rbx).
; Используем: jle skip_jmp_5 (переход НЕ сработает); jmp мимоертвогоода_5 (переход СРАБОТАЕТ)
короткий_переход_если_меньше_или_равно skip_jmp_5
короткий_переход мимоертвогоода_5
skip_jmp_5:
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться (так как jle не сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
мимоертвогоода_5:
; ШЕСТОЙ ПЕРЕХОД: короткий_переход_если_больше_или_равно
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jge: SF=OF. SF=0, OF=0, SF=OF (0=0). jge СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 6, если jge сработает (т.е. если rax >= rbx).
короткий_переход_если_больше_или_равно мимоертвогоода_6
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться (так как jge сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
мимоертвогоода_6:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 7, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_7
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
мимоертвогоода_7:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; ДЕВЯТЫЙ ПЕРЕХОД: короткий_переход (безусловный)
; Просто прыгаем мимо мертвого кода 9.
короткий_переход мимоертвогоода_9
; --- МЕРТВЫЙ КОД 9 ---
; Этот код НЕ должен выполниться (так как jmp сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_9
переместить_имм рдикс, len_msg_dead_9
вызов_системы
; ---------------------
мимоертвогоода_9:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 9!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_9: .строкауль "Мертвый код 9!"
.константа len_msg_dead_9 = 18
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,204 +0,0 @@
; сложный_тест9.квс
; Тест: запутанные переходы, крякмис
.текст
.глобал _start
_start:
; Инициализация регистров
переместить_имм раикс, 10 ; rax = 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм рсикс, 10 ; rcx = 10 (для проверки ZF в jz)
; ПЕРВЫЙ ПЕРЕХОД: короткий_переход_если_неоль
; ZF=0 (так как rax=10), значит jnz сработает
; Прыгаем к метке 'проверка_2', пропуская мертвый_код_1
короткий_переход_если_неоль проверка_2
; --- МЕРТВЫЙ КОД 1 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_1
переместить_имм рдикс, len_msg_dead_1
вызов_системы
; ---------------------
проверка_2:
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 1: "Два шага вперед" - короткий_переход через мертвый_код_2 и мертвый_код_3
; Прыгаем к проверка_4, минуя мертвый_код_2 и мертвый_код_3
короткий_переход проверка_4
; --- МЕРТВЫЙ КОД 2 ---
; Этот код НЕ должен выполниться из-за прыжка выше
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_2
переместить_имм рдикс, len_msg_dead_2
вызов_системы
; ---------------------
мертвый_код_3:
; ЖИВОЙ КОД 3: Этот код выполнится благодаря прыжку "назад"
; Выведем сообщение, чтобы убедиться, что он выполнился
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_live_from_3
переместить_имм рдикс, len_msg_live_from_3
вызов_системы
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 3: "Четыре вперед" - переход через мертвый_код_4, 5, 6, 7
; Цель слишком далеко для короткого перехода. Используем длинный.
; Прыгаем к проверка_8
переход проверка_8
; ---------------------
проверка_4:
; ЧЕТВЁРТЫЙ ПЕРЕХОД: короткий_переход_если_больше
; Сравниваем rax (10) и rbx (5) -> rax > rbx, ZF=0, SF=0, OF=0
сравнить раикс, рбикс
; Условие jg: ZF=0 (истина) И SF=OF (0=0, истина). jg СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 4, если jg сработает (т.е. если rax > rbx).
короткий_переход_если_больше мимоертвогоода_4
; --- МЕРТВЫЙ КОД 4 ---
; Этот код НЕ должен выполниться (так как jg сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_4
переместить_имм рдикс, len_msg_dead_4
вызов_системы
; ---------------------
мимоертвогоода_4:
; ДОПОЛНИТЕЛЬНЫЙ ПЕРЕХОД 2: "Один назад" - короткий_переход к мертвомуоду_3 (делая его живым)
короткий_переход мертвый_код_3
; --- МЕРТВЫЙ КОД 5 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_5
переместить_имм рдикс, len_msg_dead_5
вызов_системы
; ---------------------
; --- МЕРТВЫЙ КОД 6 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_6
переместить_имм рдикс, len_msg_dead_6
вызов_системы
; ---------------------
; --- МЕРТВЫЙ КОД 7 ---
; Этот код НЕ должен выполниться
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_7
переместить_имм рдикс, len_msg_dead_7
вызов_системы
; ---------------------
проверка_8:
; СЕДЬМОЙ ПЕРЕХОД: короткий_переход_если_перенос
; Выполним вычитание, которое вызовет перенос: rbx - rax = 5 - 10
переместить_имм рбикс, 5 ; rbx = 5
переместить_имм раикс, 10 ; rax = 10
; rbx - rax = 5 - 10 -> CF=1
вычесть рбикс, раикс ; rbx = rbx - rax = 5 - 10. Это вызовет перенос (CF=1).
; Условие jc: CF=1. CF=1. jc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 8, если jc сработает (т.е. если был перенос).
короткий_переход_если_перенос мимоертвогоода_8
; --- МЕРТВЫЙ КОД 8 ---
; Этот код НЕ должен выполниться (так как jc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_8
переместить_имм рдикс, len_msg_dead_8
вызов_системы
; ---------------------
мимоертвогоода_8:
; ВОСЬМОЙ ПЕРЕХОД: короткий_переход_если_нет_переноса
; Убедимся, что CF=0. Выполним операцию, которая сбросит CF.
сравнить раикс, раикс ; CF = 0 (так как rax - rax = 0, без переноса)
; Условие jnc: CF=0. CF=0. jnc СРАБОТАЕТ.
; Мы хотим прыгнуть МИМО мертвого кода 9, если jnc сработает (т.е. если НЕ было переноса).
короткий_переход_если_нет_переноса мимоертвогоода_9
; --- МЕРТВЫЙ КОД 9 ---
; Этот код НЕ должен выполниться (так как jnc сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_9
переместить_имм рдикс, len_msg_dead_9
вызов_системы
; ---------------------
мимоертвогоода_9:
; ДЕВЯТЫЙ ПЕРЕХОД: короткий_переход (безусловный)
; Просто прыгаем мимо мертвого кода 10.
короткий_переход мимоертвогоода_10
; --- МЕРТВЫЙ КОД 10 ---
; Этот код НЕ должен выполниться (так как jmp сработал)
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_dead_10
переместить_имм рдикс, len_msg_dead_10
вызов_системы
; ---------------------
мимоертвогоода_10:
; --- ВЫВОД ЦЕЛЕВОГО СООБЩЕНИЯ ---
вывести_сообщение:
переместить_имм раикс, 1
переместить_имм рдиай, 1
переместить_имм рсиай, msg_success
переместить_имм рдикс, len_msg_success
вызов_системы
; --- ЗАВЕРШЕНИЕ ---
переместить_имм раикс, 60
переместить_имм рдиай, 0
вызов_системы
.данные
; Перемешиваем строки и константы
; "Живой из 3!\n" = 8 букв * 2 + 1 (!) + 2 (\n) = 16 + 3 = 19 байт на содержимое. len = 19
msg_live_from_3: .строкауль "Живой из 3!\n"
.константа len_msg_live_from_3 = 19
; "Успех!\n" = 5 букв * 2 + 1 (!) + 2 (\n) = 13 байт на содержимое. len = 13
msg_success: .строкауль "Успех!\n"
.константа len_msg_success = 13
; "Мертвый код 10!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_10: .строкауль "Мертвый код 10!"
.константа len_msg_dead_10 = 18
; "Мертвый код 9!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_9: .строкауль "Мертвый код 9!"
.константа len_msg_dead_9 = 18
; "Мертвый код 8!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_8: .строкауль "Мертвый код 8!"
.константа len_msg_dead_8 = 18
; "Мертвый код 7!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_7: .строкауль "Мертвый код 7!"
.константа len_msg_dead_7 = 18
; "Мертвый код 6!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_6: .строкауль "Мертвый код 6!"
.константа len_msg_dead_6 = 18
; "Мертвый код 5!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_5: .строкауль "Мертвый код 5!"
.константа len_msg_dead_5 = 18
; "Мертвый код 4!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_4: .строкауль "Мертвый код 4!"
.константа len_msg_dead_4 = 18
; "Мертвый код 3!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_3: .строкауль "Мертвый код 3!"
.константа len_msg_dead_3 = 18
; "Мертвый код 2!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_2: .строкауль "Мертвый код 2!"
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18

View file

@ -1,237 +0,0 @@
; шорт_тест.квс (обновлённый)
; Исходный файл для тестирования всех коротких условных переходов
.текст
.глобал _start
_start:
; Подготовка: инициализируем регистры для тестирования флагов
переместить_имм раикс, 5 ; rax = 5
переместить_имм рбикс, 3 ; rbx = 3
переместить_имм рсикс, 5 ; rcx = 5 (для теста равенства)
; --- Тесты для переходов, зависящих от результата cmp ---
; cmp rax, rbx -> rax(5) > rbx(3) -> SF != OF, ZF = 0
сравнить раикс, рбикс
; Переход если равно (ZF=1) -> не сработает, прыгаем мимо
короткий_переход_если_равно мимо_равно
переместить_имм рдикс, 1 ; Если сюда попали, ZF=0
переместить_имм рсиай, msg_zf_clear
переместить_имм рдикс, len_msg_zf_clear
переместить_имм раикс, 1 ; write
переместить_имм рдиай, 1 ; stdout
вызов_системы
короткий_переход мимо_равно
мимо_равно:
; Переход если не равно (ZF=0) -> сработает
короткий_переход_если_неоль мимое_равно
переместить_имм рдикс, 1 ; Не должно сработать
переместить_имм рсиай, msg_should_not_print
переместить_имм рдикс, len_msg_should_not_print
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
мимое_равно:
; Переход если меньше (SF != OF и SF=1) -> не сработает (SF=0, OF=0)
короткий_переход_если_меньше мимоеньше
переместить_имм рдикс, 1 ; SF=0
переместить_имм рсиай, msg_sf_clear
переместить_имм рдикс, len_msg_sf_clear
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход послееньше
мимоеньше:
; Если сработал переход, то SF=1
переместить_имм рдикс, 1
переместить_имм рсиай, msg_sf_set
переместить_имм рдикс, len_msg_sf_set
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
послееньше:
; Переход если больше (ZF=0 и SF=OF) -> сработает (ZF=0, SF=0, OF=0)
короткий_переход_если_больше после_больше
; Если сюда попали, условие не выполнилось
переместить_имм рдикс, 1
переместить_имм рсиай, msg_should_not_print
переместить_имм рдикс, len_msg_should_not_print
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_еще_одного
после_больше:
; Переход если меньше или равно (ZF=1 или SF!=OF) -> не сработает (ZF=0, SF=OF=0)
короткий_переход_если_меньше_или_равно мимоеньше_равно
; Условие не выполнилось, ZF=0, SF=OF
переместить_имм рдикс, 1
переместить_имм рсиай, msg_zf_clear_or_sf_eq_of
переместить_имм рдикс, len_msg_zf_clear_or_sf_eq_of
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_еще_одного
мимоеньше_равно:
; Условие выполнилось, ZF=1 или SF!=OF
переместить_имм рдикс, 1
переместить_имм рсиай, msg_zf_set_or_sf_neq_of
переместить_имм рдикс, len_msg_zf_set_or_sf_neq_of
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
после_еще_одного:
; Переход если больше или равно (SF=OF) -> сработает (SF=0, OF=0)
короткий_переход_если_больше_или_равно после_больше_равно
; Условие не выполнилось, SF != OF
переместить_имм рдикс, 1
переместить_имм рсиай, msg_sf_neq_of
переместить_имм рдикс, len_msg_sf_neq_of
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход конец_cmp
после_больше_равно:
; Условие выполнилось, SF = OF
переместить_имм рдикс, 1
переместить_имм рсиай, msg_sf_eq_of
переместить_имм рдикс, len_msg_sf_eq_of
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
конец_cmp:
; --- Тесты для переходов, зависящих от флага переноса (CF) ---
; Проверим CF, установив его с помощью вычитания
; rax = 3, rbx = 5 -> rax - rbx -> CF=1 (т.к. 3 < 5)
переместить_имм раикс, 3
переместить_имм рбикс, 5
вычесть рбикс, раикс ; rax = rax - rbx, CF=1
; Переход если перенос (CF=1) -> сработает
короткий_переход_если_перенос после_cf_set
; Это не должно напечататься
переместить_имм рдикс, 1
переместить_имм рсиай, msg_should_not_print
переместить_имм рдикс, len_msg_should_not_print
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_cf_clear
после_cf_set:
; CF был установлен
переместить_имм рдикс, 1
переместить_имм рсиай, msg_cf_set
переместить_имм рдикс, len_msg_cf_set
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
; Теперь очистим CF, например, через сравнение 5 и 3
переместить_имм раикс, 5
переместить_имм рбикс, 3
сравнить раикс, рбикс ; rax - rbx, CF=0 (т.к. 5 >= 3)
; Переход если нет переноса (CF=0) -> сработает
после_cf_clear:
короткий_переход_если_нет_переноса после_cf_tests
; Это не должно напечататься
переместить_имм рдикс, 1
переместить_имм рсиай, msg_should_not_print
переместить_имм рдикс, len_msg_should_not_print
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_cf_tests
после_cf_tests:
; CF был очищен
переместить_имм рдикс, 1
переместить_имм рсиай, msg_cf_clear
переместить_имм рдикс, len_msg_cf_clear
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
; --- Тесты для переходов, зависящих от ZF (через test) ---
; test rax, rax -> ZF=0 (rax != 0)
переместить_имм раикс, 10
проверить раикс, раикс
; Переход если ноль (ZF=1) -> не сработает
короткий_переход_если_ноль мимо_test_zf
; ZF=0
переместить_имм рдикс, 1
переместить_имм рсиай, msg_zf_clear
переместить_имм рдикс, len_msg_zf_clear
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_test_zf
мимо_test_zf:
; ZF=1
переместить_имм рдикс, 1
переместить_имм рсиай, msg_zf_set
переместить_имм рдикс, len_msg_zf_set
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
после_test_zf:
; test rax, rax -> ZF=1 (rax = 0)
переместить_имм раикс, 0
проверить раикс, раикс
; Переход если ноль (ZF=1) -> сработает
короткий_переход_если_ноль после_test_zf2
; Это не должно напечататься
переместить_имм рдикс, 1
переместить_имм рсиай, msg_should_not_print
переместить_имм рдикс, len_msg_should_not_print
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
короткий_переход после_test_zf2
после_test_zf2:
; ZF=1
переместить_имм рдикс, 1
переместить_имм рсиай, msg_zf_set
переместить_имм рдикс, len_msg_zf_set
переместить_имм раикс, 1
переместить_имм рдиай, 1
вызов_системы
; --- Завершение ---
переместить_имм раикс, 60 ; exit
переместить_имм рдиай, 0 ; status
вызов_системы
.данные
msg_zf_clear: .строкауль "ZF=0 (Clear). "
.константа len_msg_zf_clear = 15
msg_zf_set: .строкауль "ZF=1 (Set). "
.константа len_msg_zf_set = 12
msg_sf_clear: .строкауль "SF=0 (Clear). "
.константа len_msg_sf_clear = 14
msg_sf_set: .строкауль "SF=1 (Set). "
.константа len_msg_sf_set = 12
msg_sf_eq_of: .строкауль "SF=OF. "
.константа len_msg_sf_eq_of = 8
msg_sf_neq_of: .строкауль "SF!=OF. "
.константа len_msg_sf_neq_of = 8
msg_zf_clear_or_sf_eq_of: .строкауль "ZF=0 или SF=OF. "
.константа len_msg_zf_clear_or_sf_eq_of = 16
msg_zf_set_or_sf_neq_of: .строкауль "ZF=1 или SF!=OF. "
.константа len_msg_zf_set_or_sf_neq_of = 16
msg_cf_set: .строкауль "CF=1 (Carry Set). "
.константа len_msg_cf_set = 18
msg_cf_clear: .строкауль "CF=0 (No Carry). "
.константа len_msg_cf_clear = 17
msg_should_not_print: .строкауль "ОШИБКА: Это не должно быть напечатано! "
.константа len_msg_should_not_print = 36

273
todo.txt Normal file
View file

@ -0,0 +1,273 @@
===========================
нужна сложная адресация
===========================
Хорошо, вот **полностью доработанный пошаговый план** внедрения сложной адресации во все модули компилятора КВС, с учётом:
- Поддержки `+`, `-`, `*`, `[`, `]`
- Новых мнемоник для работы с памятью
- Адресации всех типов: `[reg]`, `[disp]`, `[reg+disp]`, `[reg+reg*scale]`, `[reg+reg*scale+disp]`
- Корректной работы лексера, парсера, pass1, pass2, сборки ELF
---
# ПЛАН РЕАЛИЗАЦИИ СЛОЖНОЙ АДРЕСАЦИИ (ПОЛНАЯ ВЕРСИЯ)
## 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, генерация кода | очень высокая |
---
## 2. Детальный план по шагам (в порядке реализации)
### Шаг 1 — Лексер (`kvs_lexer.py`)
**Что сделать:**
- Добавить токены:
- `LBRACKET` → `[`
- `RBRACKET` → `]`
- `PLUS` → `+`
- `MINUS` → `-`
- `STAR` → `*`
- Добавить `NUMBER` (отдельно от `WORD`) для:
- десятичных чисел (`123`)
- отрицательных (`-456`)
- шестнадцатеричных (`0x7F`)
** парсер:**
- Парсер сможет отличать `[` от начала слова
- Числа не будут смешиваться с метками/регистрами
**Результат:** новые типы токенов на выходе лексера.
---
### Шаг 2 — Парсер (`kvs_parser.py`) — основа
**Что сделать:**
2.1. Заменить прямую загрузку токена в `parse_instruction()` на вызов `parse_operand()`
2.2. Реализовать `parse_operand()`:
- Если токен `LBRACKET` → вызвать `parse_memory_operand()`
- Иначе → вернуть значение токена (регистр/число/метка/строка)
2.3. Реализовать `parse_memory_operand()`:
- Разбирает содержимое между `[` и `]`
- Поддерживает выражения вида:
- `регистр`
- `число`
- `регистр + смещение`
- `регистр + регистр * масштаб`
- `регистр + регистр * масштаб + смещение`
- любые комбинации с `+` и `-`
- Возвращает строку типа `[раикс + рсикс*8 + 16]`
**Результат:** AST будет содержать операнды памяти в каноническом строковом виде.
---
### Шаг 3 — Данные инструкций (`kvs_data.py`)
**Что сделать:**
3.1. Активировать (или добавить) записи для:
| Мнемоника | Opcode | Тип (формат) |
|-----------|--------|--------------|
| `загрузить` | `48 8B` | `mov_reg_mem` |
| `сохранить` | `48 89` | `mov_mem_reg` |
| `загрузить_байт_из_памяти` | `8A` | `mov_reg8_mem` |
| `сохранить_байт` | `88` | `mov_mem8_reg8` |
| `загрузить_адрес` | `48 8D` | `lea` |
| `переместить_сулями` | `48 0F B6` | `movzx` |
| `переместить_со_знаком` | `48 0F BE` | `movsx` |
| `втолкнуть_из_памяти` | `FF /6` | `push_mem` |
| `вытолкнуть_в_память` | `8F /0` | `pop_mem` |
| `обменять_с_памятью` | `48 87` | `xchg_mem` |
3.2. Удалить поле `type`, оставить `format` или прямо в коде проверять по мнемонике (как вы любите).
**Результат:** словарь инструкций готов для кодогенерации.
---
### Шаг 4 — Первый проход (`kvs_pass1.py`) — оценка размера
**Что сделать:**
4.1. Написать функцию `estimate_memory_operand_size(operand_str)`:
- Парсит строку `[раикс + рсикс*8 + 16]`
- Определяет:
- наличие SIB (нужен, если есть индексный регистр)
- размер смещения (disp8 = 1 байт, disp32 = 4 байта)
- Возвращает дополнительные байты: 0, 1, 4 или 5 (SIB + disp)
4.2. Модифицировать `estimate_size()`:
- Для инструкций с памятью (список из шага 3) считать:
- базовый размер opcode (REX + опкод)
- +1 байт (ModR/M)
- +1 байт (SIB, если нужен)
- + размер смещения (1 или 4)
**Результат:** правильное вычисление `.text` и адресов меток.
---
### Шаг 5 — Второй проход (`kvs_pass2.py`) — ядро адресации
Это самый объёмный шаг. Реализовать **поэтапно**:
#### 5.1. Структура данных для режима адресации
Создать класс/словарь `AddressingMode`:
- `has_base` (bool)
- `base_reg` (int 015 или None)
- `has_index` (bool)
- `index_reg` (int 015)
- `scale` (1,2,4,8)
- `has_disp` (bool)
- `disp_value` (int)
- `disp_size` (1 или 4)
#### 5.2. Парсер адресации
Написать `parse_addressing_mode(operand_str)`:
- Вход: `[раикс + рсикс*8 + 16]`
- Выход: `AddressingMode`
- Поддержка всех форм из шага 2.3
#### 5.3. Кодирование ModR/M
Написать `encode_modrm_reg_mem(reg, addr_mode)`:
- Формирует байт ModR/M:
- `mod` (00, 01, 10, 11)
- `reg` (3 бита — номер регистра)
- `r/m` (3 бита — зависит от наличия SIB)
#### 5.4. Кодирование SIB
Написать `encode_sib(addr_mode)`:
- Формирует байт SIB:
- `scale` (2 бита)
- `index` (3 бита)
- `base` (3 бита)
#### 5.5. Кодирование смещения
Написать `encode_displacement(addr_mode, current_pos, vaddr_text)`:
- Для `disp8` → 1 байт со знаком
- Для `disp32` → 4 байта (little-endian)
#### 5.6. Генераторы инструкций
Написать для каждой мнемоники:
| Функция | Для какой инструкции |
|---------|----------------------|
| `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` | `обменять_с_памятью` |
Каждая функция:
- Берёт операнды (регистр + операнд памяти)
- Вызывает `parse_addressing_mode` для операнда памяти
- Формирует REX-префикс (W, R, B, X биты)
- Добавляет opcode
- Добавляет ModR/M
- Добавляет SIB (если нужен)
- Добавляет disp
#### 5.7. Диспетчер
Модифицировать `encode_instruction()`:
- Добавить ветки для всех новых мнемоник
- Вызывать соответствующие функции из 5.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 |
---
### Шаг 7 — Стандартная библиотека (параллельно или после)
После того как адресация заработает, можно добавить:
| Файл | Содержание |
|------|------------|
| `libkvs.квс` | `malloc`, `free`, `strlen`, `strcmp`, `memcpy`, `putchar`, `getchar`, `printf` (упрощённый) |
| `libkvs_data.квс` | статические данные для библиотеки |
Библиотека будет использовать сложную адресацию для работы со структурами и кучей.
---
## 3. Зависимости между подзадачами
```
Лексер (шаг 1)
Парсер (шаг 2)
Данные инструкций (шаг 3)
Pass1 (шаг 4) ← может тестироваться отдельно
Pass2:
5.1 → 5.2 → 5.3 → 5.4 → 5.5
5.6 (для каждой мнемоники)
5.7 (диспетчер)
Тестирование (шаг 6)
Библиотека (шаг 7)
```
---
## 4. Итог: что получим на выходе
- **Лексер** выдаёт `LBRACKET`, `RBRACKET`, `PLUS`, `MINUS`, `STAR`, `NUMBER`
- **Парсер** понимает `[раикс + рсикс*8 - 16]`
- **Pass1** правильно считает размер инструкций с памятью
- **Pass2** генерирует правильный машинный код для всех форм адресации
- **ELF** содержит корректные инструкции x86-64 с ModR/M, SIB, disp
- **Библиотека** позволяет писать более сложные программы (malloc, строки)
---
---
План готов. Когда скажете — начинаем писать код по шагам.

View file

@ -933,4 +933,4 @@ except SystemExit:
raise
except Exception as e:
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
sys.exit(1)
sys.exit(1)

View file

@ -10,7 +10,6 @@ kvs_9_8.py
- комментарии на русском
- dry вреден, изоляция логики уместна
- избегать ооп (имеем в виду возможную самокомпиляцию)
- добавить поиск модулей по абсолютным и относительным путям
- готовить внедрение сложной адресации
"""
@ -1893,133 +1892,6 @@ def create_elf(filename):
os.chmod(filename, 0o755)
# == РЕАЛИЗАЦИЯ ИМПОРТА ==
# добавить поиск модулей по абсолютным и относительным путям,
# команда .путь_импорта
def build_intermediate(main_file: str) -> str:
"""
Собирает промежуточный текст со всеми импортами и переименованными метками/данными.
Возвращает ПОЛНЫЙ текст промежуточного файла (str).
"""
visited = set() # абсолютные пути уже обработанных файлов
data_lines = [] # итоговая секция .данные
text_lines = [] # итоговая секция .текст
# нормализованное имя метки: метка + суффикс модуля
def qual(label: str, module: str) -> str:
return f"{label}_{module}"
def rename_line(line: str, module: str, labels: set, constants: set) -> str:
"""Улучшенная функция переименования с обработкой констант и глобальных меток"""
if not line.strip():
return line
# Обработка директивы .глобал
if line.strip().startswith('.глобал'):
parts = line.split()
if len(parts) >= 2:
global_label = parts[1]
if global_label in labels:
return f".глобал {qual(global_label, module)}"
# Обработка директивы .константа
elif line.strip().startswith('.константа'):
parts = line.split('=', 1)
if len(parts) >= 2:
const_part = parts[0].strip()
value_part = parts[1].strip()
const_parts = const_part.split()
if len(const_parts) >= 2:
const_name = const_parts[1]
if const_name in constants:
new_const_name = qual(const_name, module)
# Переименовываем также использование этой константы в значении
new_value = value_part
for const in constants:
new_value = new_value.replace(const, qual(const, module))
for lbl in labels:
new_value = new_value.replace(lbl, qual(lbl, module))
return f".константа {new_const_name} = {new_value}"
# Общая замена для всех остальных случаев
result = line
for const in sorted(constants, key=len, reverse=True):
result = result.replace(const, qual(const, module))
for lbl in sorted(labels, key=len, reverse=True):
result = result.replace(lbl, qual(lbl, module))
return result
def process(path: str):
abspath = os.path.abspath(path)
if abspath in visited:
return
visited.add(abspath)
module_name = os.path.splitext(os.path.basename(path))[0]
with open(path, encoding="utf-8") as f:
src = f.read()
# 1-й проход — собираем метки И константы
labels = set()
constants = set()
for line in src.splitlines():
line = line.split(';')[0].rstrip()
# Собираем метки
if ':' in line:
lbl = line.split(':')[0].strip()
if lbl and not lbl.startswith('.'):
labels.add(lbl)
# Собираем константы
if line.startswith('.константа'):
parts = line.split()
if len(parts) >= 2:
# Берем имя константы (часть до = если есть)
const_name = parts[1].split('=')[0].strip()
constants.add(const_name)
# 2-й проход — раскидываем строки и переименовываем
cur_data = False
cur_text = False
for raw in src.splitlines(True): # splitlines(True) сохраняет \n
line = raw.rstrip()
if line.startswith('.данные'):
cur_data, cur_text = True, False
continue
if line.startswith('.текст'):
cur_data, cur_text = False, True
continue
if line.startswith('.импорт'):
parts = line.split()
if len(parts) != 2:
raise ValueError(f"Неверная директива импорта: {line}")
nextfile = parts[1]
if not nextfile.endswith('.квс'):
nextfile += '.квс'
# ищем рядом с текущим файлом
neighbour = os.path.join(os.path.dirname(abspath), nextfile)
process(neighbour)
continue
new_line = rename_line(raw, module_name, labels, constants)
if cur_data:
data_lines.append(new_line)
else:
text_lines.append(new_line)
# стартуем рекурсию из главного файла
process(main_file)
# склеиваем финальный текст
intermediate = ['.данные', *data_lines, '.текст', *text_lines]
return '\n'.join(intermediate)
# == РЕАЛИЗАЦИЯ СЛОЖНОЙ АДРЕСАЦИИ ==
"""
@ -2056,10 +1928,10 @@ if __name__ == "__main__":
print("Ошибка: файл должен иметь расширение .квс")
sys.exit(1)
# читаем исходник только для построения промежуточного
# Читаем исходник напрямую
try:
with open(source_file, encoding="utf-8") as f:
f.read() # проверка существования/кодировки
source_text = f.read()
except FileNotFoundError:
print("Ошибка: файл '" + source_file + "' не найден.")
sys.exit(1)
@ -2067,14 +1939,8 @@ if __name__ == "__main__":
print("Ошибка кодировки в файле '" + source_file + "': " + str(e))
sys.exit(1)
intermediate_text = build_intermediate(source_file)
int_file = source_file + ".промежуточный"
with open(int_file, "w", encoding="utf-8") as f:
f.write(intermediate_text)
print("Промежуточный файл создан:", int_file)
try:
parse(intermediate_text)
parse(source_text)
elf_file = source_file[:-4] + ".elf"
create_elf(elf_file)

View file

@ -0,0 +1,90 @@
мнемоника ориг на англ опкод длина в байтах пояснение
переместить_имм MOV reg, imm (кодируется динамически) 7-10 Переместить непосредственное значение в регистр. Поддерживает 8/16/32/64 битные регистры
переместить MOV reg, reg 48 89 + ModR/M 3 Переместить данные из одного регистра в другой (64-бит)
загрузить MOV reg, mem 48 8B + ModR/M 2-9 Загрузить значение из памяти в регистр. Поддержка всех форм адресации
сохранить MOV mem, reg 48 89 + ModR/M 2-9 Сохранить значение регистра в память. Поддержка всех форм адресации
загрузить_байт_из_памяти MOV reg8, mem8 8A + ModR/M 2-6 Загрузить байт из памяти в 8-битный регистр
сохранить_байт MOV mem8, reg8 88 + ModR/M 2-6 Сохранить 8-битный регистр в память
загрузить_адрес LEA reg, mem 48 8D + ModR/M 3-9 Загрузить эффективный адрес (вычисленный адрес, не значение по адресу)
переместить_сулями MOVZX reg, mem8 48 0F B6 + ModR/M 4-10 Загрузить байт из памяти с расширением нулями до 32/64 бит
переместить_со_знаком MOVSX reg, mem8 48 0F BE + ModR/M 4-10 Загрузить байт из памяти с расширением знака до 32/64 бит
загрузить_байт MOV reg8, imm8 (кодируется динамически) 2-3 Загрузить непосредственный байт в 8-битный регистр (B0+reg + imm8)
сравнить_с CMP reg, imm (кодируется динамически) 6-8 Сравнить регистр с непосредственным значением
сравнить CMP reg, reg 48 39 + ModR/M 3 Сравнить два регистра
сравнить_байт CMP reg8, reg8 38 + ModR/M 2 Сравнить два 8-битных регистра
проверить TEST reg, reg 48 85 + ModR/M 3 Логическое сравнение (побитовое И), не изменяет операнды
прибавить ADD reg, reg 48 01 + ModR/M 3 Сложить два регистра
прибавить_непосредственно ADD reg, imm (кодируется динамически) 6-8 Прибавить непосредственное значение к регистру
прибавить_байт ADD reg8, reg8 00 + ModR/M 2 Сложить два 8-битных регистра
вычесть SUB reg, reg 48 29 + ModR/M 3 Вычесть второй регистр из первого
вычесть_непосредственно SUB reg, imm (кодируется динамически) 6-8 Вычесть непосредственное значение из регистра
вычесть_байт SUB reg8, reg8 28 + ModR/M 2 Вычесть два 8-битных регистра
увеличить INC reg FF /0 + ModR/M 2-3 Увеличить регистр на 1 (не меняет CF)
уменьшить DEC reg FF /1 + ModR/M 2-3 Уменьшить регистр на 1 (не меняет CF)
умножить MUL reg/mem F7 /4 + ModR/M 2-9 Беззнаковое умножение RAX = RAX * операнд. Результат в RDX:RAX
умножить_знаковое IMUL reg/mem F7 /5 + ModR/M 2-9 Знаковое умножение RAX = RAX * операнд. Результат в RDX:RAX
разделить DIV reg/mem F7 /6 + ModR/M 2-9 Беззнаковое деление RDX:RAX / операнд. Частное в RAX, остаток в RDX
разделить_знаковое IDIV reg/mem F7 /7 + ModR/M 2-9 Знаковое деление RDX:RAX / операнд. Частное в RAX, остаток в RDX
и AND reg, reg 48 21 + ModR/M 3 Побитовое И двух регистров
или OR reg, reg 48 09 + ModR/M 3 Побитовое ИЛИ двух регистров
исключающее_или XOR reg, reg 48 31 + ModR/M 3 Побитовое исключающее ИЛИ двух регистров
инвертировать NOT reg F7 /2 + ModR/M 2-3 Побитовое НЕ (инверсия всех бит)
отрицать NEG reg F7 /3 + ModR/M 2-3 Арифметическое отрицание (дополнительный код)
переход JMP rel32 E9 5 Безусловный переход на 32-битное смещение
короткий_переход JMP SHORT rel8 EB 2 Короткий безусловный переход на 8-битное смещение
переход_если_равно JE/JZ rel32 0F 84 6 Условный переход если равно (ZF=1)
переход_если_неравно JNE/JNZ rel32 0F 85 6 Условный переход если не равно (ZF=0)
переход_если_меньше JL rel32 0F 8C 6 Условный переход если меньше (знаковое, SF≠OF)
переход_если_больше JG rel32 0F 8F 6 Условный переход если больше (знаковое, ZF=0 и SF=OF)
переход_если_меньше_или_равно JLE rel32 0F 8E 6 Условный переход если меньше или равно (знаковое)
переход_если_больше_или_равно JGE rel32 0F 8D 6 Условный переход если больше или равно (знаковое)
переход_если_перенос JC rel32 0F 82 6 Условный переход если перенос (CF=1). Для беззнаковых: меньше
переход_если_нет_переноса JNC rel32 0F 83 6 Условный переход если нет переноса (CF=0). Для беззнаковых: больше/равно
переход_если_ноль JZ rel32 0F 84 6 Условный переход если ноль (ZF=1). Синоним JE
переход_если_неоль JNZ rel32 0F 85 6 Условный переход если не ноль (ZF=0). Синоним JNE
короткий_переход_если_равно JE SHORT rel8 74 2 Короткий условный переход если равно
короткий_переход_если_неравно JNE SHORT rel8 75 2 Короткий условный переход если не равно
короткий_переход_если_меньше JL SHORT rel8 7C 2 Короткий условный переход если меньше (знаковое)
короткий_переход_если_больше JG SHORT rel8 7F 2 Короткий условный переход если больше (знаковое)
короткий_переход_если_меньше_или_равно JLE SHORT rel8 7E 2 Короткий условный переход если меньше или равно
короткий_переход_если_больше_или_равно JGE SHORT rel8 7D 2 Короткий условный переход если больше или равно
короткий_переход_если_перенос JC SHORT rel8 72 2 Короткий условный переход если перенос
короткий_переход_если_нет_переноса JNC SHORT rel8 73 2 Короткий условный переход если нет переноса
короткий_переход_если_ноль JZ SHORT rel8 74 2 Короткий условный переход если ноль
короткий_переход_если_неоль JNZ SHORT rel8 75 2 Короткий условный переход если не ноль
вызвать CALL rel32 E8 5 Вызов процедуры (подпрограммы). Адрес возврата в стек
вернуться RET C3 1 Возврат из процедуры. Адрес из стека
втолкнуть PUSH reg 50 + reg 1-2 Поместить регистр в стек (RSP = RSP - 8)
вытолкнуть POP reg 58 + reg 1-2 Извлечь регистр из стека (RSP = RSP + 8)
втолкнуть_непосредственно PUSH imm32 68 5 Поместить 32-битное значение в стек
втолкнуть_байт PUSH imm8 6A 2 Поместить 8-битное значение в стек с расширением знака
втолкнуть_из_памяти PUSH mem FF /6 + ModR/M 2-9 Поместить в стек значение из памяти
вытолкнуть_в_память POP mem 8F /0 + ModR/M 2-9 Извлечь из стека в память
сдвиг_влево SHL reg, imm 48 C1 E0 + ModR/M 4 Логический сдвиг влево (умножение на 2)
сдвиг_вправо SHR reg, imm 48 C1 E8 + ModR/M 4 Логический сдвиг вправо (деление на 2 без знака)
сдвиг_арифметический_вправо SAR reg, imm 48 C1 F8 + ModR/M 4 Арифметический сдвиг вправо (деление на 2 со знаком)
вращать_влево ROL reg, imm 48 C1 C0 + ModR/M 4 Вращение бит влево (биты не теряются)
вращать_вправо ROR reg, imm 48 C1 C8 + ModR/M 4 Вращение бит вправо (биты не теряются)
цикл LOOP rel8 E2 2 Инструкция цикла: RCX = RCX - 1; если RCX != 0, переход
обменять XCHG reg, reg 48 87 + ModR/M 3 Обменять значения двух регистров
обменять_с_памятью XCHG mem, reg 48 87 + ModR/M 2-9 Обменять значение регистра и памяти
вызов_системы SYSCALL 0F 05 2 Системный вызов в ядро Linux
прервать INT imm8 CD 2 Программное прерывание. Номер прерывания из операнда
войти_в_систему SYSENTER 0F 34 2 Быстрый системный вызов (32-битный режим)
остановить HLT F4 1 Остановить процессор до следующего прерывания
нет_операции NOP 90 1 Нет операции. Процессор ничего не делает
отладка INT3 CC 1 Точка останова для отладчика
переместить_байт MOVSB A4 1 Переместить байт: [RDI] = [RSI]; RSI++, RDI++
переместить_слово MOVSW A5 1 Переместить слово (2 байта): [RDI] = [RSI]; RSI+=2, RDI+=2
сравнить_байты CMPSB A6 1 Сравнить байты: сравнить [RSI] и [RDI]; RSI++, RDI++
сканировать_байт SCASB AE 1 Сканировать байт: сравнить AL и [RDI]; RDI++
установить_перенос STC F9 1 Установить флаг переноса CF = 1
сбросить_перенос CLC F8 1 Сбросить флаг переноса CF = 0
установить_направление STD FD 1 Установить флаг направления DF = 1 (строковые операции назад)
сбросить_направление CLD FC 1 Сбросить флаг направления DF = 0 (строковые операции вперёд)
втолкнуть_флаги PUSHF 9C 1 Поместить регистр флагов в стек
вытолкнуть_флаги POPF 9D 1 Извлечь регистр флагов из стека
ввод_байта IN imm8 E4 2 Ввод байта из порта ввода-вывода в AL
вывод_байта OUT imm8 E6 2 Вывод байта из AL в порт ввода-вывода
идентифицировать_процессор CPUID 0F A2 2 Получить информацию о процессоре (производитель, возможности)
прочитать_счётчик RDTSC 0F 31 2 Прочитать счётчик времени (TSC) в EDX:EAX
1 мнемоника ориг на англ опкод длина в байтах пояснение
2 переместить_имм MOV reg, imm (кодируется динамически) 7-10 Переместить непосредственное значение в регистр. Поддерживает 8/16/32/64 битные регистры
3 переместить MOV reg, reg 48 89 + ModR/M 3 Переместить данные из одного регистра в другой (64-бит)
4 загрузить MOV reg, mem 48 8B + ModR/M 2-9 Загрузить значение из памяти в регистр. Поддержка всех форм адресации
5 сохранить MOV mem, reg 48 89 + ModR/M 2-9 Сохранить значение регистра в память. Поддержка всех форм адресации
6 загрузить_байт_из_памяти MOV reg8, mem8 8A + ModR/M 2-6 Загрузить байт из памяти в 8-битный регистр
7 сохранить_байт MOV mem8, reg8 88 + ModR/M 2-6 Сохранить 8-битный регистр в память
8 загрузить_адрес LEA reg, mem 48 8D + ModR/M 3-9 Загрузить эффективный адрес (вычисленный адрес, не значение по адресу)
9 переместить_с_нулями MOVZX reg, mem8 48 0F B6 + ModR/M 4-10 Загрузить байт из памяти с расширением нулями до 32/64 бит
10 переместить_со_знаком MOVSX reg, mem8 48 0F BE + ModR/M 4-10 Загрузить байт из памяти с расширением знака до 32/64 бит
11 загрузить_байт MOV reg8, imm8 (кодируется динамически) 2-3 Загрузить непосредственный байт в 8-битный регистр (B0+reg + imm8)
12 сравнить_с CMP reg, imm (кодируется динамически) 6-8 Сравнить регистр с непосредственным значением
13 сравнить CMP reg, reg 48 39 + ModR/M 3 Сравнить два регистра
14 сравнить_байт CMP reg8, reg8 38 + ModR/M 2 Сравнить два 8-битных регистра
15 проверить TEST reg, reg 48 85 + ModR/M 3 Логическое сравнение (побитовое И), не изменяет операнды
16 прибавить ADD reg, reg 48 01 + ModR/M 3 Сложить два регистра
17 прибавить_непосредственно ADD reg, imm (кодируется динамически) 6-8 Прибавить непосредственное значение к регистру
18 прибавить_байт ADD reg8, reg8 00 + ModR/M 2 Сложить два 8-битных регистра
19 вычесть SUB reg, reg 48 29 + ModR/M 3 Вычесть второй регистр из первого
20 вычесть_непосредственно SUB reg, imm (кодируется динамически) 6-8 Вычесть непосредственное значение из регистра
21 вычесть_байт SUB reg8, reg8 28 + ModR/M 2 Вычесть два 8-битных регистра
22 увеличить INC reg FF /0 + ModR/M 2-3 Увеличить регистр на 1 (не меняет CF)
23 уменьшить DEC reg FF /1 + ModR/M 2-3 Уменьшить регистр на 1 (не меняет CF)
24 умножить MUL reg/mem F7 /4 + ModR/M 2-9 Беззнаковое умножение RAX = RAX * операнд. Результат в RDX:RAX
25 умножить_знаковое IMUL reg/mem F7 /5 + ModR/M 2-9 Знаковое умножение RAX = RAX * операнд. Результат в RDX:RAX
26 разделить DIV reg/mem F7 /6 + ModR/M 2-9 Беззнаковое деление RDX:RAX / операнд. Частное в RAX, остаток в RDX
27 разделить_знаковое IDIV reg/mem F7 /7 + ModR/M 2-9 Знаковое деление RDX:RAX / операнд. Частное в RAX, остаток в RDX
28 и AND reg, reg 48 21 + ModR/M 3 Побитовое И двух регистров
29 или OR reg, reg 48 09 + ModR/M 3 Побитовое ИЛИ двух регистров
30 исключающее_или XOR reg, reg 48 31 + ModR/M 3 Побитовое исключающее ИЛИ двух регистров
31 инвертировать NOT reg F7 /2 + ModR/M 2-3 Побитовое НЕ (инверсия всех бит)
32 отрицать NEG reg F7 /3 + ModR/M 2-3 Арифметическое отрицание (дополнительный код)
33 переход JMP rel32 E9 5 Безусловный переход на 32-битное смещение
34 короткий_переход JMP SHORT rel8 EB 2 Короткий безусловный переход на 8-битное смещение
35 переход_если_равно JE/JZ rel32 0F 84 6 Условный переход если равно (ZF=1)
36 переход_если_неравно JNE/JNZ rel32 0F 85 6 Условный переход если не равно (ZF=0)
37 переход_если_меньше JL rel32 0F 8C 6 Условный переход если меньше (знаковое, SF≠OF)
38 переход_если_больше JG rel32 0F 8F 6 Условный переход если больше (знаковое, ZF=0 и SF=OF)
39 переход_если_меньше_или_равно JLE rel32 0F 8E 6 Условный переход если меньше или равно (знаковое)
40 переход_если_больше_или_равно JGE rel32 0F 8D 6 Условный переход если больше или равно (знаковое)
41 переход_если_перенос JC rel32 0F 82 6 Условный переход если перенос (CF=1). Для беззнаковых: меньше
42 переход_если_нет_переноса JNC rel32 0F 83 6 Условный переход если нет переноса (CF=0). Для беззнаковых: больше/равно
43 переход_если_ноль JZ rel32 0F 84 6 Условный переход если ноль (ZF=1). Синоним JE
44 переход_если_не_ноль JNZ rel32 0F 85 6 Условный переход если не ноль (ZF=0). Синоним JNE
45 короткий_переход_если_равно JE SHORT rel8 74 2 Короткий условный переход если равно
46 короткий_переход_если_неравно JNE SHORT rel8 75 2 Короткий условный переход если не равно
47 короткий_переход_если_меньше JL SHORT rel8 7C 2 Короткий условный переход если меньше (знаковое)
48 короткий_переход_если_больше JG SHORT rel8 7F 2 Короткий условный переход если больше (знаковое)
49 короткий_переход_если_меньше_или_равно JLE SHORT rel8 7E 2 Короткий условный переход если меньше или равно
50 короткий_переход_если_больше_или_равно JGE SHORT rel8 7D 2 Короткий условный переход если больше или равно
51 короткий_переход_если_перенос JC SHORT rel8 72 2 Короткий условный переход если перенос
52 короткий_переход_если_нет_переноса JNC SHORT rel8 73 2 Короткий условный переход если нет переноса
53 короткий_переход_если_ноль JZ SHORT rel8 74 2 Короткий условный переход если ноль
54 короткий_переход_если_не_ноль JNZ SHORT rel8 75 2 Короткий условный переход если не ноль
55 вызвать CALL rel32 E8 5 Вызов процедуры (подпрограммы). Адрес возврата в стек
56 вернуться RET C3 1 Возврат из процедуры. Адрес из стека
57 втолкнуть PUSH reg 50 + reg 1-2 Поместить регистр в стек (RSP = RSP - 8)
58 вытолкнуть POP reg 58 + reg 1-2 Извлечь регистр из стека (RSP = RSP + 8)
59 втолкнуть_непосредственно PUSH imm32 68 5 Поместить 32-битное значение в стек
60 втолкнуть_байт PUSH imm8 6A 2 Поместить 8-битное значение в стек с расширением знака
61 втолкнуть_из_памяти PUSH mem FF /6 + ModR/M 2-9 Поместить в стек значение из памяти
62 вытолкнуть_в_память POP mem 8F /0 + ModR/M 2-9 Извлечь из стека в память
63 сдвиг_влево SHL reg, imm 48 C1 E0 + ModR/M 4 Логический сдвиг влево (умножение на 2)
64 сдвиг_вправо SHR reg, imm 48 C1 E8 + ModR/M 4 Логический сдвиг вправо (деление на 2 без знака)
65 сдвиг_арифметический_вправо SAR reg, imm 48 C1 F8 + ModR/M 4 Арифметический сдвиг вправо (деление на 2 со знаком)
66 вращать_влево ROL reg, imm 48 C1 C0 + ModR/M 4 Вращение бит влево (биты не теряются)
67 вращать_вправо ROR reg, imm 48 C1 C8 + ModR/M 4 Вращение бит вправо (биты не теряются)
68 цикл LOOP rel8 E2 2 Инструкция цикла: RCX = RCX - 1; если RCX != 0, переход
69 обменять XCHG reg, reg 48 87 + ModR/M 3 Обменять значения двух регистров
70 обменять_с_памятью XCHG mem, reg 48 87 + ModR/M 2-9 Обменять значение регистра и памяти
71 вызов_системы SYSCALL 0F 05 2 Системный вызов в ядро Linux
72 прервать INT imm8 CD 2 Программное прерывание. Номер прерывания из операнда
73 войти_в_систему SYSENTER 0F 34 2 Быстрый системный вызов (32-битный режим)
74 остановить HLT F4 1 Остановить процессор до следующего прерывания
75 нет_операции NOP 90 1 Нет операции. Процессор ничего не делает
76 отладка INT3 CC 1 Точка останова для отладчика
77 переместить_байт MOVSB A4 1 Переместить байт: [RDI] = [RSI]; RSI++, RDI++
78 переместить_слово MOVSW A5 1 Переместить слово (2 байта): [RDI] = [RSI]; RSI+=2, RDI+=2
79 сравнить_байты CMPSB A6 1 Сравнить байты: сравнить [RSI] и [RDI]; RSI++, RDI++
80 сканировать_байт SCASB AE 1 Сканировать байт: сравнить AL и [RDI]; RDI++
81 установить_перенос STC F9 1 Установить флаг переноса CF = 1
82 сбросить_перенос CLC F8 1 Сбросить флаг переноса CF = 0
83 установить_направление STD FD 1 Установить флаг направления DF = 1 (строковые операции назад)
84 сбросить_направление CLD FC 1 Сбросить флаг направления DF = 0 (строковые операции вперёд)
85 втолкнуть_флаги PUSHF 9C 1 Поместить регистр флагов в стек
86 вытолкнуть_флаги POPF 9D 1 Извлечь регистр флагов из стека
87 ввод_байта IN imm8 E4 2 Ввод байта из порта ввода-вывода в AL
88 вывод_байта OUT imm8 E6 2 Вывод байта из AL в порт ввода-вывода
89 идентифицировать_процессор CPUID 0F A2 2 Получить информацию о процессоре (производитель, возможности)
90 прочитать_счётчик RDTSC 0F 31 2 Прочитать счётчик времени (TSC) в EDX:EAX

34
доки/запуск Normal file
View file

@ -0,0 +1,34 @@
me@acer4g:~/Документы/kvs$ python3 kvs_build.py сложный_тест9.квс
=== Лексический анализ ===
Лексер: 475 токенов записано в сложный_тест9.токены
=== Парсинг ===
Парсер: 123 строк AST записано в сложный_тест9.аст
=== Первый проход ===
Проход 1: text_size=562, data_size=284
=== Второй проход и генерация CSV ===
Проход 2: 846 строк CSV записано в сложный_тест9.csv
.text: 562 байт, виртуальный адрес: 0x401000
.data: 284 байт, виртуальный адрес: 0x402000
=== Сборка ELF ===
Сборщик: ELF-файл создан: сложный_тест9.elf
.text: 562 байт, адрес: 0x401000
.data: 284 байт, адрес: 0x402000
Расстояние между секциями: 0x1000 байт
=== Компиляция успешно завершена ===
Исполняемый файл: сложный_тест9.elf
Запустить: ./сложный_тест9.elf
me@acer4g:~/Документы/kvs$ ./сложный_тест9.elf
Живой из 3!
Успех!
me@acer4g:~/Документы/kvs$

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,110 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
import os
import subprocess
def binary_to_disasm(input_file, output_file=None):
"""Конвертирует бинарный файл в дизассемблированный код с помощью ndisasm, исключая строки с 'add [rax],al'."""
try:
# Если выходной файл не указан, создаём рядом с оригиналом
if not output_file:
base_name = os.path.splitext(input_file)[0]
output_file = f"{base_name}_disasm.txt"
# Вызываем ndisasm для дизассемблирования
result = subprocess.run(
['ndisasm', '-b64', input_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Проверяем наличие ошибок от ndisasm
if result.returncode != 0:
error_msg = result.stderr.strip()
return False, f"Ошибка ndisasm: {error_msg}"
# Фильтруем строки: исключаем те, которые содержат 'add [rax],al'
# Разбиваем вывод на строки
lines = result.stdout.splitlines()
# Фильтруем, исключая строки с 'add [rax],al'
# Используем strip(), чтобы убрать лишние пробелы/переводы строк, если нужно
filtered_lines = [line for line in lines if 'add [rax],al' not in line]
# Записываем отфильтрованные строки в файл
with open(output_file, 'w', encoding='utf-8') as out_file:
for line in filtered_lines:
out_file.write(line + '\n') # Восстанавливаем перевод строки
return True, output_file
except FileNotFoundError:
return False, "ndisasm не найден. Убедитесь, что NASM установлен и доступен в PATH"
except Exception as e:
return False, str(e)
def select_and_convert():
"""Открывает диалог выбора файла и конвертирует"""
input_file = filedialog.askopenfilename(
title="Выберите бинарный файл",
filetypes=(("Бинарные файлы", "*.bin *.elf *.o"), ("Все файлы", "*.*")) # Расширим типы файлов
)
if not input_file:
return
success, result = binary_to_disasm(input_file)
if success:
messagebox.showinfo(
"Готово",
f"Файл успешно дизассемблирован!\nНули (add [rax],al) удалены.\nРезультат сохранён как:\n{result}"
)
else:
messagebox.showerror(
"Ошибка",
f"Не удалось дизассемблировать файл:\n{result}"
)
def main():
"""Создаём графический интерфейс"""
root = tk.Tk()
root.title("Дизассемблер бинарных файлов")
root.geometry("450x200")
# Центрируем окно
root.eval('tk::PlaceWindow . center')
# Создаём элементы интерфейса
label = tk.Label(
root,
text="Дизассемблер бинарных файлов (ndisasm)",
font=('Arial', 14),
pady=20
)
label.pack()
btn_convert = tk.Button(
root,
text="Выбрать файл и дизассемблировать",
command=select_and_convert,
font=('Arial', 12),
padx=20,
pady=10
)
btn_convert.pack()
btn_exit = tk.Button(
root,
text="Выход",
command=root.quit,
font=('Arial', 10),
padx=20,
pady=5
)
btn_exit.pack(pady=20)
root.mainloop()
if __name__ == "__main__":
main()

View file

@ -1,19 +0,0 @@
section .text
global _start
_start:
; Простые случаи с разными базовыми регистрами
push qword [rax]
pop qword [rax]
push qword [rbx]
pop qword [rbx]
push qword [rsp]
pop qword [rsp]
push qword [rbp]
pop qword [rbp]
;mov rax, 60
;mov rdi, 0
;syscall

View file

@ -1,9 +0,0 @@
section .text
global _start
_start:
push qword [r8]
pop qword [r8]
push qword [r9]
pop qword [r9]
push qword [r10]
pop qword [r10]

View file

@ -1,7 +0,0 @@
section .text
global _start
_start:
push qword [r12]
pop qword [r12]
push qword [r13]
pop qword [r13]

View file

@ -1,153 +0,0 @@
.текст
.глобал _start
_start:
; ========== БАЗОВЫЕ ИНСТРУКЦИИ ==========
переместить раикс, рбикс
переместить рсипи, рдиай
переместить р8, р9
переместить_имм раикс, 0x12345678
переместить_имм еаикс, 0x1234
переместить_имм аикс, 0x12
переместить_имм ал, 0x42
прибавить раикс, рбикс
вычесть рсипи, рдиай
вызвать some_function
вернуться
вызов_системы
; ========== СРАВНЕНИЯ И ПЕРЕХОДЫ ==========
сравнить раикс, рбикс
сравнить32 еаикс, ебикс
сравнить_байт ал, бл
переход loop
переход_если_равно loop
переход_если_неравно loop
переход_если_ноль loop
переход_если_неоль loop
переход_если_перенос loop
переход_если_нет_переноса loop
переход_если_меньше loop
переход_если_больше loop
переход_если_меньше_или_равно loop
переход_если_больше_или_равно loop
ороткий_переход loop
; ========== АРИФМЕТИКА ==========
прибавить_непосредственно раикс, 100
вычесть_непосредственно рбикс, 50
увеличить раикс
уменьшить рбикс
увеличить ал
уменьшить бл
умножить раикс
умножить_знаковое рбикс
разделить рсикс
разделить_знаковое рдикс
отрицать раикс
инвертировать рбикс
; ========== ЛОГИЧЕСКИЕ ОПЕРАЦИИ ==========
и раикс, рбикс
или рсипи, рдиай
исключающее_или р8, р9
проверить раикс, рбикс
очистить раикс
; ========== СДВИГИ И ВРАЩЕНИЯ ==========
;сдвиг_влево раикс, 4
;сдвиг_вправо рбикс, 2
;сдвиг_арифметический_влево рсикс, 1
;сдвиг_арифметический_вправо рдикс, 3
ращать_влево р8, 5
ращать_вправо р9, 6
; ========== СТЕК И ПАМЯТЬ ==========
втолкнуть раикс
втолкнуть рбикс
втолкнуть_из_памяти [рсипи]
вытолкнуть рдиай
вытолкнуть_в_память [рбипи]
загрузить раикс, [рсипи]
сохранить [рдиай], рбикс
загрузить_адрес раикс, some_data
; ========== 8-БИТНЫЕ ОПЕРАЦИИ ==========
переместить_имм ал, 0xAA
прибавить_байт ал, бл
вычесть_байт бл, кл
сравнить_байт дл, бл
сохранить_байт [раикс], ал
сохранить_в_адрес [рбикс], ал
; ========== РАСШИРЕНИЕ РЕГИСТРОВ ==========
переместить_сулями раикс, ал
переместить_со_знаком рбикс, бл
; ========== ОБМЕН ==========
обменять раикс, рбикс
;обменять_с_памятью [рсипи], рдиай
; ========== ФЛАГИ ==========
установить_перенос
сбросить_перенос
установить_направление
сбросить_направление
втолкнуть_флаги
вытолкнуть_флаги
; ========== СТРОКОВЫЕ ОПЕРАЦИИ ==========
переместить_байт
переместить_слово
сравнить_байты
сканировать_байт
; ========== СПЕЦИАЛЬНЫЕ КОМАНДЫ ==========
идентифицировать_процессор
прочитать_счетчик
получить_управление
отладка
прервать 0x80
; ========== ВВОД-ВЫВОД ==========
ввод_байта 0x60
вывод_байта 0x61
; ========== ЦИКЛЫ ==========
цикл loop
; ========== .БАЙТ ДИРЕКТИВА ==========
.байт 0x90 0x90 0x90 ; NOP-ы для выравнивания
loop:
; Метка для переходов
нет_операции
остановить
some_function:
вернуться
.данные
some_data: .строка "Test data"
;constant_value: .константа 0xDEADBEEF
; ========== РАЗНЫЕ ТИПЫ ДАННЫХ ==========
byte_array: .байт 0x01 0x02 0x03 0x04
string_data: .строкауль "Null terminated"
wide_string: .строка "Wide string test"
; Глобальная константа
;.глобал global_var
;global_var: .константа 42

View file

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

View file

@ -1,11 +0,0 @@
.текст
_старт:
; Расширенные регистры R8-R15
втолкнуть_из_памяти [р8]
вытолкнуть_в_память [р8]
втолкнуть_из_памяти [р9]
вытолкнуть_в_память [р9]
втолкнуть_из_памяти [р10]
вытолкнуть_в_память [р10]

View file

@ -1,8 +0,0 @@
.текст
_старт:
; R12 и R13 требуют особой обработки как RSP/RBP
втолкнуть_из_памяти [р12]
вытолкнуть_в_память [р12]
втолкнуть_из_памяти [р13]
вытолкнуть_в_память [р13]

View file

@ -1,18 +0,0 @@
.текст
.глобал _start
_start:
; Системный вызов write(1, msg, len)
переместить_имм раикс, 1 ; номер syscall write = 1
переместить_имм рдиай, 1 ; stdout = 1
переместить_имм рсиай, msg ; указатель на строку
переместить_имм рдикс, 21 ; длина строки
вызов_системы
; Системный вызов exit(0)
переместить_имм раикс, 60 ; номер syscall exit = 60
переместить_имм рдиай, 0 ; код возврата
вызов_системы
.данные
msg: .строка "Привет мир!\n"

View file

@ -1,153 +0,0 @@
.текст
.глобал _start
_start:
; ========== БАЗОВЫЕ ИНСТРУКЦИИ ==========
переместить раикс, рбикс
переместить рсипи, рдиай
переместить р8, р9
переместить_имм раикс, 0x12345678
переместить_имм еаикс, 0x1234
переместить_имм аикс, 0x12
переместить_имм ал, 0x42
прибавить раикс, рбикс
вычесть рсипи, рдиай
вызвать some_function
вернуться
вызов_системы
; ========== СРАВНЕНИЯ И ПЕРЕХОДЫ ==========
сравнить раикс, рбикс
сравнить32 еаикс, ебикс
сравнить_байт ал, бл
переход loop
переход_если_равно loop
переход_если_неравно loop
переход_если_ноль loop
переход_если_неоль loop
переход_если_перенос loop
переход_если_нет_переноса loop
переход_если_меньше loop
переход_если_больше loop
переход_если_меньше_или_равно loop
переход_если_больше_или_равно loop
ороткий_переход loop
; ========== АРИФМЕТИКА ==========
прибавить_непосредственно раикс, 100
вычесть_непосредственно рбикс, 50
увеличить раикс
уменьшить рбикс
увеличить ал
уменьшить бл
умножить раикс
умножить_знаковое рбикс
разделить рсикс
разделить_знаковое рдикс
отрицать раикс
инвертировать рбикс
; ========== ЛОГИЧЕСКИЕ ОПЕРАЦИИ ==========
и раикс, рбикс
или рсипи, рдиай
исключающее_или р8, р9
проверить раикс, рбикс
очистить раикс
; ========== СДВИГИ И ВРАЩЕНИЯ ==========
;сдвиг_влево раикс, 4
;сдвиг_вправо рбикс, 2
;сдвиг_арифметический_влево рсикс, 1
;сдвиг_арифметический_вправо рдикс, 3
ращать_влево р8, 5
ращать_вправо р9, 6
; ========== СТЕК И ПАМЯТЬ ==========
втолкнуть раикс
втолкнуть рбикс
втолкнуть_из_памяти [рсипи]
вытолкнуть рдиай
вытолкнуть_в_память [рбипи]
загрузить раикс, [рсипи]
сохранить [рдиай], рбикс
загрузить_адрес раикс, some_data
; ========== 8-БИТНЫЕ ОПЕРАЦИИ ==========
переместить_имм ал, 0xAA
прибавить_байт ал, бл
вычесть_байт бл, кл
сравнить_байт дл, бл
сохранить_байт [раикс], ал
сохранить_в_адрес [рбикс], ал
; ========== РАСШИРЕНИЕ РЕГИСТРОВ ==========
переместить_сулями раикс, ал
переместить_со_знаком рбикс, бл
; ========== ОБМЕН ==========
обменять раикс, рбикс
;обменять_с_памятью [рсипи], рдиай
; ========== ФЛАГИ ==========
установить_перенос
сбросить_перенос
установить_направление
сбросить_направление
втолкнуть_флаги
вытолкнуть_флаги
; ========== СТРОКОВЫЕ ОПЕРАЦИИ ==========
переместить_байт
переместить_слово
сравнить_байты
сканировать_байт
; ========== СПЕЦИАЛЬНЫЕ КОМАНДЫ ==========
идентифицировать_процессор
прочитать_счетчик
получить_управление
отладка
прервать 0x80
; ========== ВВОД-ВЫВОД ==========
ввод_байта 0x60
вывод_байта 0x61
; ========== ЦИКЛЫ ==========
цикл loop
; ========== .БАЙТ ДИРЕКТИВА ==========
.байт 0x90 0x90 0x90 ; NOP-ы для выравнивания
loop:
; Метка для переходов
нет_операции
остановить
some_function:
вернуться
.данные
some_data: .строка "Test data"
;constant_value: .константа 0xDEADBEEF
; ========== РАЗНЫЕ ТИПЫ ДАННЫХ ==========
byte_array: .байт 0x01 0x02 0x03 0x04
string_data: .строкауль "Null terminated"
wide_string: .строка "Wide string test"
; Глобальная константа
;.глобал global_var
;global_var: .константа 42

337
тесты/t1.py Normal file
View file

@ -0,0 +1,337 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Тестовый скрипт для проверки инструкций с фиксированной длиной
Запуск: python3 test_fixed_instructions.py [--keep-files]
"""
import sys
import os
import subprocess
import csv
# ANSI color codes
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
RESET = '\033[0m'
# === ТЕСТОВЫЙ ИСХОДНИК ===
TEST_SOURCE = '''.глобал _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
вызов_системы
'''
# === ОЖИДАЕМЫЕ РЕЗУЛЬТАТЫ ===
# Для инструкций с фиксированным кодом — полная проверка
EXPECTED_INSTRUCTIONS_FULL = {
"нет_операции": [0x90],
"вызов_системы": [0x0F, 0x05],
"вернуться": [0xC3],
"втолкнуть раикс": [0x50],
"втолкнуть р8": [0x41, 0x50],
"вытолкнуть раикс": [0x58],
"вытолкнуть р8": [0x41, 0x58],
"переместить раикс, рбикс": [0x48, 0x89, 0xD8],
"переместить р8, р9": [0x4D, 0x89, 0xC8],
"сравнить раикс, рбикс": [0x48, 0x39, 0xD8],
"сравнить р8, р9": [0x4D, 0x39, 0xC8],
"проверить раикс, рбикс": [0x48, 0x85, 0xD8],
"проверить р8, р9": [0x4D, 0x85, 0xC8],
"прибавить раикс, рбикс": [0x48, 0x01, 0xD8],
"прибавить р8, р9": [0x4D, 0x01, 0xC8],
"вычесть раикс, рбикс": [0x48, 0x29, 0xD8],
"вычесть р8, р9": [0x4D, 0x29, 0xC8],
"и раикс, рбикс": [0x48, 0x21, 0xD8],
"и р8, р9": [0x4D, 0x21, 0xC8],
"или раикс, рбикс": [0x48, 0x09, 0xD8],
"или р8, р9": [0x4D, 0x09, 0xC8],
"исключающее_или раикс, рбикс": [0x48, 0x31, 0xD8],
"исключающее_или р8, р9": [0x4D, 0x31, 0xC8],
"увеличить раикс": [0x48, 0xFF, 0xC0],
"увеличить р8": [0x49, 0xFF, 0xC0],
"увеличить еаикс": [0xFF, 0xC0],
"уменьшить раикс": [0x48, 0xFF, 0xC8],
"уменьшить р8": [0x49, 0xFF, 0xC8],
"уменьшить еаикс": [0xFF, 0xC8],
"инвертировать раикс": [0x48, 0xF7, 0xD0],
"инвертировать еаикс": [0xF7, 0xD0],
"отрицать раикс": [0x48, 0xF7, 0xD8],
"отрицать еаикс": [0xF7, 0xD8],
"умножить рбикс": [0x48, 0xF7, 0xE0],
"разделить рбикс": [0x48, 0xF7, 0xF0],
"умножить_знаковое рбикс": [0x48, 0xF7, 0xE8],
"разделить_знаковое рбикс": [0x48, 0xF7, 0xF8],
"сдвиг_влево раикс, 2": [0x48, 0xC1, 0xE0, 0x02],
"сдвиг_вправо раикс, 2": [0x48, 0xC1, 0xE8, 0x02],
"сдвиг_арифметический_вправо раикс, 2": [0x48, 0xC1, 0xF8, 0x02],
"вращать_влево раикс, 2": [0x48, 0xC1, 0xC0, 0x02],
"вращать_вправо раикс, 2": [0x48, 0xC1, 0xC8, 0x02],
"переместить_имм раикс, 10": [0x48, 0xB8, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
"переместить_имм рбикс, 5": [0x48, 0xBB, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
"переместить_имм раикс, 100": [0x48, 0xB8, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
"переместить_имм рбикс, 7": [0x48, 0xBB, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
"переместить_имм раикс, 60": [0x48, 0xB8, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
"переместить_имм рдикс, 0": [0x48, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
}
# Инструкции, у которых проверяем только opcode (первые байты)
EXPECTED_INSTRUCTIONS_PREFIX = {
"переход метка_длинная": [0xE9], # JMP rel32
"короткий_переход меткаороткая": [0xEB], # JMP SHORT
"цикл меткаороткая": [0xE2], # LOOP
"вызвать метка_длинная": [0xE8], # CALL rel32
}
def read_csv_instructions(csv_file):
"""Читает CSV и возвращает словарь {мнемоника: [байты]}"""
instructions = {}
current_mnemonic = None
current_bytes = []
with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.reader(f, delimiter=';')
header = next(reader)
for row in reader:
if len(row) < 4:
continue
cmd = row[3].strip()
byte_str = row[1].strip()
if not byte_str:
continue
byte_val = int(byte_str, 16)
if cmd:
if current_mnemonic and current_bytes:
instructions[current_mnemonic] = current_bytes
current_mnemonic = cmd
current_bytes = [byte_val]
else:
if current_mnemonic:
current_bytes.append(byte_val)
if current_mnemonic and current_bytes:
instructions[current_mnemonic] = current_bytes
return instructions
def run_tests(generated_instructions):
"""Сравнивает сгенерированные инструкции с ожидаемыми"""
errors = []
missing = []
# Проверяем инструкции с полным совпадением
for mnemonic, expected_bytes in EXPECTED_INSTRUCTIONS_FULL.items():
if mnemonic not in generated_instructions:
missing.append(mnemonic)
continue
actual_bytes = generated_instructions[mnemonic]
if actual_bytes != expected_bytes:
expected_hex = ' '.join(f'{b:02X}' for b in expected_bytes)
actual_hex = ' '.join(f'{b:02X}' for b in actual_bytes)
errors.append(f" {mnemonic}: ожидалось [{expected_hex}], получено [{actual_hex}]")
# Проверяем инструкции только по префиксу
for mnemonic, expected_prefix in EXPECTED_INSTRUCTIONS_PREFIX.items():
if mnemonic not in generated_instructions:
missing.append(mnemonic)
continue
actual_bytes = generated_instructions[mnemonic]
prefix_len = len(expected_prefix)
if len(actual_bytes) < prefix_len:
errors.append(f" {mnemonic}: слишком короткая ({len(actual_bytes)} байт, ожидалось минимум {prefix_len})")
elif actual_bytes[:prefix_len] != expected_prefix:
expected_hex = ' '.join(f'{b:02X}' for b in expected_prefix)
actual_hex = ' '.join(f'{b:02X}' for b in actual_bytes[:prefix_len])
errors.append(f" {mnemonic}: префикс [{expected_hex}...], получено [{actual_hex}...]")
return errors, missing
def cleanup(files_to_delete, keep_files=False):
"""Удаляет временные файлы"""
if keep_files:
print("\nФлаг --keep-files указан, промежуточные файлы сохранены:")
for f in files_to_delete:
if os.path.exists(f):
print(f" {f}")
return
deleted = []
for f in files_to_delete:
if os.path.exists(f):
os.remove(f)
deleted.append(f)
if deleted:
print(f"\nУдалено {len(deleted)} временных файлов")
def main():
keep_files = '--keep-files' in sys.argv or '-k' in sys.argv
base_name = "т1"
source_file = f"{base_name}.квс"
tokens_file = f"{base_name}.токены"
ast_file = f"{base_name}.аст"
p1_file = f"{base_name}.проход1"
csv_file = f"{base_name}.csv"
elf_file = f"{base_name}.elf"
temp_files = [tokens_file, ast_file, p1_file, csv_file, elf_file]
print("КВС Тестировщик - Инструкции с фиксированной длиной\n")
print("=" * 60)
# Шаг 1: Создание исходного файла
print("Шаг 1: Создание тестового исходника...")
with open(source_file, 'w', encoding='utf-8') as f:
f.write(TEST_SOURCE)
print(f" Создан {source_file}")
# Шаг 2: Компиляция
print("\nШаг 2: Компиляция...")
result = subprocess.run(
[sys.executable, "kvs_build.py", source_file],
capture_output=True,
text=True
)
if result.returncode != 0:
print(" ОШИБКА компиляции:")
print(result.stderr)
print("\nОставшиеся файлы для отладки:")
print(f" {source_file}")
if os.path.exists(csv_file):
print(f" {csv_file}")
sys.exit(1)
print(" Компиляция успешна")
# Шаг 3: Проверка результатов
print("\nШаг 3: Проверка сгенерированного кода...")
if not os.path.exists(csv_file):
print(f" Файл {csv_file} не найден!")
sys.exit(1)
generated_instructions = read_csv_instructions(csv_file)
errors, missing = run_tests(generated_instructions)
# Вывод результатов
print("\n" + "=" * 60)
print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ:")
print("=" * 60)
if missing:
print(f"\n{YELLOW}ОТСУТСТВУЮТ ИНСТРУКЦИИ ({len(missing)}):{RESET}")
for m in missing[:10]:
print(f" - {m}")
if len(missing) > 10:
print(f" ... и ещё {len(missing)-10}")
if errors:
print(f"\n{RED}НАЙДЕНО {len(errors)} ОШИБОК В КОДЕ:{RESET}")
for err in errors:
print(err)
success = False
else:
print(f"\n{GREEN}НЕТ ОШИБОК! ВСЕ ИНСТРУКЦИИ ЗАКОДИРОВАНЫ ВЕРНО!{RESET}")
success = True
# Статистика
print(f"\nСтатистика:")
print(f" Ожидалось инструкций (полная проверка): {len(EXPECTED_INSTRUCTIONS_FULL)}")
print(f" Ожидалось инструкций (проверка префикса): {len(EXPECTED_INSTRUCTIONS_PREFIX)}")
print(f" Сгенерировано инструкций: {len(generated_instructions)}")
# Шаг 4: Очистка
cleanup([source_file] + temp_files, keep_files)
# Финальный вердикт
print("\n" + "=" * 60)
if success:
print(f"{GREEN}ТЕСТЫ ПРОЙДЕНЫ УСПЕШНО!{RESET}")
sys.exit(0)
else:
print(f"{RED}ТЕСТЫ НЕ ПРОЙДЕНЫ!{RESET}")
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -832,4 +832,4 @@ _start:
.константа len_msg_dead_2 = 19
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 + 1 (\n от .строкауль) = 14 + 4 + 1 = 19 байт на содержимое. len = 19
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 19
.константа len_msg_dead_1 = 19

View file

@ -15,4 +15,4 @@ _start:
вызов_системы
.данные
msg: .строка "Привет мир!\n"
msg: .строка "Привет мир!\n"

View file

@ -201,4 +201,4 @@ _start:
.константа len_msg_dead_2 = 18
; "Мертвый код 1!" = 7 букв * 2 + 4 символа * 1 = 14 + 4 = 18 байт на содержимое. len = 18
msg_dead_1: .строкауль "Мертвый код 1!"
.константа len_msg_dead_1 = 18
.константа len_msg_dead_1 = 18

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

@ -0,0 +1,112 @@
.глобал _start
.текст
_start:
; ===== ГРУППА 1: Без операндов (реализованные) =====
нет_операции
вызов_системы
вернуться
; ===== ГРУППА 2: PUSH/POP =====
втолкнуть раикс
втолкнуть р8
вытолкнуть раикс
вытолкнуть р8
; ===== ГРУППА 3: Регистр-регистр =====
переместить раикс, рбикс
переместить р8, р9
сравнить раикс, рбикс
сравнить р8, р9
проверить раикс, рбикс
проверить р8, р9
прибавить раикс, рбикс
прибавить р8, р9
вычесть раикс, рбикс
вычесть р8, р9
и раикс, рбикс
и р8, р9
или раикс, рбикс
или р8, р9
исключающее_или раикс, рбикс
исключающее_или р8, р9
; ===== ГРУППА 4: INC/DEC =====
увеличить раикс
увеличить р8
увеличить еаикс
уменьшить раикс
уменьшить р8
уменьшить еаикс
; ===== ГРУППА 5: NOT/NEG =====
инвертировать раикс
инвертировать еаикс
отрицать раикс
отрицать еаикс
; ===== ГРУППА 6: MUL/DIV =====
переместить_имм раикс, 10
переместить_имм рбикс, 5
умножить рбикс
переместить_имм раикс, 100
переместить_имм рбикс, 7
разделить рбикс
умножить_знаковое рбикс
разделить_знаковое рбикс
; ===== ГРУППА 7: Битовые операции =====
сдвиг_влево раикс, 2
сдвиг_вправо раикс, 2
сдвиг_арифметический_вправо раикс, 2
вращать_влево раикс, 2
вращать_вправо раикс, 2
; ===== ГРУППА 8: Переходы =====
переход метка_длинная
переход_если_равно метка_длинная
переход_если_неравно метка_длинная
переход_если_меньше метка_длинная
переход_если_больше метка_длинная
переход_если_меньше_или_равно метка_длинная
переход_если_больше_или_равно метка_длинная
переход_если_перенос метка_длинная
переход_если_нет_переноса метка_длинная
короткий_переход меткаороткая
короткий_переход_если_равно меткаороткая
короткий_переход_если_неравно меткаороткая
короткий_переход_если_меньше меткаороткая
короткий_переход_если_больше меткаороткая
короткий_переход_если_меньше_или_равно меткаороткая
короткий_переход_если_больше_или_равно меткаороткая
короткий_переход_если_перенос меткаороткая
короткий_переход_если_нет_переноса меткаороткая
цикл меткаороткая
вызвать метка_длинная
метка_длинная:
нет_операции
нет_операции
нет_операции
меткаороткая:
нет_операции
; Выход
переместить_имм раикс, 60
переместить_имм рдикс, 0
вызов_системы