пересборка
This commit is contained in:
parent
054dcad97d
commit
4c5a952bae
559
README.html
Normal file
559
README.html
Normal 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>р8д</code>…<code>р15д</code></td><td><code>р8в</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
418
README.md
Normal 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-исполняемый файл. Декомпозированная архитектура компилятора облегчает понимание этапов трансляции и модификацию отдельных модулей.
|
||||
589
elfreader.py
Normal file
589
elfreader.py
Normal 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()
|
||||
2106
jcc/asm24.py
2106
jcc/asm24.py
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
вызов_системы
|
||||
160
jumps/csvhtml.py
160
jumps/csvhtml.py
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
653
jumps/kvs_5.py
653
jumps/kvs_5.py
|
|
@ -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д": 8, "р9д": 9, "р10д": 10, "р11д": 11,
|
||||
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
|
||||
# 16-битные
|
||||
"аикс": 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-битные младшие
|
||||
"ал": 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)
|
||||
930
jumps/kvs_6.py
930
jumps/kvs_6.py
|
|
@ -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д": 8, "р9д": 9, "р10д": 10, "р11д": 11,
|
||||
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
|
||||
# 16-битные
|
||||
"аикс": 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-битные младшие
|
||||
"ал": 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-битное значение вне диапазона 0–255")
|
||||
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-битное значение вне диапазона 0–255")
|
||||
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)
|
||||
933
jumps/kvs_7.py
933
jumps/kvs_7.py
|
|
@ -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д": 8, "р9д": 9, "р10д": 10, "р11д": 11,
|
||||
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
|
||||
# 16-битные
|
||||
"аикс": 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-битные младшие
|
||||
"ал": 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-битное значение вне диапазона 0–255")
|
||||
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-битное значение вне диапазона 0–255")
|
||||
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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 без нулевого байта
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
52
kvs_build.py
Normal 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
165
kvs_builder.py
Normal 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
299
kvs_data.py
Normal 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д": 8, # R8D - младшие 32 бита R8
|
||||
"р9д": 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в": 8, # R8W - младшие 16 бит R8
|
||||
"р9в": 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
128
kvs_lexer.py
Normal 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
168
kvs_parser.py
Normal 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
191
kvs_pass1.py
Normal 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
1261
kvs_pass2.py
Normal file
File diff suppressed because it is too large
Load diff
2010
nop/asm23.py
2010
nop/asm23.py
File diff suppressed because it is too large
Load diff
2081
nop/asm23_.py
2081
nop/asm23_.py
File diff suppressed because it is too large
Load diff
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
section .text
|
||||
global _start
|
||||
|
||||
_start:
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
.текст
|
||||
.глобал _start
|
||||
|
||||
_start:
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
нет_операции
|
||||
|
|
@ -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
|
||||
строковые операции
|
||||
ввод-вывод
|
||||
|
|
@ -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")
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
715
readme.html
715
readme.html
|
|
@ -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>р8д</code> ... <code>р15д</code></td>
|
||||
<td><code>р8в</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
154
readme.md
|
|
@ -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 могут быть заполнены упрощённо.
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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д": 8, "р9д": 9, "р10д": 10, "р11д": 11,
|
||||
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
|
||||
# 16-битные
|
||||
"аикс": 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-битные младшие
|
||||
"ал": 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)
|
||||
|
|
@ -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д": 8, "р9д": 9, "р10д": 10, "р11д": 11,
|
||||
"р12д": 12, "р13д": 13, "р14д": 14, "р15д": 15,
|
||||
# 16-битные
|
||||
"аикс": 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-битные младшие
|
||||
"ал": 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-битное значение вне диапазона 0–255")
|
||||
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-битное значение вне диапазона 0–255")
|
||||
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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
273
todo.txt
Normal 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 0–15 или None)
|
||||
- `has_index` (bool)
|
||||
- `index_reg` (int 0–15)
|
||||
- `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, строки)
|
||||
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
План готов. Когда скажете — начинаем писать код по шагам.
|
||||
|
|
@ -933,4 +933,4 @@ except SystemExit:
|
|||
raise
|
||||
except Exception as e:
|
||||
print("Внутренняя ошибка ассемблера: " + str(e), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
sys.exit(1)
|
||||
|
|
@ -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)
|
||||
|
||||
90
доки/kvs_instructions.csv
Normal file
90
доки/kvs_instructions.csv
Normal 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
|
||||
|
34
доки/запуск
Normal file
34
доки/запуск
Normal 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$
|
||||
1999
квс/asm21_.py
1999
квс/asm21_.py
File diff suppressed because it is too large
Load diff
2011
квс/asm22.py
2011
квс/asm22.py
File diff suppressed because it is too large
Load diff
2010
квс/asm23.py
2010
квс/asm23.py
File diff suppressed because it is too large
Load diff
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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]
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
section .text
|
||||
global _start
|
||||
_start:
|
||||
push qword [r12]
|
||||
pop qword [r12]
|
||||
push qword [r13]
|
||||
pop qword [r13]
|
||||
153
квс/тест.квс
153
квс/тест.квс
|
|
@ -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
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
.текст
|
||||
_старт:
|
||||
; Простые случаи с разными базовыми регистрами
|
||||
втолкнуть_из_памяти [раикс]
|
||||
вытолкнуть_в_память [раикс]
|
||||
|
||||
втолкнуть_из_памяти [рбикс]
|
||||
вытолкнуть_в_память [рбикс]
|
||||
|
||||
втолкнуть_из_памяти [рсипи]
|
||||
вытолкнуть_в_память [рсипи]
|
||||
|
||||
втолкнуть_из_памяти [рбипи]
|
||||
вытолкнуть_в_память [рбипи]
|
||||
|
||||
; Завершение программы
|
||||
;переместить раикс, 60
|
||||
;переместить рсикс, 0
|
||||
;вызов_системы
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
.текст
|
||||
_старт:
|
||||
; Расширенные регистры R8-R15
|
||||
втолкнуть_из_памяти [р8]
|
||||
вытолкнуть_в_память [р8]
|
||||
|
||||
втолкнуть_из_памяти [р9]
|
||||
вытолкнуть_в_память [р9]
|
||||
|
||||
втолкнуть_из_памяти [р10]
|
||||
вытолкнуть_в_память [р10]
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.текст
|
||||
_старт:
|
||||
; R12 и R13 требуют особой обработки как RSP/RBP
|
||||
втолкнуть_из_памяти [р12]
|
||||
вытолкнуть_в_память [р12]
|
||||
|
||||
втолкнуть_из_памяти [р13]
|
||||
вытолкнуть_в_память [р13]
|
||||
18
привет.квс
18
привет.квс
|
|
@ -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"
|
||||
153
тест.квс
153
тест.квс
|
|
@ -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
337
тесты/t1.py
Normal 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()
|
||||
|
|
@ -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
|
||||
|
|
@ -15,4 +15,4 @@ _start:
|
|||
вызов_системы
|
||||
|
||||
.данные
|
||||
msg: .строка "Привет мир!\n"
|
||||
msg: .строка "Привет мир!\n"
|
||||
|
|
@ -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
112
тесты/т1_.квс
Normal 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
|
||||
вызов_системы
|
||||
Loading…
Reference in a new issue