kvs/README.md

531 lines
21 KiB
Markdown
Raw Permalink Normal View History

2026-05-02 23:17:09 +03:00
```markdown
2026-05-17 17:18:35 +03:00
# КВС — Ассемблер с русским синтаксисом для x86-64
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
**КВС** — это экспериментальный ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит образовательный характер и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код.
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
## 📋 Оглавление
- [Особенности](#-особенности)
- [Архитектура](#-архитектура)
- [Установка](#-установка)
- [Быстрый старт](#-быстрый-старт)
- [Синтаксис](#-синтаксис)
- [Система команд](#-система-команд)
- [Регистры](#-регистры-процессора)
- [Директивы](#-директивы-ассемблера)
- [Примеры программ](#-примеры-программ)
- [Отладка](#-отладка-и-проверка)
- [Ограничения](#-ограничения)
- [План развития](#-план-развития)
2026-05-02 23:17:09 +03:00
- [Лицензия](#-лицензия)
## ✨ Особенности
- **Полностью русский синтаксис** — все инструкции, регистры и директивы на кириллице
- **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску
- **Двухпроходная сборка** — традиционная схема с разрешением меток
2026-05-17 17:18:35 +03:00
- **Поддержка секций** — разделение на `.текст` (код), `.данные` (данные) и `.бсс` (неинициализированные данные)
- **Гибкая адресация памяти**:
- абсолютная адресация по меткам `[переменная]`
- абсолютная адресация по числовому адресу `[0x402000]`
- косвенная адресация через регистр `[раикс]`
- всё с RIP-relative кодированием для 64-битных перемещений
2026-05-02 23:17:09 +03:00
- **CSV-логирование** — детальный файл с соответствием адресов, байтов и исходных команд
- **Более 50 инструкций** — полный набор основных команд x86-64
- **Минимализм зависимостей** — только стандартная библиотека Python
- **Рефакторинг** — кодогенерация вынесена в отдельный модуль `kvs_pass2_encoder.py`
2026-05-02 23:17:09 +03:00
## 🏗 Архитектура
Процесс трансляции исходного кода `.квс` в исполняемый файл `.elf` проходит через несколько этапов:
```
Исходный файл (.квс)
2026-05-17 17:18:35 +03:00
▼ (kvs_lexer.py)
2026-05-02 23:17:09 +03:00
Файл токенов (.токены)
2026-05-17 17:18:35 +03:00
▼ (kvs_parser.py)
2026-05-02 23:17:09 +03:00
AST-представление (.аст)
2026-05-17 17:18:35 +03:00
▼ (kvs_pass1.py)
2026-05-02 23:17:09 +03:00
Первый проход: размеры, метки (.проход1)
2026-05-17 17:18:35 +03:00
▼ (kvs_pass2.py + kvs_pass2_encoder.py)
2026-05-02 23:17:09 +03:00
Второй проход: генерация кода (.csv)
2026-05-17 17:18:35 +03:00
▼ (kvs_builder.py)
2026-05-02 23:17:09 +03:00
ELF-компоновщик (.elf)
2026-05-17 17:18:35 +03:00
2026-05-02 23:17:09 +03:00
Исполняемый файл!
```
### Модули компилятора
2026-05-02 23:04:46 +03:00
| Файл | Назначение |
2026-05-17 17:18:35 +03:00
|------|-------------|
2026-05-02 23:17:09 +03:00
| `kvs_build.py` | Главный сборочный скрипт |
2026-05-02 23:04:46 +03:00
| `kvs_lexer.py` | Лексический анализ, разбор на токены |
| `kvs_parser.py` | Синтаксический анализ, построение AST |
| `kvs_pass1.py` | Первый проход: вычисление размеров и адресов меток |
| `kvs_pass2.py` | Второй проход: обход AST, генерация CSV |
| `kvs_pass2_encoder.py` | Кодировщик инструкций (все encode_* функции) |
2026-05-02 23:17:09 +03:00
| `kvs_builder.py` | Сборка ELF-файла из CSV |
| `kvs_data.py` | Общие данные (регистры, инструкции) |
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
## 🔧 Установка
2026-05-02 23:04:46 +03:00
```bash
2026-05-02 23:17:09 +03:00
# Клонирование репозитория
2026-05-17 17:18:35 +03:00
git clone https://github.com/artradeskz/kvs.git
2026-05-02 23:17:09 +03:00
cd kvs
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
# Установка прав на выполнение (при необходимости)
chmod +x kvs_build.py
# Проверка работы
python3 kvs_build.py --help
2026-05-02 23:04:46 +03:00
```
2026-05-02 23:17:09 +03:00
## 🚀 Быстрый старт
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### 1. Создайте файл `hello.квс`:
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:17:09 +03:00
.текст
.глобал _start
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
_start:
; write(1, msg, 13)
2026-05-17 17:18:35 +03:00
переместить_имм раикс,1
переместить_имм рдиай,1
2026-05-02 23:17:09 +03:00
переместить_имм рсиай, msg
2026-05-17 17:18:35 +03:00
переместить_имм рдикс,13
2026-05-02 23:17:09 +03:00
вызов_системы
; exit(0)
2026-05-17 17:18:35 +03:00
переместить_имм раикс,60
переместить_имм рдиай,0
2026-05-02 23:17:09 +03:00
вызов_системы
.данные
msg: .строкауль "Hello, World!"
```
### 2. Скомпилируйте:
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
```bash
python3 kvs_build.py hello.квс
```
### 3. Запустите:
```bash
./hello.elf
```
**Вывод:**
2026-05-17 17:18:35 +03:00
2026-05-02 23:17:09 +03:00
```
Hello, World!
```
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
## 📝 Синтаксис
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### Структура программы
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
.текст ; секция кода (обязательна)
; ... инструкции ...
.данные ; секция данных (опциональна)
2026-05-17 17:18:35 +03:00
; ... инициализированные данные ...
.бсс ; секция неинициализированных данных (опциональна)
; ... переменные без начального значения ...
2026-05-02 23:04:46 +03:00
```
2026-05-02 23:17:09 +03:00
### Метки
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
Метка — это имя, за которым следует двоеточие:
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
_start: ; метка _start
2026-05-17 17:18:35 +03:00
переместить_имм раикс,1
2026-05-02 23:04:46 +03:00
переход _start ; переход на метку
```
2026-05-02 23:17:09 +03:00
### Комментарии
2026-05-02 23:04:46 +03:00
Однострочные комментарии начинаются с `;`:
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
; Это комментарий
2026-05-17 17:18:35 +03:00
переместить_имм раикс,42 ; комментарий после инструкции
2026-05-02 23:04:46 +03:00
```
2026-05-17 17:18:35 +03:00
### Работа с памятью
2026-05-17 17:18:35 +03:00
```assembly
; Запись значения в память по метке
2026-05-17 17:18:35 +03:00
переместить_имм раикс,42
сохранить [переменная], раикс
; Чтение значения из памяти
2026-05-17 17:18:35 +03:00
загрузить рбикс,[переменная]
; Запись по абсолютному адресу
сохранить [0x402000], раикс
2026-05-17 17:18:35 +03:00
; Косвенная адресация через регистр
переместить_имм рбикс, переменная
загрузить раикс, [рбикс]
; Работа с BSS-секцией
.бсс
счётчик: .байт 0
```
2026-05-02 23:17:09 +03:00
## 📖 Система команд
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### Загрузка констант
2026-05-02 23:04:46 +03:00
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| `переместить_имм` | `переместить_имм рег, значение` | Загрузить константу в регистр |
2026-05-17 17:18:35 +03:00
```assembly
переместить_имм раикс,42 ; RAX = 42
переместить_имм еаикс,0x7FFF ; EAX = 32767
переместить_имм ал,0xFF ; AL = 255
2026-05-02 23:04:46 +03:00
```
### Работа с памятью
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
2026-05-17 17:18:35 +03:00
| `загрузить` | `загрузить рег, [метка/адрес/регистр]` | Загрузить значение из памяти |
| `сохранить` | `сохранить [метка/адрес/регистр], рег` | Сохранить значение в память |
2026-05-17 17:18:35 +03:00
```assembly
загрузить раикс,[переменная] ; RAX = *переменная
сохранить [результат], раикс ; *результат = RAX
2026-05-17 17:18:35 +03:00
загрузить рбикс,[0x402000] ; загрузка по абсолютному адресу
загрузить раикс,[рбикс] ; косвенная загрузка через регистр
```
2026-05-02 23:17:09 +03:00
### Арифметика и логика
2026-05-02 23:04:46 +03:00
| Инструкция | Синтаксис | Описание |
|------------|-----------|----------|
| `прибавить` | `прибавить рег1, рег2` | рег1 += рег2 |
| `вычесть` | `вычесть рег1, рег2` | рег1 -= рег2 |
| `увеличить` | `увеличить рег` | рег++ |
| `уменьшить` | `уменьшить рег` | рег-- |
2026-05-02 23:17:09 +03:00
| `сравнить` | `сравнить рег1, рег2` | Сравнить регистры |
| `сравнить_с` | `сравнить_с рег, значение` | Сравнить с константой |
| `проверить` | `проверить рег1, рег2` | TEST (логическое AND) |
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
прибавить раикс, рбикс ; RAX += RBX
увеличить рсикс ; RCX++
2026-05-17 17:18:35 +03:00
сравнить раикс,10 ; сравнить RAX с 10
2026-05-02 23:04:46 +03:00
```
2026-05-02 23:17:09 +03:00
### Переходы
| Инструкция | Условие |
|------------|---------|
| `переход` | Безусловный |
| ороткий_переход` | Безусловный (смещение ±127) |
| `переход_если_равно` | ZF = 1 |
| `переход_если_неравно` | ZF = 0 |
| `переход_если_меньше` | SF ≠ OF (signed <) |
| `переход_если_больше` | ZF = 0 и SF = OF (signed >) |
| `переход_если_меньше_или_равно` | ZF = 1 или SF ≠ OF |
| `переход_если_больше_или_равно` | SF = OF |
| `переход_если_перенос` | CF = 1 |
| `переход_если_нет_переноса` | CF = 0 |
2026-05-02 23:04:46 +03:00
**Короткие условные переходы** (префикс ороткий_`):
- ороткий_переход_если_равно`, ороткий_переход_если_неравно`, и т.д.
- Генерируют 2-байтовые инструкции (смещение ±127 байт)
2026-05-02 23:17:09 +03:00
### Системные вызовы
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
| Инструкция | Описание |
|------------|----------|
| `вызов_системы` | Вызов ядра Linux (syscall) |
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
; Пример: завершение программы
2026-05-17 17:18:35 +03:00
переместить_имм раикс,60 ; syscall 60 = exit
переместить_имм рдиай,0 ; код возврата 0
2026-05-02 23:04:46 +03:00
вызов_системы
```
2026-05-02 23:17:09 +03:00
## 💾 Регистры процессора
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
КВС поддерживает все основные 64-битные регистры x86-64:
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
| 64-бит | 32-бит | 16-бит | 8-бит (мл.) |
|--------|--------|--------|-------------|
| `раикс` | `еаикс` | `аикс` | `ал` |
| `рбикс` | `ебикс` | `бикс` | `бл` |
| `рсикс` | `есикс` | `сикс` | `кл` |
| `рдикс` | `едикс` | `дикс` | `дл` |
| `рсипи` | `есипи` | `эсп` | `спл` |
| `рбипи` | `ебипи` | `бипи` | `бпл` |
| `рсиай` | `есиай` | `эс` | `сил` |
| `рдиай` | `едиай` | `ди` | `дил` |
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
## 🔧 Директивы ассемблера
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
| Директива | Назначение | Пример |
|-----------|------------|--------|
| `.текст` | Начало секции кода | `.текст` |
2026-05-17 17:18:35 +03:00
| `.данные` | Начало секции инициализированных данных | `.данные` |
| `.бсс` | Начало секции неинициализированных данных | `.бсс` |
2026-05-02 23:17:09 +03:00
| `.глобал` | Объявление глобальной метки | `.глобал _start` |
| `.строка` | Строка без завершающего нуля | `.строка "Hello"` |
| `.строкауль` | Строка с завершающим нулём | `.строкауль "Hello"` |
| `.байт` | Последовательность байтов | `.байт 0x48, 0x65, 108` |
| `.константа` | Определение константы | `.константа LEN = 10` |
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
## 📝 Примеры программ
2026-05-02 23:04:46 +03:00
### Работа с памятью (переменные)
2026-05-17 17:18:35 +03:00
```assembly
.текст
.глобал _start
_start:
; Запись значения в переменную
2026-05-17 17:18:35 +03:00
переместить_имм раикс,42
сохранить [переменная], раикс
; Чтение значения из переменной
2026-05-17 17:18:35 +03:00
загрузить рбикс,[переменная]
; Завершение с кодом возврата
2026-05-17 17:18:35 +03:00
переместить_имм раикс,60
загрузить рдиай,[переменная]
вызов_системы
.данные
переменная: .байт 0
```
2026-05-02 23:17:09 +03:00
### Цикл и условный переход
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:04:46 +03:00
.текст
.глобал _start
_start:
2026-05-17 17:18:35 +03:00
переместить_имм рсикс,10 ; счетчик = 10
2026-05-02 23:04:46 +03:00
loop:
2026-05-17 17:18:35 +03:00
переместить_имм раикс,1
переместить_имм рдиай,1
2026-05-02 23:04:46 +03:00
переместить_имм рсиай, msg
2026-05-17 17:18:35 +03:00
переместить_имм рдикс,10
2026-05-02 23:04:46 +03:00
вызов_системы
уменьшить рсикс ; счетчик--
2026-05-17 17:18:35 +03:00
сравнить_с рсикс,0
2026-05-02 23:04:46 +03:00
переход_если_больше loop ; если > 0, продолжаем
2026-05-17 17:18:35 +03:00
переместить_имм раикс,60
переместить_имм рдиай,0
2026-05-02 23:04:46 +03:00
вызов_системы
.данные
msg: .строка "Iteration\n"
2026-05-02 23:04:46 +03:00
```
2026-05-02 23:17:09 +03:00
### Вычисление суммы чисел от 1 до 10
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
```assembly
2026-05-02 23:17:09 +03:00
.текст
.глобал _start
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
_start:
2026-05-17 17:18:35 +03:00
переместить_имм р8,0 ; сумма
переместить_имм р9,10 ; счётчик
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
loop:
прибавить р8, р9
уменьшить р9
2026-05-17 17:18:35 +03:00
сравнить_с р9,0
2026-05-02 23:17:09 +03:00
переход_если_больше loop
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
; exit(сумма в r8)
2026-05-17 17:18:35 +03:00
переместить_имм раикс,60
2026-05-02 23:17:09 +03:00
переместить рдикс, р8
вызов_системы
2026-05-02 23:04:46 +03:00
```
2026-05-17 17:18:35 +03:00
### Использование BSS-секции
```assembly
.текст
.глобал _start
_start:
; инициализация счётчика в BSS
переместить_имм раикс,0
сохранить [счётчик], раикс
; цикл: увеличить счётчик 10 раз
переместить_имм рсикс,10
loop:
загрузить раикс,[счётчик]
увеличить раикс
сохранить [счётчик], раикс
уменьшить рсикс
сравнить_с рсикс,0
переход_если_больше loop
; выход с кодом = значение счётчика
загрузить рдиай,[счётчик]
переместить_имм раикс,60
вызов_системы
.бсс
счётчик: .байт 0
```
### Косвенная адресация
```assembly
.текст
.глобал _start
_start:
; загружаем адрес переменной в регистр
переместить_имм рбикс, переменная
; косвенная запись
переместить_имм раикс,99
сохранить [рбикс], раикс
; косвенное чтение
загрузить раикс,[рбикс]
; выход
переместить_имм рдиай, раикс
переместить_имм раикс,60
вызов_системы
.данные
переменная: .байт 0
```
2026-05-02 23:17:09 +03:00
## 🐛 Отладка и проверка
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### Просмотр сгенерированного кода
2026-05-02 23:04:46 +03:00
```bash
2026-05-02 23:17:09 +03:00
# Дизассемблирование
2026-05-02 23:04:46 +03:00
objdump -d программа.elf
2026-05-02 23:17:09 +03:00
# Просмотр секций
objdump -h программа.elf
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
# Просмотр заголовков программы
2026-05-02 23:04:46 +03:00
readelf -l программа.elf
2026-05-02 23:17:09 +03:00
# Извлечение строк из ELF
strings программа.elf
2026-05-02 23:04:46 +03:00
```
2026-05-02 23:17:09 +03:00
### CSV-лог
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
После компиляции создаётся файл `программа.csv` со структурой:
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
| адрес | байт | целевой_адрес | исходная_команда |
|-------|------|---------------|------------------|
| 0x401000 | 48 | | переместить_имм раикс, 1 |
| 0x401001 | B8 | | |
| 0x401002 | 01 | | |
| ... | ... | ... | ... |
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### Трассировка системных вызовов
2026-05-02 23:04:46 +03:00
```bash
strace ./программа.elf
```
2026-05-02 23:17:09 +03:00
### Отладка с GDB
2026-05-02 23:04:46 +03:00
```bash
gdb ./программа.elf
(gdb) break _start ; установить точку останова
(gdb) run ; запустить
(gdb) info registers ; показать регистры
(gdb) x/10i $rip ; показать 10 инструкций
(gdb) stepi ; выполнить одну инструкцию
```
2026-05-02 23:17:09 +03:00
## ⚠️ Ограничения
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
### Поддерживается
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
- ✅ 64-битные, 32-битные, 16-битные и 8-битные регистры (AL, BL, CL, DL)
2026-05-02 23:04:46 +03:00
- ✅ Непосредственная загрузка констант (imm) в регистры
- ✅ Сравнение регистров и регистров с константами
- ✅ Условные и безусловные переходы (длинные и короткие)
- ✅ Арифметические операции (ADD, SUB, INC, DEC)
- ✅ Логическая операция TEST
- ✅ Системные вызовы Linux (syscall)
2026-05-17 17:18:35 +03:00
- ✅ Секции `.текст`, `.данные` и `.бсс`
2026-05-02 23:04:46 +03:00
- ✅ Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`)
- ✅ Директива `.байт` для raw-данных
- ✅ Директива `.константа` для имён констант
2026-05-17 17:18:35 +03:00
-**Абсолютная адресация** по меткам и числовым адресам `[метка]` / `[0x402000]`
-**Косвенная адресация через регистр** `[раикс]`
- ✅ RIP-relative кодирование для всех видов адресации
2026-05-02 23:04:46 +03:00
### Не поддерживается (в разработке)
2026-05-02 23:04:46 +03:00
- ❌ Адресация со смещением `[reg + disp]`
- ❌ Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация
2026-05-17 17:18:35 +03:00
- ❌ Инструкции для работы со стеком (PUSH, POP, CALL, RET)
- ❌ Старшие 8-битные регистры (AH, BH, CH, DH) и расширенные младшие (SPL, BPL, SIL, DIL)
- ❌ Регистры R8R15 и их байтовые/словные версии
2026-05-02 23:17:09 +03:00
- ❌ Поддержка плавающей запятой (FPU/SSE)
- ❌ Многомодульная компиляция
- ❌ Макросы
2026-05-02 23:04:46 +03:00
## 📈 План развития
### Реализовано (v2.0)
2026-05-17 17:18:35 +03:00
- Лексер с токенами `[`, `]`, `NUMBER`
- Парсер с поддержкой операндов в квадратных скобках
- Первый проход с корректным расчётом размера инструкций с памятью
- Второй проход с кодированием абсолютной адресации через RIP-relative
- Поддержка меток в операндах памяти `[переменная]`
- Поддержка числовых адресов `[0x402000]`
- Поддержка косвенной адресации `[регистр]`
- Секция `.бсс` для неинициализированных данных
- Рефакторинг: выделение кодировщика в отдельный модуль
### В планах (v3.0)
2026-05-17 17:18:35 +03:00
- Адресация со смещением `[reg + disp]`
- Сложная адресация с индексом `[reg + reg*scale]`
- Полная SIB-адресация `[reg + reg*scale + disp]`
- Поддержка инструкций PUSH/POP/CALL/RET для стековых операций
- Стандартная библиотека (`libkvs.квс`): malloc, free, strlen, strcmp, print
2026-05-02 23:17:09 +03:00
## 📄 Лицензия
2026-05-02 23:04:46 +03:00
2026-05-02 23:17:09 +03:00
Только для образовательных целей. Используйте на свой страх и риск.
2026-05-02 23:04:46 +03:00
2026-05-17 17:18:35 +03:00
---
*КВС Ассемблер | Экспериментальный проект | 2026*
*Только для образовательных целей. Используйте на свой страх и риск.*
```