```markdown # КВС — Ассемблер с русским синтаксисом для x86-64 **КВС** — это экспериментальный ассемблер для архитектуры x86-64, полностью использующий **русскую лексику** для мнемоник инструкций, имён регистров и директив. Проект носит образовательный характер и демонстрирует принципы работы ассемблера, построения ELF-файлов и трансляции высокоуровневых концепций в машинный код. ## 📋 Оглавление - [Особенности](#-особенности) - [Архитектура](#-архитектура) - [Установка](#-установка) - [Быстрый старт](#-быстрый-старт) - [Синтаксис](#-синтаксис) - [Система команд](#-система-команд) - [Регистры](#-регистры-процессора) - [Директивы](#-директивы-ассемблера) - [Примеры программ](#-примеры-программ) - [Отладка](#-отладка-и-проверка) - [Ограничения](#-ограничения) - [План развития](#-план-развития) - [Лицензия](#-лицензия) ## ✨ Особенности - **Полностью русский синтаксис** — все инструкции, регистры и директивы на кириллице - **Генерация исполняемых ELF-файлов** — 64-битные ELF для Linux, готовые к запуску - **Четырёхпроходная сборка** — традиционная схема с разрешением меток, подстановкой констант и перекодированием - **Поддержка секций** — разделение на `.текст` (код), `.данные` (данные) и `.бнд` (неинициализированные данные) - **Гибкая адресация памяти**: - абсолютная адресация по меткам `[переменная]` - абсолютная адресация по числовому адресу `[0x402000]` - косвенная адресация через регистр `[раикс]` - всё с RIP-relative кодированием для 64-битных перемещений - **CSV-логирование** — детальный файл с соответствием адресов, байтов, меток и исходных команд - **Более 50 инструкций** — полный набор основных команд x86-64 - **Минимализм зависимостей** — только стандартная библиотека Python ## 🏗 Архитектура Процесс трансляции исходного кода `.квс` в исполняемый файл `.elf` проходит через **6 этапов** (4 прохода + сборка): ``` Исходный файл (.квс) │ ▼ (kvs_lexer.py) ← лексический анализ Файл токенов (.токены) │ ▼ (kvs_parser.py) ← синтаксический анализ AST-представление (.аст) │ ▼ (kvs_pass1.py) ← ПРОХОД 1: генерация CSV с заглушками (0xCC) CSV-файл (.csv) ← содержит все байты программы, метки, виртуальные адреса │ ▼ (kvs_pass2.py) ← ПРОХОД 2: разрешение меток │ ▼ (kvs_pass3.py) ← ПРОХОД 3: подстановка констант (.константа) │ ▼ (kvs_pass4.py) ← ПРОХОД 4: перекодирование инструкций │ ▼ (kvs_builder.py) ← сборка ELF из CSV ELF-файл (.elf) │ ▼ Исполняемый файл! ``` ### Модули компилятора | Файл | Назначение | |------|-------------| | `kvs_build.py` | Главный сборочный скрипт (запускает все этапы по порядку) | | `kvs_lexer.py` | Лексический анализ, разбор на токены (поддерживает `[]`, `+`, `-`, `*`) | | `kvs_parser.py` | Синтаксический анализ, построение AST (директивы, метки, инструкции) | | `kvs_data.py` | Общие данные: регистры, таблица инструкций, вспомогательные функции | | `kvs_encoder.py` | Фасад кодировщика (объединяет fixsize и mutsize) | | `kvs_encoder_fixsize.py` | Кодировщик инструкций фиксированного размера | | `kvs_encoder_mutsize.py` | Кодировщик инструкций переменного размера + диспетчер | | `kvs_pass1.py` | Первый проход — генерация CSV с заглушками для неизвестных меток | | `kvs_pass2.py` | Второй проход — разрешение меток (замена заглушек на реальные адреса) | | `kvs_pass3.py` | Третий проход — подстановка констант из директив `.константа` | | `kvs_pass4.py` | Четвёртый проход — перекодирование инструкций с подставленными значениями | | `kvs_builder.py` | Сборка ELF: чтение CSV, заполнение байтов по адресам, установка прав на исполнение | ### Почему 4 прохода? 1. **Первый проход** — неизвестно, какие адреса будут у меток (метка может использоваться до определения). Вместо остановки ставим заглушки `0xCC` и продолжаем. 2. **Второй проход** — после того как все адреса меток известны, заменяем заглушки на реальные значения. 3. **Третий проход** — константы могут ссылаться друг на друга и на метки, поэтому их подстановка делается отдельно. 4. **Четвёртый проход** — после подстановки чисел (адресов/констант) нужно перегенерировать машинный код, так как разные операнды могут менять длину инструкции. ### Структура CSV-файла | Колонка | Назначение | |---------|-------------| | `сегмент` | Имя секции (`.header`, `.text`, `.data`, `.bss`) | | `адрес` | Файловое смещение байта | | `виртуальный_адрес` | Виртуальный адрес байта в памяти (для вычисления смещений) | | `байт` | Исходное значение байта (из pass1) | | `приводящая_метка` | Имена меток, находящихся на этом адресе (через запятую) | | `уводящий_адрес` | Строка `ЗАГЛУШКА имя` для неразрешённых меток | | `исходная_команда` | Исходная инструкция (например, `переход метка`) | | `команда_со_значениями` | Инструкция с подставленными адресами/константами (pass2/pass3) | | `рассчитанный_уводящий_адрес` | Реальный адрес метки (файловое смещение) после разрешения | | `рассчитанный_байт` | Итоговый байт после перекодирования (pass4) | ## 🔧 Установка ```bash # Клонирование репозитория git clone https://github.com/artradeskz/kvs.git cd kvs # Установка прав на выполнение (при необходимости) chmod +x kvs_build.py # Проверка работы python3 kvs_build.py --help ``` ## 🚀 Быстрый старт ### 1. Создайте файл `hello.квс`: ```assembly .текст .глобал _start _start: ; write(1, msg, 13) переместить_имм раикс,1 переместить_имм рдиай,1 переместить_имм рсиай, msg переместить_имм рдикс,13 вызов_системы ; exit(0) переместить_имм раикс,60 переместить_имм рдиай,0 вызов_системы .данные msg: .строка_нуль "Hello, World!" ``` ### 2. Скомпилируйте: ```bash python3 kvs_build.py hello.квс ``` ### 3. Запустите: ```bash ./hello.elf ``` **Вывод:** ``` Hello, World! ``` ## 📝 Синтаксис ### Структура программы ```assembly .текст ; секция кода (обязательна) ; ... инструкции ... .данные ; секция данных (опциональна) ; ... инициализированные данные ... .бнд ; секция неинициализированных данных (опциональна) ; ... переменные без начального значения ... ``` ### Метки Метка — это имя, за которым следует двоеточие: ```assembly _start: ; метка _start переместить_имм раикс,1 переход _start ; переход на метку ``` ### Комментарии Однострочные комментарии начинаются с `;`: ```assembly ; Это комментарий переместить_имм раикс,42 ; комментарий после инструкции ``` ### Работа с памятью ```assembly ; Запись значения в память по метке переместить_имм раикс,42 сохранить [переменная], раикс ; Чтение значения из памяти загрузить рбикс,[переменная] ; Запись по абсолютному адресу сохранить [0x402000], раикс ; Косвенная адресация через регистр переместить_имм рбикс, переменная загрузить раикс, [рбикс] ; Работа с BND-секцией .бнд счётчик: .байт 0 ``` ## 📖 Система команд ### Загрузка констант | Инструкция | Синтаксис | Описание | |------------|-----------|----------| | `переместить_имм` | `переместить_имм рег, значение` | Загрузить константу в регистр | ```assembly переместить_имм раикс,42 ; RAX = 42 переместить_имм еаикс,0x7FFF ; EAX = 32767 переместить_имм ал,0xFF ; AL = 255 ``` ### Работа с памятью | Инструкция | Синтаксис | Описание | |------------|-----------|----------| | `загрузить` | `загрузить рег, [метка/адрес/регистр]` | Загрузить значение из памяти | | `сохранить` | `сохранить [метка/адрес/регистр], рег` | Сохранить значение в память | ```assembly загрузить раикс,[переменная] ; RAX = *переменная сохранить [результат], раикс ; *результат = RAX загрузить рбикс,[0x402000] ; загрузка по абсолютному адресу загрузить раикс,[рбикс] ; косвенная загрузка через регистр ``` ### Арифметика и логика | Инструкция | Синтаксис | Описание | |------------|-----------|----------| | `прибавить` | `прибавить рег1, рег2` | рег1 += рег2 | | `вычесть` | `вычесть рег1, рег2` | рег1 -= рег2 | | `увеличить` | `увеличить рег` | рег++ | | `уменьшить` | `уменьшить рег` | рег-- | | `сравнить` | `сравнить рег1, рег2` | Сравнить регистры | | `сравнить_с` | `сравнить_с рег, значение` | Сравнить с константой | | `проверить` | `проверить рег1, рег2` | TEST (логическое AND) | ```assembly прибавить раикс, рбикс ; 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 | **Короткие условные переходы** (префикс `короткий_`): - `короткий_переход_если_равно`, `короткий_переход_если_неравно`, и т.д. - Генерируют 2-байтовые инструкции (смещение ±127 байт) ### Системные вызовы | Инструкция | Описание | |------------|----------| | `вызов_системы` | Вызов ядра Linux (syscall) | ```assembly ; Пример: завершение программы переместить_имм раикс,60 ; syscall 60 = exit переместить_имм рдиай,0 ; код возврата 0 вызов_системы ``` ## 💾 Регистры процессора КВС поддерживает все основные 64-битные регистры x86-64: | 64-бит | 32-бит | 16-бит | 8-бит (мл.) | |--------|--------|--------|-------------| | `раикс` | `еаикс` | `аикс` | `ал` | | `рбикс` | `ебикс` | `бикс` | `бл` | | `рсикс` | `есикс` | `сикс` | `кл` | | `рдикс` | `едикс` | `дикс` | `дл` | | `рсипи` | `есипи` | `эсп` | `спл` | | `рбипи` | `ебипи` | `бипи` | `бпл` | | `рсиай` | `есиай` | `эс` | `сил` | | `рдиай` | `едиай` | `ди` | `дил` | ## 🔧 Директивы ассемблера | Директива | Назначение | Пример | |-----------|------------|--------| | `.текст` | Начало секции кода | `.текст` | | `.данные` | Начало секции инициализированных данных | `.данные` | | `.бнд` | Начало секции неинициализированных данных | `.бнд` | | `.глобал` | Объявление глобальной метки | `.глобал _start` | | `.строка` | Строка без завершающего нуля | `.строка "Hello"` | | `.строка_нуль` | Строка с завершающим нулём | `.строка_нуль "Hello"` | | `.байт` | Последовательность байтов | `.байт 0x48, 0x65, 108` | | `.константа` | Определение константы | `.константа LEN = 10` | | `.резб` | Резервирование байтов в BND | `.резб 100` | | `.резс` | Резервирование слов в BND | `.резс 50` | | `.рездс` | Резервирование двойных слов в BND | `.рездс 25` | | `.резкс` | Резервирование квадрослов в BND | `.резкс 10` | ## 📝 Примеры программ ### Работа с памятью (переменные) ```assembly .текст .глобал _start _start: ; Запись значения в переменную переместить_имм раикс,42 сохранить [переменная], раикс ; Чтение значения из переменной загрузить рбикс,[переменная] ; Завершение с кодом возврата переместить_имм раикс,60 загрузить рдиай,[переменная] вызов_системы .данные переменная: .байт 0 ``` ### Цикл и условный переход ```assembly .текст .глобал _start _start: переместить_имм рсикс,10 ; счетчик = 10 loop: переместить_имм раикс,1 переместить_имм рдиай,1 переместить_имм рсиай, msg переместить_имм рдикс,10 вызов_системы уменьшить рсикс ; счетчик-- сравнить_с рсикс,0 переход_если_больше loop ; если > 0, продолжаем переместить_имм раикс,60 переместить_имм рдиай,0 вызов_системы .данные msg: .строка "Iteration\n" ``` ### Вычисление суммы чисел от 1 до 10 ```assembly .текст .глобал _start _start: переместить_имм р8,0 ; сумма переместить_имм р9,10 ; счётчик loop: прибавить р8, р9 уменьшить р9 сравнить_с р9,0 переход_если_больше loop ; exit(сумма в r8) переместить_имм раикс,60 переместить рдикс, р8 вызов_системы ``` ### Использование BND-секции ```assembly .текст .глобал _start _start: ; инициализация счётчика в BND переместить_имм раикс,0 сохранить [счётчик], раикс ; цикл: увеличить счётчик 10 раз переместить_имм рсикс,10 loop: загрузить раикс,[счётчик] увеличить раикс сохранить [счётчик], раикс уменьшить рсикс сравнить_с рсикс,0 переход_если_больше loop ; выход с кодом = значение счётчика загрузить рдиай,[счётчик] переместить_имм раикс,60 вызов_системы .бнд счётчик: .байт 0 ``` ### Косвенная адресация ```assembly .текст .глобал _start _start: ; загружаем адрес переменной в регистр переместить_имм рбикс, переменная ; косвенная запись переместить_имм раикс,99 сохранить [рбикс], раикс ; косвенное чтение загрузить раикс,[рбикс] ; выход переместить_имм рдиай, раикс переместить_имм раикс,60 вызов_системы .данные переменная: .байт 0 ``` ## 🐛 Отладка и проверка ### Просмотр сгенерированного кода ```bash # Дизассемблирование objdump -d программа.elf # Просмотр секций objdump -h программа.elf # Просмотр заголовков программы readelf -l программа.elf # Извлечение строк из ELF strings программа.elf ``` ### CSV-лог После компиляции создаётся файл `программа.csv` со структурой: | адрес | виртуальный_адрес | байт | приводящая_метка | исходная_команда | |-------|-------------------|------|------------------|------------------| | 0x1000 | 0x401000 | 48 | _start | переместить_имм раикс, 10 | | 0x1001 | 0x401001 | B8 | | | | ... | ... | ... | ... | ... | ### Трассировка системных вызовов ```bash strace ./программа.elf ``` ### Отладка с GDB ```bash gdb ./программа.elf (gdb) break _start ; установить точку останова (gdb) run ; запустить (gdb) info registers ; показать регистры (gdb) x/10i $rip ; показать 10 инструкций (gdb) stepi ; выполнить одну инструкцию ``` ## ⚠️ Ограничения ### Поддерживается - 64-битные, 32-битные, 16-битные и 8-битные регистры (AL, BL, CL, DL) - Непосредственная загрузка констант (imm) в регистры - Сравнение регистров и регистров с константами - Условные и безусловные переходы (длинные и короткие) - Арифметические операции (ADD, SUB, INC, DEC) - Логическая операция TEST - Системные вызовы Linux (syscall) - Секции `.текст`, `.данные` и `.бнд` - Строковые литералы с escape-последовательностями (`\n`, `\t`, `\\`, `\"`) - Директива `.байт` для raw-данных - Директива `.константа` для имён констант - Директивы резервирования `.резб`, `.резс`, `.рездс`, `.резкс` - Абсолютная адресация по меткам и числовым адресам `[метка]` / `[0x402000]` - Косвенная адресация через регистр `[раикс]` - RIP-relative кодирование для всех видов адресации ### Не поддерживается (в разработке) - Адресация со смещением `[reg + disp]` - Сложная адресация с индексом `[reg + reg*scale]` и полная SIB-адресация - Поддержка плавающей запятой (FPU/SSE) - Многомодульная компиляция - Макросы ## 📈 План развития ### Реализовано (v2.0) - Лексер с токенами `[`, `]`, `NUMBER`, `+`, `-`, `*` - Парсер с поддержкой операндов в квадратных скобках - Четырёхпроходная схема сборки - Первый проход с корректным расчётом размера инструкций с памятью - Второй проход с разрешением меток - Третий проход с подстановкой констант - Четвёртый проход с перекодированием инструкций - Поддержка меток в операндах памяти `[переменная]` - Поддержка числовых адресов `[0x402000]` - Поддержка косвенной адресации `[регистр]` - Секция `.бнд` для неинициализированных данных - Вынесение кодировщика в отдельный модуль - Колонка `виртуальный_адрес` в CSV для точного вычисления смещений ### В планах (v3.0) - Адресация со смещением `[reg + disp]` - Сложная адресация с индексом `[reg + reg*scale]` - Полная SIB-адресация `[reg + reg*scale + disp]` - Стандартная библиотека (`libkvs.квс`): malloc, free, strlen, strcmp, print ## 📄 Лицензия Только для образовательных целей. Используйте на свой страх и риск. --- *КВС Ассемблер | Экспериментальный проект | 2026* *Только для образовательных целей. Используйте на свой страх и риск.* ```