diff --git a/ЯПы/Семинары/Семинар-1.md b/ЯПы/Семинары/Семинар-1.md
new file mode 100644
index 0000000..f5cc929
--- /dev/null
+++ b/ЯПы/Семинары/Семинар-1.md
@@ -0,0 +1,1511 @@
+# Методичка по языку ассемблера x86-64
+## Семинар 1: Окружение, инструменты, первая программа
+
+### Содержание
+
+1. [Необходимые инструменты](#1-необходимые-инструменты)
+2. [Командная оболочка и окружение выполнения](#2-командная-оболочка-и-окружение-выполнения)
+3. [Регулярные выражения и grep](#3-регулярные-выражения-и-grep)
+4. [Переменные окружения](#4-переменные-окружения)
+5. [Модель памяти процесса x86-64](#5-модель-памяти-процесса-x86-64)
+6. [Цикл выполнения инструкций](#6-цикл-выполнения-инструкций)
+7. [Компиляция программ на ассемблере](#7-компиляция-программ-на-ассемблере)
+8. [Разбор программы Hello World](#8-разбор-программы-hello-world)
+9. [Системные вызовы Linux x86-64](#9-системные-вызовы-linux-x86-64)
+10. [Регистры процессора x86-64](#10-регистры-процессора-x86-64)
+11. [Отладчик GDB](#11-отладчик-gdb)
+12. [Типичные ошибки и их решение](#12-типичные-ошибки-и-их-решение)
+13. [Практические задания](#13-практические-задания)
+14. [Справочник: часто используемые конструкции](#справочник-часто-используемые-конструкции)
+15. [Полезные ссылки и документация](#полезные-ссылки-и-документация)
+
+---
+
+## 1. Необходимые инструменты
+
+### Базовый набор инструментов
+
+Для работы с ассемблером x86-64 в Linux потребуются:
+
+- **NASM** (Netwide Assembler) — ассемблер, переводящий мнемоники в машинный код
+- **ld** (GNU linker) — компоновщик, создающий исполняемые файлы из объектных файлов
+- **GDB** (GNU Debugger) — отладчик для пошагового выполнения и анализа программ
+- **make** — утилита для автоматизации сборки
+- **grep**, **sed**, **awk** — инструменты обработки текста
+
+### Установка в Ubuntu/Debian
+
+```bash
+sudo apt update
+sudo apt install nasm gdb build-essential
+```
+
+### Установка в Fedora/RHEL
+
+```bash
+sudo dnf install nasm gdb make gcc
+```
+
+### Проверка установки
+
+```bash
+nasm -version # Должна показать версию NASM
+ld --version # Должна показать версию GNU ld
+gdb --version # Должна показать версию GDB
+```
+
+### Виртуальные машины
+
+Если вы работаете на Windows или macOS, рекомендуется использовать виртуальную машину с Linux:
+
+- **VirtualBox** + Ubuntu 22.04 LTS
+- **VMware Workstation Player** + Fedora
+- **WSL2** (Windows Subsystem for Linux) — для Windows 10/11
+
+**Важно:** Убедитесь, что виртуализация включена в BIOS/UEFI, и что VM имеет доступ к 64-битным инструкциям процессора.
+
+---
+
+## 2. Командная оболочка и окружение выполнения
+
+### Стандартные потоки ввода-вывода
+
+Каждый процесс в Unix/Linux имеет три предопределённых потока:
+
+| Поток | Дескриптор | Назначение | Обычное устройство |
+|-------|------------|------------|-------------------|
+| **stdin** | 0 | Стандартный ввод | Клавиатура |
+| **stdout** | 1 | Стандартный вывод | Терминал |
+| **stderr** | 2 | Стандартный поток ошибок | Терминал |
+
+### Перенаправление потоков
+
+```bash
+# Перенаправление stdout в файл (перезапись)
+./program > output.txt
+
+# Перенаправление stdout в файл (добавление)
+./program >> output.txt
+
+# Перенаправление stderr в файл
+./program 2> errors.txt
+
+# Перенаправление и stdout, и stderr в один файл
+./program > all.txt 2>&1
+
+# Перенаправление stdin из файла
+./program < input.txt
+
+# Конвейер (pipe): stdout первой программы → stdin второй
+./program1 | ./program2
+```
+
+### Пример: разделение потоков
+
+Создайте тестовый скрипт `test.sh`:
+
+```bash
+#!/bin/bash
+echo "Это сообщение в stdout"
+echo "Это сообщение в stderr" >&2
+```
+
+Запустите с перенаправлением:
+
+```bash
+chmod +x test.sh
+./test.sh > stdout.txt 2> stderr.txt
+cat stdout.txt # Покажет только stdout
+cat stderr.txt # Покажет только stderr
+```
+
+### Диаграмма: процесс и стандартные потоки
+
+```mermaid
+graph LR
+ A[Клавиатура] -->|stdin fd=0| B[Процесс]
+ B -->|stdout fd=1| C[Терминал]
+ B -->|stderr fd=2| C
+ B -->|write fd=3| D[Файл data.txt]
+```
+
+---
+
+## 3. Регулярные выражения и grep
+
+### Основные метасимволы
+
+| Символ | Значение | Пример | Что найдёт |
+|--------|----------|--------|------------|
+| `.` | Любой символ | `a.c` | `abc`, `a5c`, `a c` |
+| `*` | 0 или более повторений | `ab*c` | `ac`, `abc`, `abbc` |
+| `+` | 1 или более повторений | `ab+c` | `abc`, `abbc` (но не `ac`) |
+| `?` | 0 или 1 повторение | `ab?c` | `ac`, `abc` |
+| `^` | Начало строки | `^Hello` | строки, начинающиеся с Hello |
+| `$` | Конец строки | `world$` | строки, заканчивающиеся на world |
+| `[]` | Класс символов | `[abc]` | `a`, `b` или `c` |
+| `[^]` | Отрицание класса | `[^0-9]` | любой нецифровой символ |
+| `\|` | Альтернатива (или) | `cat\|dog` | `cat` или `dog` |
+| `()` | Группировка | `(ab)+` | `ab`, `abab`, `ababab` |
+
+### Примеры использования grep
+
+```bash
+# Поиск строк, содержащих слово "error" (регистронезависимо)
+grep -i "error" /var/log/syslog
+
+# Поиск строк, начинающихся с цифры
+grep "^[0-9]" file.txt
+
+# Поиск email-адресов (упрощённый паттерн)
+grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" file.txt
+
+# Показать номера строк
+grep -n "function" program.c
+
+# Инвертированный поиск (строки БЕЗ паттерна)
+grep -v "^#" config.conf
+
+# Рекурсивный поиск в директории
+grep -r "TODO" ./src/
+
+# Подсчёт совпадений
+grep -c "error" logfile.txt
+```
+
+### Расширенные регулярные выражения (ERE)
+
+Для использования расширенного синтаксиса добавьте флаг `-E`:
+
+```bash
+# Найти числа от 100 до 199
+grep -E "1[0-9]{2}" file.txt
+
+# Найти IP-адреса (упрощённый паттерн)
+grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" file.txt
+
+# Найти строки с 3 или более повторяющимися буквами
+grep -E "([a-z])\1{2,}" file.txt
+```
+
+---
+
+## 4. Переменные окружения
+
+### Что такое переменные окружения?
+
+Переменные окружения — это пары ключ-значение, которые хранятся в памяти процесса и наследуются дочерними процессами. Они используются для настройки поведения программ без изменения их кода.
+
+### Просмотр переменных окружения
+
+```bash
+# Показать все переменные окружения
+env
+
+# Показать конкретную переменную
+echo $PATH
+echo $HOME
+echo $USER
+
+# Показать все переменные, включая локальные переменные оболочки
+set
+```
+
+### Важные переменные окружения
+
+| Переменная | Назначение | Пример значения |
+|------------|------------|-----------------|
+| `PATH` | Список директорий для поиска исполняемых файлов | `/usr/local/bin:/usr/bin:/bin` |
+| `HOME` | Домашняя директория пользователя | `/home/username` |
+| `USER` | Имя текущего пользователя | `username` |
+| `SHELL` | Путь к текущей оболочке | `/bin/bash` |
+| `PWD` | Текущая рабочая директория | `/home/username/projects` |
+| `LANG` | Локаль системы | `en_US.UTF-8` |
+| `LD_LIBRARY_PATH` | Пути поиска динамических библиотек | `/usr/local/lib:/usr/lib` |
+
+### Установка и экспорт переменных
+
+```bash
+# Локальная переменная (только в текущей оболочке)
+MY_VAR="hello"
+
+# Экспорт переменной (доступна дочерним процессам)
+export MY_VAR="hello"
+
+# Одной командой
+export MY_VAR="hello"
+
+# Временная переменная только для одной команды
+MY_VAR="hello" ./program
+
+# Добавление в PATH
+export PATH="$PATH:/new/directory"
+```
+
+### Постоянные переменные окружения
+
+Для постоянного сохранения переменных добавьте их в конфигурационные файлы:
+
+- `~/.bashrc` — для bash (интерактивные non-login сессии)
+- `~/.bash_profile` или `~/.profile` — для login сессий
+- `~/.zshrc` — для zsh
+
+Пример в `~/.bashrc`:
+
+```bash
+export MY_PROJECT_PATH="$HOME/projects/asm"
+export PATH="$PATH:$MY_PROJECT_PATH/bin"
+```
+
+После изменения выполните:
+
+```bash
+source ~/.bashrc
+```
+
+---
+
+## 5. Модель памяти процесса x86-64
+
+### Адресное пространство процесса
+
+В x86-64 Linux каждый процесс имеет виртуальное адресное пространство размером до 2^48 байт (256 ТБ в теории, практически меньше). Память организована в несколько сегментов:
+
+```mermaid
+graph TD
+ A[0x00007FFFFFFFFFFF
Высокие адреса] --> B[Stack
Стек растёт вниз]
+ B --> C[...]
+ C --> D[Memory Mapping
mmap, разделяемые библиотеки]
+ D --> E[...]
+ E --> F[Heap
Куча растёт вверх]
+ F --> G[BSS
Неинициализированные данные]
+ G --> H[Data
Инициализированные данные]
+ H --> I[Text
Код программы read-only]
+ I --> J[0x0000000000400000
Низкие адреса]
+```
+
+### Секции исполняемого файла ELF
+
+| Секция | Права доступа | Содержимое | Размер определяется |
+|--------|---------------|------------|---------------------|
+| `.text` | r-x | Машинный код программы | На этапе компиляции |
+| `.rodata` | r-- | Константы, строковые литералы | На этапе компиляции |
+| `.data` | rw- | Инициализированные глобальные переменные | На этапе компиляции |
+| `.bss` | rw- | Неинициализированные глобальные переменные | На этапе компиляции (обнуляется при загрузке) |
+| **stack** | rw- | Локальные переменные, адреса возврата | Динамически (растёт при вызовах функций) |
+| **heap** | rw- | Динамически выделяемая память (malloc) | Динамически (по запросу программы) |
+
+### Визуализация секций программы
+
+```mermaid
+graph LR
+ A[hello.asm исходник] --> B[NASM ассемблер]
+ B --> C[hello.o объектный файл]
+ C --> D[ld компоновщик]
+ D --> E[hello исполняемый ELF]
+
+ subgraph "Секции в памяти при запуске"
+ F[.text: инструкции]
+ G[.data: msg + len]
+ H[.bss: пусто]
+ I[stack: начальный фрейм]
+ end
+```
+
+### Пример: просмотр секций программы
+
+```bash
+# Скомпилируйте hello.asm
+nasm -f elf64 hello.asm
+ld -o hello hello.o
+
+# Просмотр секций
+readelf -S hello
+
+# Просмотр размеров секций
+size hello
+
+# Дизассемблирование секции .text
+objdump -d -M intel hello
+```
+
+Типичный вывод `size`:
+
+```
+ text data bss dec hex filename
+ 181 13 0 194 c2 hello
+```
+
+- `text` (181 байт) — код программы
+- `data` (13 байт) — строка "Hello, World!\n"
+- `bss` (0 байт) — нет неинициализированных данных
+
+### Пример объявления секций в NASM
+
+```asm
+section .data ; Инициализированные данные
+ msg db "Hello", 0xA
+ len equ $ - msg
+ counter dq 42 ; 64-битное число
+
+section .bss ; Неинициализированные данные
+ buffer resb 100 ; Резерв 100 байт
+ number resq 1 ; Резерв 8 байт (qword)
+
+section .text ; Код программы
+ global _start
+
+_start:
+ ; Ваш код здесь
+```
+
+### Важное замечание о Stack и Heap
+
+- **Stack (стек)** используется для:
+ - Локальных переменных функций
+ - Адресов возврата из функций
+ - Сохранённых значений регистров
+ - Аргументов функций (сверх 6 аргументов в System V ABI)
+
+- **Heap (куча)** используется для:
+ - Динамического выделения памяти (`malloc`, `mmap`)
+ - Больших структур данных, размер которых не известен на этапе компиляции
+ - Данных, которые должны жить дольше, чем функция, создавшая их
+
+В ассемблере вы обычно работаете только со стеком (через регистры `rsp`, `rbp`) и статическими секциями (`.data`, `.bss`). Для работы с кучей нужно вызывать системные вызовы `brk`/`mmap` или библиотечные функции C.
+
+---
+
+## 6. Цикл выполнения инструкций
+
+### Fetch-Decode-Execute Cycle
+
+Процессор выполняет программу в бесконечном цикле из трёх фаз:
+
+```mermaid
+graph LR
+ A[FETCH
Загрузить инструкцию
из памяти по адресу RIP] --> B[DECODE
Декодировать
опкод и операнды]
+ B --> C[EXECUTE
Выполнить операцию
обновить регистры/память]
+ C --> D[Увеличить RIP
на длину инструкции]
+ D --> A
+```
+
+### Детальное объяснение фаз
+
+1. **FETCH (Выборка)**
+ - Процессор читает байты инструкции из памяти по адресу, на который указывает регистр `RIP` (Instruction Pointer)
+ - В x86-64 инструкции имеют переменную длину: от 1 до 15 байт
+ - Процессор использует кэш инструкций (I-cache) для ускорения выборки
+
+2. **DECODE (Декодирование)**
+ - Декодер разбирает байты инструкции:
+ - Префиксы (опционально)
+ - Опкод (код операции)
+ - ModR/M байт (режим адресации и операнды)
+ - SIB байт (scale-index-base, для сложной адресации)
+ - Смещение и immediate-значения
+ - Определяет, какие исполнительные блоки нужны (ALU, FPU, Load/Store Unit)
+
+3. **EXECUTE (Выполнение)**
+ - Исполнительные блоки выполняют операцию:
+ - Арифметико-логическое устройство (ALU) для вычислений
+ - Блок загрузки/сохранения для операций с памятью
+ - Блок управления потоком для переходов
+ - Обновляются регистры назначения и флаги
+
+4. **Обновление RIP**
+ - `RIP` увеличивается на длину выполненной инструкции
+ - Для инструкций перехода (jump, call, ret) `RIP` устанавливается в целевой адрес
+ - Для условных переходов `RIP` изменяется только если условие истинно
+
+### Пример: пошаговое выполнение
+
+Рассмотрим последовательность инструкций:
+
+```asm
+mov rax, 5 ; Загрузить 5 в rax
+add rax, 3 ; Добавить 3 к rax
+mov rbx, rax ; Скопировать rax в rbx
+```
+
+Предположим, код загружен по адресу `0x401000`.
+
+| Цикл | RIP | Инструкция | FETCH | DECODE | EXECUTE | Результат |
+|------|-----|------------|-------|--------|---------|-----------|
+| 1 | `0x401000` | `mov rax, 5` | Читает 7 байт | Опкод `48 C7 C0 05 00 00 00` | `rax ← 5` | `rax=5`, `RIP=0x401007` |
+| 2 | `0x401007` | `add rax, 3` | Читает 4 байта | Опкод `48 83 C0 03` | `rax ← rax + 3` | `rax=8`, `RIP=0x40100B` |
+| 3 | `0x40100B` | `mov rbx, rax` | Читает 3 байта | Опкод `48 89 C3` | `rbx ← rax` | `rbx=8`, `RIP=0x40100E` |
+
+### Современные оптимизации
+
+Современные процессоры x86-64 (Intel Core, AMD Ryzen) используют:
+
+- **Конвейеризацию (Pipelining):** несколько инструкций находятся на разных стадиях одновременно
+- **Суперскалярность:** несколько исполнительных блоков выполняют инструкции параллельно
+- **Out-of-Order Execution:** инструкции выполняются не по порядку, если это безопасно
+- **Предсказание переходов (Branch Prediction):** процессор угадывает, будет ли выполнен условный переход
+
+Однако **логическая модель** остаётся последовательной: программа ведёт себя так, как будто инструкции выполняются строго по порядку.
+
+---
+
+## 7. Компиляция программ на ассемблере
+
+### Двухэтапный процесс сборки
+
+```mermaid
+graph LR
+ A[hello.asm
Исходный код] --> B[nasm]
+ B --> C[hello.o
Объектный файл
машинный код + метаданные]
+ C --> D[ld]
+ D --> E[hello
Исполняемый файл ELF]
+```
+
+### Этап 1: Ассемблирование (NASM)
+
+```bash
+nasm -f elf64 hello.asm -o hello.o
+```
+
+**Опции:**
+
+- `-f elf64` — формат вывода: ELF 64-bit (для Linux x86-64)
+- `-o hello.o` — имя выходного объектного файла
+- `-g` — добавить отладочную информацию (для GDB)
+- `-F dwarf` — формат отладочной информации DWARF
+- `-l hello.lst` — создать листинг (listing) файл с адресами и машинными кодами
+
+Пример с отладочной информацией:
+
+```bash
+nasm -f elf64 -g -F dwarf hello.asm -o hello.o
+```
+
+**Что делает NASM:**
+
+1. Разбирает синтаксис исходного кода
+2. Преобразует мнемоники в опкоды (например, `mov rax, 1` → `48 C7 C0 01 00 00 00`)
+3. Разрешает метки (заменяет имена меток на адреса или смещения)
+4. Создаёт объектный файл в формате ELF с секциями `.text`, `.data`, `.bss`
+5. Добавляет таблицу символов и релокации (для последующей компоновки)
+
+### Этап 2: Компоновка (ld)
+
+```bash
+ld -o hello hello.o
+```
+
+**Опции:**
+
+- `-o hello` — имя выходного исполняемого файла
+- `-m elf_x86_64` — целевая платформа (обычно определяется автоматически)
+- `-dynamic-linker /lib64/ld-linux-x86-64.so.2` — для динамической линковки с библиотеками C
+- `-lc` — линковка с libc (если используете функции C)
+- `-e _start` — точка входа (по умолчанию `_start`)
+
+**Что делает ld:**
+
+1. Объединяет секции из всех объектных файлов (если их несколько)
+2. Разрешает внешние символы (например, вызовы функций из других файлов)
+3. Выполняет релокацию (заменяет временные адреса на окончательные)
+4. Создаёт заголовок ELF и таблицы программных заголовков
+5. Записывает исполняемый файл
+
+### Проверка результата
+
+```bash
+# Проверить тип файла
+file hello
+# Вывод: hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV)...
+
+# Сделать исполняемым (если ld не установил права)
+chmod +x hello
+
+# Запустить
+./hello
+```
+
+### Makefile для автоматизации
+
+Создайте файл `Makefile`:
+
+```makefile
+# Переменные
+ASM = nasm
+LD = ld
+ASMFLAGS = -f elf64 -g -F dwarf
+LDFLAGS =
+
+# Цель по умолчанию
+all: hello
+
+# Правило для объектных файлов
+%.o: %.asm
+ $(ASM) $(ASMFLAGS) $< -o $@
+
+# Правило для исполняемого файла
+hello: hello.o
+ $(LD) $(LDFLAGS) $< -o $@
+
+# Очистка
+clean:
+ rm -f *.o hello
+
+# Запуск
+run: hello
+ ./hello
+
+# Отладка
+debug: hello
+ gdb ./hello
+
+.PHONY: all clean run debug
+```
+
+Использование:
+
+```bash
+make # Собрать программу
+make run # Собрать и запустить
+make debug # Собрать и запустить в GDB
+make clean # Удалить сгенерированные файлы
+```
+
+---
+
+## 8. Разбор программы Hello World
+
+### Полный исходный код
+
+```asm
+section .data
+ msg db "Hello, World!", 0xA ; Строка с переводом строки (0xA = '\n')
+ len equ $ - msg ; Вычислить длину строки
+
+section .text
+ global _start
+
+_start:
+ ; Системный вызов write(1, msg, len)
+ mov rax, 1 ; syscall номер: sys_write
+ mov rdi, 1 ; arg1: file descriptor (stdout)
+ mov rsi, msg ; arg2: указатель на буфер
+ mov rdx, len ; arg3: количество байт
+ syscall ; Выполнить системный вызов
+
+ ; Системный вызов exit(0)
+ mov rax, 60 ; syscall номер: sys_exit
+ xor rdi, rdi ; arg1: exit code = 0
+ syscall ; Выполнить системный вызов
+```
+
+### Пошаговый разбор секции .data
+
+```asm
+section .data
+```
+
+- Директива секции: все последующие объявления будут помещены в секцию `.data` (инициализированные данные)
+
+```asm
+ msg db "Hello, World!", 0xA
+```
+
+- `msg` — метка (адрес начала строки)
+- `db` (define byte) — директива объявления байтов
+- `"Hello, World!"` — ASCII-строка (13 байт)
+- `0xA` — шестнадцатеричное значение 10 (код символа `\n` — перевод строки)
+- **Итого:** 14 байт данных
+
+```asm
+ len equ $ - msg
+```
+
+- `len` — константа времени ассемблирования (не выделяет память)
+- `equ` — директива определения константы
+- `$` — текущий адрес (адрес сразу после строки)
+- `$ - msg` — разность адресов = длина строки
+- **Результат:** `len = 14`
+
+### Пошаговый разбор секции .text
+
+```asm
+section .text
+```
+
+- Секция кода (исполняемая, read-only)
+
+```asm
+ global _start
+```
+
+- Объявляет символ `_start` глобальным (видимым компоновщику)
+- Компоновщик `ld` использует `_start` как точку входа по умолчанию
+
+```asm
+_start:
+```
+
+- Метка: адрес первой инструкции программы
+
+### Системный вызов write
+
+```asm
+ mov rax, 1 ; syscall номер: sys_write
+ mov rdi, 1 ; arg1: file descriptor (stdout)
+ mov rsi, msg ; arg2: указатель на буфер
+ mov rdx, len ; arg3: количество байт
+ syscall ; Выполнить системный вызов
+```
+
+**Регистры для системных вызовов (System V ABI):**
+
+| Регистр | Назначение | В данном примере |
+|---------|------------|------------------|
+| `rax` | Номер системного вызова | `1` (sys_write) |
+| `rdi` | 1-й аргумент | `1` (stdout) |
+| `rsi` | 2-й аргумент | Адрес строки `msg` |
+| `rdx` | 3-й аргумент | Длина строки `14` |
+| `r10` | 4-й аргумент | — |
+| `r8` | 5-й аргумент | — |
+| `r9` | 6-й аргумент | — |
+
+**После `syscall`:**
+
+- Управление передаётся ядру Linux
+- Ядро выполняет `sys_write(1, msg, 14)`
+- Строка "Hello, World!\n" выводится в терминал
+- Управление возвращается в программу
+- `rax` содержит результат (количество записанных байт или код ошибки)
+
+### Системный вызов exit
+
+```asm
+ mov rax, 60 ; syscall номер: sys_exit
+ xor rdi, rdi ; arg1: exit code = 0
+ syscall ; Выполнить системный вызов
+```
+
+- `sys_exit(0)` завершает процесс с кодом возврата 0 (успех)
+- `xor rdi, rdi` — идиоматический способ обнулить регистр (быстрее и короче, чем `mov rdi, 0`)
+
+**Проверка кода возврата в bash:**
+
+```bash
+./hello
+echo $? # Выведет 0
+```
+
+### Таблица инструкций с машинными кодами
+
+| Адрес (пример) | Машинный код | Инструкция | Описание |
+|----------------|--------------|------------|----------|
+| `0x401000` | `48 C7 C0 01 00 00 00` | `mov rax, 1` | Загрузить 1 в rax |
+| `0x401007` | `48 C7 C7 01 00 00 00` | `mov rdi, 1` | Загрузить 1 в rdi |
+| `0x40100E` | `48 BE xx xx xx xx xx xx xx xx` | `mov rsi, msg` | Загрузить адрес msg в rsi |
+| `0x401018` | `48 C7 C2 0E 00 00 00` | `mov rdx, 14` | Загрузить 14 в rdx |
+| `0x40101F` | `0F 05` | `syscall` | Системный вызов |
+| `0x401021` | `48 C7 C0 3C 00 00 00` | `mov rax, 60` | Загрузить 60 в rax |
+| `0x401028` | `48 31 FF` | `xor rdi, rdi` | Обнулить rdi |
+| `0x40102B` | `0F 05` | `syscall` | Системный вызов |
+
+**Обратите внимание:**
+
+- Инструкция `syscall` кодируется всего двумя байтами: `0F 05`
+- `mov` с 64-битным immediate занимает 10 байт (префикс REX.W `48` + опкод + 8 байт данных)
+- `xor rdi, rdi` занимает всего 3 байта
+
+### Эксперимент: изменение строки
+
+Попробуйте изменить программу:
+
+```asm
+section .data
+ msg db "Hello, Assembly!", 0xA, 0 ; Добавлен null-terminator (для совместимости с C)
+ len equ $ - msg - 1 ; Не считаем null-terminator
+
+section .text
+ global _start
+
+_start:
+ mov rax, 1
+ mov rdi, 1
+ mov rsi, msg
+ mov rdx, len
+ syscall
+
+ mov rax, 60
+ xor rdi, rdi
+ syscall
+```
+
+Соберите и запустите:
+
+```bash
+nasm -f elf64 hello.asm && ld -o hello hello.o && ./hello
+```
+
+---
+
+## 9. Системные вызовы Linux x86-64
+
+### Что такое системный вызов?
+
+**Системный вызов (syscall)** — это интерфейс между программой в пользовательском пространстве (user space) и ядром операционной системы (kernel space). Это единственный легальный способ для программы:
+
+- Взаимодействовать с устройствами ввода-вывода
+- Работать с файловой системой
+- Управлять процессами и потоками
+- Выделять память
+- Взаимодействовать с сетью
+
+### Переход в режим ядра
+
+```mermaid
+sequenceDiagram
+ participant U as User Space
(программа)
+ participant C as CPU
+ participant K as Kernel Space
(ядро Linux)
+
+ U->>C: syscall (0x0F 0x05)
+ Note over C: Сохранить контекст
+ C->>K: Передать управление
Ring 3 → Ring 0
+ Note over K: Проверить права
Выполнить операцию
+ K->>C: Вернуть результат в RAX
+ Note over C: Восстановить контекст
+ C->>U: Продолжить выполнение
+
+**Кольца защиты (Protection Rings):**
+- **Ring 0** (kernel mode): полный доступ ко всему оборудованию, выполнение привилегированных инструкций
+- **Ring 3** (user mode): ограниченный доступ, не может напрямую взаимодействовать с устройствами
+
+### Механизм вызова
+
+**Инструкция `syscall` (x86-64):**
+```nasm
+syscall ; 0x0F 0x05 в машинном коде
+```
+
+**Передача параметров:**
+
+| Параметр | Регистр | Описание |
+|----------|---------|----------|
+| Номер syscall | rax | Идентификатор системного вызова (0-331+) |
+| Аргумент 1 | rdi | Первый параметр |
+| Аргумент 2 | rsi | Второй параметр |
+| Аргумент 3 | rdx | Третий параметр |
+| Аргумент 4 | r10 | Четвёртый параметр (не rcx!) |
+| Аргумент 5 | r8 | Пятый параметр |
+| Аргумент 6 | r9 | Шестой параметр |
+| **Возвращаемое значение** | **rax** | **Результат выполнения** |
+
+**Важно:** в отличие от calling convention функций, системные вызовы используют **r10** вместо rcx для 4-го аргумента (rcx используется ядром для сохранения return address).
+
+### Основные системные вызовы
+
+#### 1. read (номер 0)
+Чтение данных из файлового дескриптора.
+
+```nasm
+mov rax, 0 ; syscall номер: read
+mov rdi, 0 ; fd = 0 (stdin)
+mov rsi, buffer ; указатель на буфер
+mov rdx, 128 ; максимальное количество байт для чтения
+syscall
+; rax содержит количество прочитанных байт (или -errno при ошибке)
+```
+
+#### 2. write (номер 1)
+Запись данных в файловый дескриптор.
+
+```nasm
+mov rax, 1 ; syscall номер: write
+mov rdi, 1 ; fd = 1 (stdout)
+mov rsi, message ; указатель на данные
+mov rdx, 13 ; количество байт для записи
+syscall
+; rax содержит количество записанных байт
+```
+
+#### 3. exit (номер 60)
+Завершение процесса.
+
+```nasm
+mov rax, 60 ; syscall номер: exit
+mov rdi, 0 ; код возврата (0 = успех)
+syscall
+; не возвращается
+```
+
+### Стандартные файловые дескрипторы
+
+| Номер | Имя | Назначение |
+|-------|-----|------------|
+| 0 | stdin | Стандартный ввод |
+| 1 | stdout | Стандартный вывод |
+| 2 | stderr | Стандартный вывод ошибок |
+
+### Коды ошибок
+
+При ошибке системный вызов возвращает отрицательное значение `-errno`:
+
+| Код | Макрос | Описание |
+|-----|--------|----------|
+| -1 | EPERM | Операция не разрешена |
+| -2 | ENOENT | Файл или каталог не существует |
+| -9 | EBADF | Неверный файловый дескриптор |
+| -14 | EFAULT | Неверный адрес памяти |
+| -22 | EINVAL | Неверный аргумент |
+
+**Проверка ошибок:**
+```nasm
+syscall
+test rax, rax ; Проверить знак результата
+js .error ; Если отрицательное → ошибка
+; Успех
+jmp .continue
+
+.error:
+neg rax ; Преобразовать -errno в errno
+; rax содержит код ошибки (положительный)
+```
+
+### Полная таблица системных вызовов
+
+Смотрите `docs/system-calls.md` или https://filippo.io/linux-syscall-table/
+
+**Популярные системные вызовы:**
+- 0: read
+- 1: write
+- 2: open
+- 3: close
+- 9: mmap (выделение памяти)
+- 12: brk (изменение размера кучи)
+- 39: getpid
+- 57: fork
+- 59: execve
+- 60: exit
+
+---
+
+## 10. Регистры общего назначения
+
+### Иерархия регистров
+
+В x86-64 регистры имеют многоуровневую структуру для обратной совместимости:
+
+```mermaid
+graph TD
+ A[RAX 64 бит
0x0123456789ABCDEF] --> B[EAX 32 бит младших
0x89ABCDEF]
+ B --> C[AX 16 бит младших
0xCDEF]
+ C --> D[AH 8 бит старших
0xCD]
+ C --> E[AL 8 бит младших
0xEF]
+
+ style A fill:#4ecdc4
+ style B fill:#95e1d3
+ style C fill:#f38181
+ style D fill:#ffd93d
+ style E fill:#6bcf7f
+```
+
+### Полный список регистров общего назначения
+
+| 64-бит | 32-бит | 16-бит | 8-бит (low) | 8-бит (high) | Назначение в calling convention |
+|--------|--------|--------|-------------|--------------|--------------------------------|
+| rax | eax | ax | al | ah | Возвращаемое значение, accumulator |
+| rbx | ebx | bx | bl | bh | Callee-saved, base |
+| rcx | ecx | cx | cl | ch | 4-й аргумент, counter |
+| rdx | edx | dx | dl | dh | 3-й аргумент, data |
+| rsi | esi | si | sil | — | 2-й аргумент, source index |
+| rdi | edi | di | dil | — | 1-й аргумент, destination index |
+| rsp | esp | sp | spl | — | Stack pointer (указатель стека) |
+| rbp | ebp | bp | bpl | — | Base pointer (база фрейма) |
+| r8 | r8d | r8w | r8b | — | 5-й аргумент |
+| r9 | r9d | r9w | r9b | — | 6-й аргумент |
+| r10 | r10d | r10w | r10b | — | Caller-saved |
+| r11 | r11d | r11w | r11b | — | Caller-saved |
+| r12 | r12d | r12w | r12b | — | Callee-saved |
+| r13 | r13d | r13w | r13b | — | Callee-saved |
+| r14 | r14d | r14w | r14b | — | Callee-saved |
+| r15 | r15d | r15w | r15b | — | Callee-saved |
+
+**Важно:** запись в младшую 32-битную часть (eax, ebx, ...) **обнуляет** старшие 32 бита регистра!
+
+```nasm
+mov rax, 0x123456789ABCDEF ; rax = 0x123456789ABCDEF
+mov eax, 0x11111111 ; rax = 0x0000000011111111 (старшие биты обнулены!)
+```
+
+### Специальные регистры
+
+**rip** (Instruction Pointer):
+- Указывает на адрес следующей инструкции
+- Изменяется автоматически при выполнении
+- Можно изменить через `jmp`, `call`, `ret`
+- Используется для RIP-relative адресации
+
+**rflags** (Флаги процессора):
+- 64-битный регистр состояния
+- Содержит флаги результата операций (ZF, SF, CF, OF, ...)
+- Изменяется автоматически арифметическими и логическими операциями
+
+### Примеры использования разных размеров
+
+```nasm
+; Работа с байтами
+mov al, 0xFF ; al = 255, остальная часть rax не изменяется
+mov ah, 0x12 ; ah = 18, al и остальное не изменяется
+
+; Работа с 16-битными значениями
+mov ax, 0x1234 ; ax = 0x1234, старшие 48 бит rax не изменяются
+
+; 32-битная запись обнуляет старшие биты
+mov rax, -1 ; rax = 0xFFFFFFFFFFFFFFFF (все биты = 1)
+mov eax, 0 ; rax = 0x0000000000000000 (старшие 32 бита обнулились!)
+
+; 8 и 16-битная запись НЕ обнуляет старшие биты
+mov rax, -1 ; rax = 0xFFFFFFFFFFFFFFFF
+mov al, 0 ; rax = 0xFFFFFFFFFFFFFF00 (только младший байт обнулён)
+```
+
+---
+
+## 11. Арифметические и логические операции
+
+### Арифметические инструкции
+
+| Инструкция | Описание | Пример | Эффект |
+|------------|----------|--------|--------|
+| `add dst, src` | Сложение | `add rax, rbx` | rax = rax + rbx |
+| `sub dst, src` | Вычитание | `sub rax, 10` | rax = rax - 10 |
+| `inc dst` | Инкремент (+1) | `inc rcx` | rcx = rcx + 1 |
+| `dec dst` | Декремент (-1) | `dec rdx` | rdx = rdx - 1 |
+| `imul dst, src` | Знаковое умножение | `imul rax, rbx` | rax = rax * rbx |
+| `mul src` | Беззнаковое умножение | `mul rbx` | rdx:rax = rax * rbx |
+| `idiv src` | Знаковое деление | `idiv rbx` | rax = rdx:rax / rbx, rdx = остаток |
+| `div src` | Беззнаковое деление | `div rcx` | rax = rdx:rax / rcx, rdx = остаток |
+| `neg dst` | Смена знака | `neg rax` | rax = -rax |
+
+**Примечание о умножении/делении:**
+- `mul`/`div` работают с парой rdx:rax (128-битный результат/делимое)
+- `imul` с двумя операндами работает как обычная операция
+- Перед делением нужно расширить делимое: `cqo` (sign-extend rax → rdx:rax)
+
+```nasm
+; Умножение: результат может не поместиться в 64 бита
+mov rax, 0x8000000000000000
+mov rbx, 2
+mul rbx ; rdx:rax = rax * rbx
+ ; rax = 0, rdx = 1 (результат = 2^64)
+
+; Деление: делимое в rdx:rax
+mov rax, 100 ; Делимое = 100
+cqo ; Расширить rax в rdx:rax (rdx = 0 или -1)
+mov rbx, 7
+idiv rbx ; rax = 100 / 7 = 14, rdx = 100 % 7 = 2
+```
+
+### Логические операции
+
+| Инструкция | Описание | Пример | Эффект |
+|------------|----------|--------|--------|
+| `and dst, src` | Логическое И | `and rax, 0xFF` | rax = rax & 0xFF |
+| `or dst, src` | Логическое ИЛИ | `or rax, rbx` | rax = rax \| rbx |
+| `xor dst, src` | Исключающее ИЛИ | `xor rax, rax` | rax = 0 (обнуление) |
+| `not dst` | Логическое НЕ | `not rax` | rax = ~rax (инверсия) |
+| `test op1, op2` | Тест (AND без записи) | `test rax, rax` | Установить флаги по rax & rax |
+| `cmp op1, op2` | Сравнение (SUB без записи) | `cmp rax, 10` | Установить флаги по rax - 10 |
+
+**Популярные идиомы:**
+
+```nasm
+; Обнуление регистра (быстрее mov rax, 0)
+xor rax, rax ; rax = 0, обнуляет CF и OF
+
+; Проверка на ноль
+test rax, rax ; ZF = 1 если rax == 0
+jz .is_zero
+
+; Извлечение младшего байта
+and rax, 0xFF ; Обнулить все биты кроме младших 8
+
+; Установка бита
+or rax, 0x80 ; Установить бит 7
+
+; Сброс бита
+and rax, ~0x80 ; Сбросить бит 7
+
+; Инверсия бита
+xor rax, 0x80 ; Инвертировать бит 7
+```
+
+### Сдвиги и вращения
+
+| Инструкция | Описание | Пример | Эффект |
+|------------|----------|--------|--------|
+| `shl dst, count` | Логический сдвиг влево | `shl rax, 3` | rax = rax << 3 (× 8) |
+| `shr dst, count` | Логический сдвиг вправо | `shr rax, 2` | rax = rax >> 2 (÷ 4, беззнаковое) |
+| `sal dst, count` | Арифметический сдвиг влево | `sal rax, 1` | То же что shl |
+| `sar dst, count` | Арифметический сдвиг вправо | `sar rax, 1` | rax = rax >> 1 (÷ 2, знаковое) |
+| `rol dst, count` | Циклический сдвиг влево | `rol rax, 8` | Биты циклически влево |
+| `ror dst, count` | Циклический сдвиг вправо | `ror rax, 4` | Биты циклически вправо |
+
+**Различие между shr и sar:**
+
+```nasm
+; Беззнаковый сдвиг (shr)
+mov rax, -8 ; rax = 0xFFFFFFFFFFFFFFF8
+shr rax, 1 ; rax = 0x7FFFFFFFFFFFFFFC (= 9223372036854775804)
+
+; Знаковый сдвиг (sar) сохраняет знаковый бит
+mov rax, -8 ; rax = 0xFFFFFFFFFFFFFFF8 (= -8)
+sar rax, 1 ; rax = 0xFFFFFFFFFFFFFFFC (= -4, знак сохранён!)
+```
+
+**Быстрое умножение/деление на степени двойки:**
+
+```nasm
+; Умножить на 8 (2^3)
+shl rax, 3 ; Быстрее чем imul rax, 8
+
+; Разделить на 4 (2^2)
+shr rax, 2 ; Для беззнакового
+sar rax, 2 ; Для знакового (сохранит знак)
+```
+
+---
+
+## 12. Флаги процессора и условные переходы
+
+### Регистр RFLAGS
+
+Основные флаги (биты регистра rflags):
+
+| Флаг | Бит | Имя | Устанавливается когда |
+|------|-----|-----|----------------------|
+| CF | 0 | Carry Flag | Перенос при сложении / заём при вычитании (unsigned) |
+| PF | 2 | Parity Flag | Чётное количество единиц в младшем байте |
+| ZF | 6 | Zero Flag | Результат равен нулю |
+| SF | 7 | Sign Flag | Результат отрицательный (старший бит = 1) |
+| OF | 11 | Overflow Flag | Переполнение при знаковых операциях |
+
+### Как устанавливаются флаги
+
+```nasm
+; Арифметические операции устанавливают флаги
+mov rax, 5
+sub rax, 5 ; ZF = 1 (результат 0)
+
+mov rax, 10
+sub rax, 20 ; SF = 1 (результат отрицательный)
+ ; CF = 1 (заём при unsigned вычитании)
+
+; test и cmp устанавливают флаги без изменения операндов
+test rax, rax ; Выполнить rax & rax, установить флаги
+ ; ZF = 1 если rax == 0
+
+cmp rax, 10 ; Выполнить rax - 10, установить флаги
+ ; ZF = 1 если rax == 10
+ ; CF = 1 если rax < 10 (unsigned)
+```
+
+### Условные переходы
+
+**После сравнения двух значений (`cmp a, b`):**
+
+| Условие | Unsigned | Signed | Проверяемые флаги |
+|---------|----------|--------|-------------------|
+| a == b | `je` (jump if equal) | `je` | ZF = 1 |
+| a != b | `jne` (not equal) | `jne` | ZF = 0 |
+| a < b | `jb` (below) | `jl` (less) | CF = 1 / SF ≠ OF |
+| a <= b | `jbe` (below/equal) | `jle` (less/equal) | CF = 1 or ZF = 1 / ZF = 1 or SF ≠ OF |
+| a > b | `ja` (above) | `jg` (greater) | CF = 0 and ZF = 0 / ZF = 0 and SF = OF |
+| a >= b | `jae` (above/equal) | `jge` (greater/equal) | CF = 0 / SF = OF |
+
+**Проверка отдельных флагов:**
+
+| Инструкция | Условие | Флаги |
+|------------|---------|-------|
+| `jz` / `je` | Ноль / равно | ZF = 1 |
+| `jnz` / `jne` | Не ноль / не равно | ZF = 0 |
+| `js` | Знак отрицательный | SF = 1 |
+| `jns` | Знак положительный | SF = 0 |
+| `jc` | Перенос | CF = 1 |
+| `jnc` | Нет переноса | CF = 0 |
+| `jo` | Переполнение | OF = 1 |
+| `jno` | Нет переполнения | OF = 0 |
+
+### Примеры использования
+
+**Проверка диапазона (0-9 для ASCII цифр):**
+
+```nasm
+; Проверить, что символ в al — цифра '0'-'9'
+cmp al, '0'
+jb .not_digit ; Если al < '0' → не цифра (unsigned)
+cmp al, '9'
+ja .not_digit ; Если al > '9' → не цифра (unsigned)
+; Здесь: это цифра
+jmp .is_digit
+
+.not_digit:
+; Обработка не-цифры
+
+.is_digit:
+; Обработка цифры
+```
+
+**Знаковое сравнение:**
+
+```nasm
+; Проверить, что rax находится в диапазоне [-100, 100]
+cmp rax, -100
+jl .out_of_range ; Если rax < -100 (signed)
+cmp rax, 100
+jg .out_of_range ; Если rax > 100 (signed)
+; Здесь: -100 <= rax <= 100
+```
+
+**Цикл с счётчиком:**
+
+```nasm
+ mov rcx, 10 ; Счётчик = 10
+.loop:
+ ; Тело цикла
+
+ dec rcx ; Уменьшить счётчик
+ jnz .loop ; Повторить, пока rcx != 0
+
+; Выход из цикла
+```
+
+---
+
+## 13. Типичные ошибки начинающих
+
+### Ошибка 1: Забыли указать размер секции .bss
+
+**Неправильно:**
+```nasm
+section .bss
+ buffer: ; ❌ Не указан размер!
+```
+
+**Правильно:**
+```nasm
+section .bss
+ buffer: resb 128 ; ✓ Резервировать 128 байт
+ counter: resq 1 ; ✓ Резервировать 1 qword (8 байт)
+```
+
+### Ошибка 2: Использовали неправильный размер операндов
+
+**Неправильно:**
+```nasm
+mov rax, [buffer] ; Загружает 8 байт
+mov byte [buffer], rax ; ❌ Пытается записать 8 байт в 1 байт!
+```
+
+**Правильно:**
+```nasm
+mov al, [buffer] ; ✓ Загрузить 1 байт
+mov byte [buffer], al ; ✓ Записать 1 байт
+
+; Или явно указать размер
+movzx rax, byte [buffer] ; Загрузить байт с zero-extend
+```
+
+### Ошибка 3: Забыли про обнуление старших битов при 32-битной записи
+
+**Проблема:**
+```nasm
+mov rax, 0xFFFFFFFFFFFFFFFF
+mov eax, 0 ; ❌ rax станет 0, а не 0xFFFFFFFF00000000!
+```
+
+**Объяснение:** запись в 32-битную часть (eax) обнуляет старшие 32 бита автоматически.
+
+### Ошибка 4: Неправильно вычислили длину строки
+
+**Неправильно:**
+```nasm
+section .data
+ msg: db "Hello", 10 ; Строка из 6 байт (5 букв + '\n')
+
+section .text
+ mov rdx, 5 ; ❌ Длина = 5, но должна быть 6!
+ ; В результате '\n' не выведется
+```
+
+**Правильно:**
+```nasm
+section .data
+ msg: db "Hello", 10
+ msg_len equ $ - msg ; ✓ Автоматически вычислить длину
+
+section .text
+ mov rdx, msg_len ; ✓ Правильная длина = 6
+```
+
+### Ошибка 5: Перепутали signed и unsigned переходы
+
+**Неправильно:**
+```nasm
+; Проверка ASCII кода (беззнаковое значение)
+cmp al, '0'
+jl .not_digit ; ❌ jl для signed сравнения!
+```
+
+**Правильно:**
+```nasm
+; ASCII коды — беззнаковые
+cmp al, '0'
+jb .not_digit ; ✓ jb (below) для unsigned
+```
+
+**Напоминание:**
+- `jl/jg/jle/jge` — для **signed** (знаковых) сравнений
+- `jb/ja/jbe/jae` — для **unsigned** (беззнаковых) сравнений
+
+### Ошибка 6: Не обработали ошибку системного вызова
+
+**Неправильно:**
+```nasm
+mov rax, 0
+mov rdi, 0
+mov rsi, buffer
+mov rdx, 100
+syscall
+; ❌ Не проверили, что rax >= 0 (может быть -errno)
+mov [bytes_read], rax
+```
+
+**Правильно:**
+```nasm
+syscall
+test rax, rax
+js .read_error ; ✓ Если отрицательное → ошибка
+
+; Успех
+mov [bytes_read], rax
+jmp .continue
+
+.read_error:
+neg rax ; Преобразовать -errno в errno
+; Обработать ошибку (rax содержит код ошибки)
+```
+
+### Ошибка 7: Забыли про завершающий ноль в строках C
+
+**Проблема:** при передаче строк в функции C (printf, strlen) они ожидают завершающий `\0`.
+
+**Неправильно:**
+```nasm
+section .data
+ msg: db "Hello" ; ❌ Нет '\0'!
+
+extern strlen
+ mov rdi, msg
+ call strlen ; strlen может читать за границу
+```
+
+**Правильно:**
+```nasm
+section .data
+ msg: db "Hello", 0 ; ✓ Завершающий ноль
+```
+
+---
+
+## 14. Отладка с GDB
+
+### Основные команды
+
+**Запуск программы:**
+```bash
+gdb ./program
+(gdb) break _start # Точка останова на метке
+(gdb) run # Запустить программу
+```
+
+**Пошаговое выполнение:**
+```bash
+(gdb) stepi # Выполнить одну инструкцию (step into)
+(gdb) nexti # Следующая инструкция (step over)
+(gdb) continue # Продолжить до следующего breakpoint
+(gdb) finish # Выполнить до выхода из функции
+```
+
+**Просмотр состояния:**
+```bash
+(gdb) info registers # Показать все регистры
+(gdb) info registers rax rbx rsp # Конкретные регистры
+(gdb) print $rax # Вывести значение rax
+(gdb) print/x $rax # В hex формате
+(gdb) print/t $rax # В бинарном формате
+(gdb) print/d $rax # В десятичном (со знаком)
+```
+
+**Просмотр памяти:**
+```bash
+(gdb) x/10gx $rsp # 10 qword в hex от адреса rsp
+(gdb) x/20bx $rsi # 20 байт в hex от адреса rsi
+(gdb) x/s $rdi # Строку (до '\0') по адресу rdi
+(gdb) x/10i $rip # 10 инструкций от текущего rip
+```
+
+**Формат команды `x` (examine):**
+- Формат: `x/[count][format][size] address`
+- count: количество единиц
+- format: o(octal), x(hex), d(decimal), u(unsigned), t(binary), i(instruction), c(char), s(string)
+- size: b(byte), h(halfword-2), w(word-4), g(giant-8)
+
+**Точки останова:**
+```bash
+(gdb) break _start # На метке
+(gdb) break *0x401000 # На конкретном адресе
+(gdb) info breakpoints # Список всех breakpoints
+(gdb) delete 1 # Удалить breakpoint №1
+(gdb) disable 2 # Временно отключить breakpoint №2
+```
+
+**Дизассемблирование:**
+```bash
+(gdb) disassemble # Дизассемблировать текущую функцию
+(gdb) disassemble _start # Конкретную метку
+(gdb) set disassembly-flavor intel # Использовать Intel синтаксис
+```
+
+### Пример сессии отладки
+
+```bash
+$ gdb ./hello
+(gdb) break _start
+Breakpoint 1 at 0x401000
+
+(gdb) run
+Starting program: /path/to/hello
+
+Breakpoint 1, 0x0000000000401000 in _start ()
+
+(gdb) disassemble
+ 0x0000000000401000 <_start+0>: mov eax,0x1
+ 0x0000000000401005 <_start+5>: mov edi,0x1
+=> 0x000000000040100a <_start+10>: movabs rsi,0x402000
+ ...
+
+(gdb) info registers rax rdi rsi rdx
+rax 0x0 0
+rdi 0x0 0
+rsi 0x0 0
+rdx 0x0 0
+
+(gdb) stepi
+(gdb) info registers rax
+rax 0x1 1
+
+(gdb) stepi
+(gdb) info registers rdi
+rdi 0x1 1
+
+(gdb) stepi
+(gdb) info registers rsi
+rsi 0x402000 4202496
+
+(gdb) x/s $rsi
+0x402000: "hello, world!\n"
+
+(gdb) continue
+hello, world!
+[Inferior 1 (process 12345) exited normally]
+```
+
+### Полезные настройки
+
+**Файл `~/.gdbinit`:**
+```gdb
+set disassembly-flavor intel
+set pagination off
+set print pretty on
+
+# Показывать регистры после каждого шага
+define hook-stop
+ info registers rip rax rbx rcx rdx rsi rdi rsp rbp
+end
+```
+
+---
+
+## 15. Справочник
+
+### Директивы NASM
+
+| Директива | Описание | Пример |
+|-----------|----------|--------|
+| `section .data` | Секция инициализированных данных | |
+| `section .bss` | Секция неинициализированных данных | |
+| `section .text` | Секция кода | |
+| `global symbol` | Экспортировать символ | `global _start` |
+| `extern symbol` | Импортировать внешний символ | `extern printf` |
+| `equ` | Определить константу | `BUF_SIZE equ 128` |
+| `%define` | Макроопределение | `%define STDOUT 1` |
+
+### Объявление данных
+
+| Директива | Размер | Описание | Пример |
+|-----------|--------|----------|--------|
+| `db` | 1 байт | Define byte | `db 0x41, 'A', 10` |
+| `dw` | 2 байта | Define word | `dw 0x1234` |
+| `dd` | 4 байта | Define double word | `dd 0x12345678` |
+| `dq` | 8 байт | Define quad word | `dq 0x123456789ABCDEF` |
+| `resb` | 1 байт | Reserve bytes | `resb 100` |
+| `resw` | 2 байта | Reserve words | `resw 50` |
+| `resd` | 4 байта | Reserve double words | `resd 25` |
+| `resq` | 8 байт | Reserve quad words | `resq 10` |
+
+### Системные вызовы (краткая таблица)
+
+| rax | Имя | rdi | rsi | rdx | Описание |
+|-----|-----|-----|-----|-----|----------|
+| 0 | read | fd | buf* | count | Чтение |
+| 1 | write | fd | buf* | count | Запись |
+| 2 | open | path* | flags | mode | Открыть файл |
+| 3 | close | fd | — | — | Закрыть файл |
+| 60 | exit | code | — | — | Завершить процесс |
+
+### Шаблон минимальной программы
+
+```nasm
+; Минимальная программа hello world
+section .data
+ msg: db 'Hello, World!', 10
+ msg_len equ $ - msg
+
+section .text
+global _start
+
+_start:
+ ; write(1, msg, msg_len)
+ mov rax, 1
+ mov rdi, 1
+ mov rsi, msg
+ mov rdx, msg_len
+ syscall
+
+ ; exit(0)
+ mov rax, 60
+ xor rdi, rdi
+ syscall
+```
+
+### Полезные ссылки
+
+- **Intel Software Developer Manual**: https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html
+- **Linux System Call Table**: https://filippo.io/linux-syscall-table/
+- **NASM Documentation**: https://www.nasm.us/xdoc/2.15.05/html/nasmdoc0.html
+- **GDB Manual**: https://sourceware.org/gdb/current/onlinedocs/gdb/
+- **x86-64 ABI**: https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
+
+---
+
+**Конец методички для семинара 1**
diff --git a/ЯПы/Семинары/Семинар-2.md b/ЯПы/Семинары/Семинар-2.md
new file mode 100644
index 0000000..4828586
--- /dev/null
+++ b/ЯПы/Семинары/Семинар-2.md
@@ -0,0 +1,2106 @@
+# Методичка по языку ассемблера x86-64
+## Семинар 2: Функции, стек, конечные автоматы
+
+---
+
+## Оглавление
+
+1. [Механизм работы стека](#1-механизм-работы-стека)
+2. [Определение и вызов функций](#2-определение-и-вызов-функций)
+3. [Функции с аргументами и возвращаемые значения](#3-функции-с-аргументами-и-возвращаемые-значения)
+4. [Соглашение вызова (Calling Convention)](#4-соглашение-вызова-calling-convention)
+5. [Использование стека для локальных переменных](#5-использование-стека-для-локальных-переменных)
+6. [Фрейм стека и выравнивание](#6-фрейм-стека-и-выравнивание)
+7. [Конечные автоматы (Finite State Machines)](#7-конечные-автоматы-finite-state-machines)
+8. [Автоматы и регулярные выражения](#8-автоматы-и-регулярные-выражения)
+9. [Кодирование автоматов на ассемблере](#9-кодирование-автоматов-на-ассемблере)
+10. [Типичные ошибки](#10-типичные-ошибки)
+11. [Практические задания](#11-практические-задания)
+12. [Справочник: часто используемые конструкции](#справочник-часто-используемые-конструкции)
+
+---
+
+## 1. Механизм работы стека
+
+### Что такое стек?
+
+**Стек (stack)** — это область памяти, организованная по принципу LIFO (Last In, First Out: последним пришёл — первым ушёл). Стек используется для:
+
+- Сохранения адресов возврата при вызовах функций
+- Хранения локальных переменных функций
+- Временного сохранения значений регистров
+- Передачи аргументов функций (свыше 6 аргументов)
+
+### Регистр RSP (Stack Pointer)
+
+`RSP` (Stack Pointer) — регистр, указывающий на **вершину стека** (самый последний добавленный элемент).
+
+**Важно:** В x86-64 Linux стек растёт **вниз** (к меньшим адресам):
+
+```
+Высокие адреса (0x7FFFFFFFFFFF)
+ ↑
+ |
+ [Пустое пространство]
+ |
+ ↓ ← RSP указывает сюда (вершина стека)
+ [Последний добавленный элемент]
+ [Предыдущий элемент]
+ [Ещё более ранний элемент]
+ |
+ ↓
+Низкие адреса
+```
+
+### Инструкции работы со стеком
+
+#### PUSH — положить на стек
+
+```asm
+push rax
+```
+
+**Эквивалентно:**
+
+```asm
+sub rsp, 8 ; Сдвинуть вершину стека на 8 байт вниз
+mov [rsp], rax ; Записать значение rax по адресу вершины
+```
+
+#### POP — снять со стека
+
+```asm
+pop rax
+```
+
+**Эквивалентно:**
+
+```asm
+mov rax, [rsp] ; Прочитать значение с вершины стека
+add rsp, 8 ; Сдвинуть вершину стека на 8 байт вверх
+```
+
+### Визуализация работы стека
+
+```mermaid
+graph TD
+ A[Начальное состояние
RSP = 0x7FFFFFFFE000]
+ A --> B[push rax где rax=0x42]
+ B --> C[RSP = 0x7FFFFFFFDFF8
память 0x...DFF8: 0x42]
+ C --> D[push rbx где rbx=0x100]
+ D --> E[RSP = 0x7FFFFFFFDFF0
память 0x...DFF0: 0x100
память 0x...DFF8: 0x42]
+ E --> F[pop rcx]
+ F --> G[RCX = 0x100
RSP = 0x7FFFFFFFDFF8]
+
+ style A fill:#334155
+ style C fill:#475569
+ style E fill:#64748b
+ style G fill:#475569
+```
+
+### Пошаговый пример
+
+Предположим, `RSP = 0x7FFFFFFFE000` перед началом.
+
+```asm
+push rax ; rax = 0x1234567890ABCDEF
+push rbx ; rbx = 0xAABBCCDDEEFF0011
+pop rcx ; rcx = ?
+pop rdx ; rdx = ?
+```
+
+| Шаг | Инструкция | RSP до | RSP после | Действие | rcx | rdx |
+|-----|------------|--------|-----------|----------|-----|-----|
+| 0 | — | `0x7FFFFFFFE000` | — | Начало | — | — |
+| 1 | `push rax` | `0x7FFFFFFFE000` | `0x7FFFFFFFDFF8` | `[0x...DFF8] = 0x1234567890ABCDEF` | — | — |
+| 2 | `push rbx` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFDFF0` | `[0x...DFF0] = 0xAABBCCDDEEFF0011` | — | — |
+| 3 | `pop rcx` | `0x7FFFFFFFDFF0` | `0x7FFFFFFFDFF8` | `rcx = [0x...DFF0]` | `0xAABBCCDDEEFF0011` | — |
+| 4 | `pop rdx` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFE000` | `rdx = [0x...DFF8]` | `0xAABBCCDDEEFF0011` | `0x1234567890ABCDEF` |
+
+**Результат:** `rcx = 0xAABBCCDDEEFF0011`, `rdx = 0x1234567890ABCDEF`, `RSP` вернулся к исходному значению.
+
+### Важное правило
+
+**Стек должен быть сбалансирован:** если вы добавили N элементов с помощью `push`, вы должны снять N элементов с помощью `pop` (или скорректировать `rsp` напрямую).
+
+Несбалансированный стек приводит к:
+- Неправильным адресам возврата (`ret` вернётся не туда)
+- Segmentation fault
+- Непредсказуемому поведению программы
+
+---
+
+## 2. Определение и вызов функций
+
+### Что такое функция в ассемблере?
+
+**Функция (подпрограмма, subroutine)** — это именованный блок инструкций, который можно вызвать из разных мест программы и который возвращает управление обратно вызывающему коду.
+
+В ассемблере функция = **метка** + **код** + **инструкция `ret`**.
+
+### Инструкция CALL
+
+```asm
+call function_name
+```
+
+**Действия `call`:**
+
+1. **Сохранить адрес возврата на стек:** `push` адрес следующей инструкции после `call`
+2. **Перейти к функции:** установить `RIP = адрес метки function_name`
+
+**Эквивалентно:**
+
+```asm
+push rip_next ; (это псевдокод, rip нельзя использовать напрямую)
+jmp function_name
+rip_next: ; адрес следующей инструкции
+```
+
+### Инструкция RET
+
+```asm
+ret
+```
+
+**Действия `ret`:**
+
+1. **Снять адрес возврата со стека:** `pop` значение в `RIP`
+2. **Перейти по этому адресу:** продолжить выполнение
+
+**Эквивалентно:**
+
+```asm
+pop rip ; (псевдокод)
+```
+
+### Визуализация call и ret
+
+```mermaid
+sequenceDiagram
+ participant C as Вызывающий код
+ participant S as Стек
+ participant F as Функция
+
+ C->>C: call my_func (адрес 0x401010)
+ C->>S: push 0x401015 (адрес возврата)
+ C->>F: jmp my_func (0x401080)
+ Note over F: Выполнение тела функции
+ F->>S: pop адрес (0x401015)
+ F->>C: jmp 0x401015
+ C->>C: Продолжить выполнение
+```
+
+### Пример: простая функция
+
+```asm
+section .data
+ msg db "Hello from function!", 0xA
+ msg_len equ $ - msg
+
+section .text
+ global _start
+
+; Функция печати сообщения
+print_hello:
+ mov rax, 1 ; sys_write
+ mov rdi, 1 ; stdout
+ mov rsi, msg
+ mov rdx, msg_len
+ syscall
+ ret ; Вернуться к вызывающему коду
+
+_start:
+ call print_hello ; Вызов функции
+ call print_hello ; Вызов ещё раз
+
+ ; Выход
+ mov rax, 60
+ xor rdi, rdi
+ syscall
+```
+
+### Пошаговая трассировка выполнения
+
+Предположим:
+- `_start` находится по адресу `0x401000`
+- `print_hello` находится по адресу `0x401020`
+- `RSP` изначально `0x7FFFFFFFE000`
+
+| RIP | Инструкция | RSP до | RSP после | Стек | Комментарий |
+|-----|------------|--------|-----------|------|-------------|
+| `0x401000` | `call print_hello` | `0x7FFFFFFFE000` | `0x7FFFFFFFDFF8` | `[...FF8]=0x401005` | Сохранить 0x401005 (адрес после call) |
+| `0x401020` | `mov rax, 1` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFDFF8` | `[...FF8]=0x401005` | Начало функции |
+| ... | (syscall и др.) | `0x7FFFFFFFDFF8` | `0x7FFFFFFFDFF8` | `[...FF8]=0x401005` | Тело функции |
+| `0x40102E` | `ret` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFE000` | — | Снять 0x401005 и прыгнуть туда |
+| `0x401005` | `call print_hello` | `0x7FFFFFFFE000` | `0x7FFFFFFFDFF8` | `[...FF8]=0x40100A` | Второй вызов |
+| `0x401020` | `mov rax, 1` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFDFF8` | `[...FF8]=0x40100A` | Снова в функции |
+| `0x40102E` | `ret` | `0x7FFFFFFFDFF8` | `0x7FFFFFFFE000` | — | Вернуться к 0x40100A |
+| `0x40100A` | `mov rax, 60` | `0x7FFFFFFFE000` | `0x7FFFFFFFE000` | — | Продолжить _start |
+
+### CALL vs JMP
+
+| Инструкция | Сохраняет адрес возврата? | Можно ли вернуться? | Использование |
+|------------|---------------------------|---------------------|---------------|
+| `call` | **Да** (на стек) | Да (`ret`) | Вызов функций, которые должны вернуться |
+| `jmp` | **Нет** | Нет | Безусловные переходы, tail calls, `exit` |
+
+**Пример tail call:**
+
+```asm
+my_function:
+ ; ... какая-то работа ...
+ jmp other_function ; Tail call: не создаём новый фрейм стека
+```
+
+Это эквивалентно:
+
+```asm
+my_function:
+ ; ... какая-то работа ...
+ call other_function
+ ret
+```
+
+Но `jmp` эффективнее: не тратит место на стеке и не добавляет лишний `ret`.
+
+### Вложенные вызовы функций
+
+```asm
+section .text
+ global _start
+
+func_c:
+ ; Самая внутренняя функция
+ ret
+
+func_b:
+ call func_c ; Вызвать func_c
+ ret ; Вернуться в func_a
+
+func_a:
+ call func_b ; Вызвать func_b
+ ret ; Вернуться в _start
+
+_start:
+ call func_a ; Вызвать func_a
+ ; Продолжить
+```
+
+**Стек при вложенных вызовах:**
+
+```
+1. _start вызывает func_a:
+ Стек: [адрес возврата в _start]
+
+2. func_a вызывает func_b:
+ Стек: [адрес возврата в func_a]
+ [адрес возврата в _start]
+
+3. func_b вызывает func_c:
+ Стек: [адрес возврата в func_b]
+ [адрес возврата в func_a]
+ [адрес возврата в _start]
+
+4. func_c делает ret:
+ Стек: [адрес возврата в func_a]
+ [адрес возврата в _start]
+
+5. func_b делает ret:
+ Стек: [адрес возврата в _start]
+
+6. func_a делает ret:
+ Стек: (пустой)
+```
+
+---
+
+## 3. Функции с аргументами и возвращаемые значения
+
+### Передача аргументов через регистры
+
+В **System V ABI** (Linux x86-64) первые **6 целочисленных/указательных аргументов** передаются через регистры:
+
+| Порядок аргумента | Регистр | Размер | Примечание |
+|-------------------|---------|--------|------------|
+| 1-й | `rdi` | 64 бита | Может использоваться `edi`, `di`, `dil` для меньших размеров |
+| 2-й | `rsi` | 64 бита | — |
+| 3-й | `rdx` | 64 бита | — |
+| 4-й | `rcx` | 64 бита | **Внимание:** разрушается при syscall! |
+| 5-й | `r8` | 64 бита | — |
+| 6-й | `r9` | 64 бита | — |
+
+**Аргументы 7+** передаются через стек (справа налево).
+
+### Возвращаемое значение
+
+| Тип результата | Регистр |
+|----------------|---------|
+| Целочисленное/указатель (до 64 бит) | `rax` |
+| Дополнительное значение (128 бит) | `rdx:rax` (старшие 64 бита в `rdx`) |
+| Числа с плавающей точкой | `xmm0`, `xmm1` |
+| Структуры (маленькие) | `rax`, `rdx` |
+| Структуры (большие) | Указатель передаётся как скрытый первый аргумент |
+
+### Пример: функция сложения
+
+```asm
+; Функция: сложить два числа
+; Аргументы: rdi = a, rsi = b
+; Возвращает: rax = a + b
+add_numbers:
+ mov rax, rdi ; rax = a
+ add rax, rsi ; rax = a + b
+ ret
+
+; Вызов:
+_start:
+ mov rdi, 10 ; Первый аргумент
+ mov rsi, 32 ; Второй аргумент
+ call add_numbers ; Результат в rax
+ ; rax теперь содержит 42
+```
+
+### Пример: функция с 3 аргументами
+
+```asm
+; Функция: вычислить (a + b) * c
+; Аргументы: rdi = a, rsi = b, rdx = c
+; Возвращает: rax = (a + b) * c
+calculate:
+ add rdi, rsi ; rdi = a + b
+ mov rax, rdi ; rax = a + b
+ imul rax, rdx ; rax = (a + b) * c
+ ret
+
+; Вызов:
+_start:
+ mov rdi, 5 ; a = 5
+ mov rsi, 3 ; b = 3
+ mov rdx, 7 ; c = 7
+ call calculate ; rax = (5+3)*7 = 56
+```
+
+### Функция string_length
+
+Вычисляет длину null-terminated строки.
+
+```asm
+; Аргумент: rdi = указатель на строку
+; Возвращает: rax = длина строки (без null-terminator)
+string_length:
+ xor rax, rax ; rax = 0 (счётчик)
+.loop:
+ cmp byte [rdi + rax], 0 ; Проверить текущий байт
+ je .done ; Если 0, выйти из цикла
+ inc rax ; rax++
+ jmp .loop
+.done:
+ ret
+
+; Использование:
+section .data
+ my_string db "Hello, World!", 0
+
+section .text
+_start:
+ mov rdi, my_string ; Передать адрес строки
+ call string_length ; rax = 13
+```
+
+### Функция print_string
+
+Выводит null-terminated строку в stdout.
+
+```asm
+; Аргумент: rdi = указатель на строку
+; Возвращает: ничего (может вернуть количество байт в rax)
+print_string:
+ push rdi ; Сохранить rdi (аргумент)
+ call string_length ; rax = длина строки
+ pop rsi ; rsi = адрес строки (восстановить из стека)
+
+ mov rdx, rax ; rdx = длина
+ mov rax, 1 ; sys_write
+ mov rdi, 1 ; stdout
+ syscall ; Вывести строку
+ ret
+
+; Использование:
+_start:
+ mov rdi, my_string
+ call print_string
+```
+
+### Функция с несколькими возвращаемыми значениями
+
+```asm
+; Функция: разделить число на целую и дробную части (упрощённый пример)
+; Аргумент: rdi = число вида XY, где X - десятки, Y - единицы
+; Возвращает: rax = X (десятки), rdx = Y (единицы)
+split_number:
+ mov rax, rdi ; rax = число
+ xor rdx, rdx ; rdx = 0
+ mov rcx, 10
+ div rcx ; rax = rax / 10, rdx = rax % 10
+ ret
+
+; Вызов:
+_start:
+ mov rdi, 47 ; Число 47
+ call split_number
+ ; rax = 4 (десятки), rdx = 7 (единицы)
+```
+
+### Пример: print_hex
+
+Выводит 64-битное число в шестнадцатеричном формате.
+
+```asm
+section .data
+ hex_digits db '0123456789ABCDEF'
+
+section .text
+ global _start
+
+; Аргумент: rdi = число для вывода
+print_hex:
+ mov rcx, 64 ; Количество бит
+.loop:
+ push rax ; Сохранить rax
+ sub rcx, 4 ; Обработать следующие 4 бита
+ mov rax, rdi ; rax = число
+ shr rax, cl ; Сдвинуть вправо на cl бит
+ and rax, 0xF ; Оставить младшие 4 бита
+
+ lea rsi, [hex_digits + rax] ; rsi = адрес соответствующей цифры
+ mov rax, 1 ; sys_write
+ mov rdx, 1 ; Длина = 1 байт
+ push rdi
+ push rcx
+ mov rdi, 1 ; stdout
+ syscall
+ pop rcx
+ pop rdi
+
+ pop rax
+ test rcx, rcx
+ jnz .loop
+ ret
+
+_start:
+ mov rdi, 0x1234ABCD ; Число для вывода
+ call print_hex
+
+ ; Вывод переноса строки
+ mov rax, 1
+ mov rdi, 1
+ mov rsi, newline
+ mov rdx, 1
+ syscall
+
+ mov rax, 60
+ xor rdi, rdi
+ syscall
+
+section .data
+ newline db 0xA
+```
+
+---
+
+## 4. Соглашение вызова (Calling Convention)
+
+### Что такое Calling Convention?
+
+**Соглашение вызова (calling convention)** — это набор правил, определяющих:
+
+1. Как передаются аргументы (регистры, стек, порядок)
+2. Какой регистр используется для возвращаемого значения
+3. Какие регистры должны сохраняться функцией (callee-saved)
+4. Какие регистры может свободно изменять функция (caller-saved)
+5. Как выравнивается стек
+6. Кто очищает стек после вызова
+
+### System V AMD64 ABI (Linux, BSD, macOS)
+
+Это стандартное соглашение для Unix-подобных систем на x86-64.
+
+### Классификация регистров
+
+```mermaid
+graph TD
+ A[Регистры x86-64]
+ A --> B[Caller-saved
Вызывающий сохраняет]
+ A --> C[Callee-saved
Вызываемая функция сохраняет]
+
+ B --> D[rax - возвращаемое значение]
+ B --> E[rdi, rsi, rdx, rcx, r8, r9 - аргументы]
+ B --> F[r10, r11 - временные]
+
+ C --> G[rbx]
+ C --> H[rbp - frame pointer]
+ C --> I[r12, r13, r14, r15]
+ C --> J[rsp - stack pointer
всегда сохраняется!]
+
+ style B fill:#ef4444
+ style C fill:#22c55e
+ style D fill:#fca5a5
+ style E fill:#fca5a5
+ style F fill:#fca5a5
+ style G fill:#86efac
+ style H fill:#86efac
+ style I fill:#86efac
+ style J fill:#86efac
+```
+
+### Таблица классификации регистров
+
+| Регистр | Тип | Назначение | Сохраняется? |
+|---------|-----|------------|--------------|
+| **rax** | Caller-saved | Возвращаемое значение, syscall номер | ❌ Нет |
+| **rbx** | Callee-saved | Общего назначения | ✅ Да |
+| **rcx** | Caller-saved | 4-й аргумент | ❌ Нет (разрушается при syscall!) |
+| **rdx** | Caller-saved | 3-й аргумент, 2-е возвращаемое значение | ❌ Нет |
+| **rsi** | Caller-saved | 2-й аргумент | ❌ Нет |
+| **rdi** | Caller-saved | 1-й аргумент | ❌ Нет |
+| **rbp** | Callee-saved | Frame pointer (база стека) | ✅ Да |
+| **rsp** | Специальный | Stack pointer (вершина стека) | ✅ **Всегда** |
+| **r8** | Caller-saved | 5-й аргумент | ❌ Нет |
+| **r9** | Caller-saved | 6-й аргумент | ❌ Нет |
+| **r10** | Caller-saved | Временный | ❌ Нет |
+| **r11** | Caller-saved | Временный | ❌ Нет (разрушается при syscall!) |
+| **r12-r15** | Callee-saved | Общего назначения | ✅ Да |
+
+### Что означает "Caller-saved" и "Callee-saved"?
+
+#### Caller-saved (сохраняет вызывающий)
+
+Если функция **A** вызывает функцию **B**, и функция **A** использует регистры `rax`, `rdi`, `rsi` и т.д., то:
+
+- Функция **A** должна **сохранить** эти регистры **перед** вызовом **B** (если значения нужны после вызова)
+- Функция **B** **может свободно изменять** эти регистры без сохранения
+
+**Пример:**
+
+```asm
+function_a:
+ mov rax, 42 ; Используем rax
+
+ ; Хотим вызвать function_b, но нужно сохранить rax
+ push rax ; Сохранить rax (caller-saved)
+ call function_b ; function_b может изменить rax
+ pop rax ; Восстановить rax
+
+ ; Теперь rax снова содержит 42
+ ret
+
+function_b:
+ mov rax, 999 ; Можем свободно изменять rax
+ ret
+```
+
+#### Callee-saved (сохраняет вызываемая функция)
+
+Если функция **B** хочет использовать регистры `rbx`, `r12-r15`, `rbp`, то:
+
+- Функция **B** должна **сохранить** эти регистры в начале и **восстановить** перед `ret`
+- Функция **A** может **не беспокоиться** об этих регистрах — они останутся неизменными после вызова **B**
+
+**Пример:**
+
+```asm
+function_a:
+ mov rbx, 100 ; Используем rbx
+ call function_b ; function_b сохранит rbx внутри себя
+ ; rbx всё ещё содержит 100
+ ret
+
+function_b:
+ push rbx ; Сохранить rbx (callee-saved)
+ mov rbx, 777 ; Используем rbx для своих нужд
+ ; ... работа с rbx ...
+ pop rbx ; Восстановить rbx
+ ret
+```
+
+### Почему такое разделение?
+
+**Производительность и оптимизация:**
+
+- **Caller-saved** регистров больше → функции могут использовать много регистров для аргументов и временных значений без накладных расходов
+- **Callee-saved** регистров меньше, но они гарантируют стабильность → удобно для переменных, которые должны "пережить" вызовы функций
+- Компилятор может оптимизировать, сохраняя только действительно используемые регистры
+
+### Сравнение: функция VS системный вызов
+
+| Аспект | Функция (System V ABI) | Системный вызов (syscall) |
+|--------|------------------------|---------------------------|
+| Номер операции | — | `rax` |
+| Аргумент 1 | `rdi` | `rdi` |
+| Аргумент 2 | `rsi` | `rsi` |
+| Аргумент 3 | `rdx` | `rdx` |
+| Аргумент 4 | `rcx` | **`r10`** (не `rcx`!) |
+| Аргумент 5 | `r8` | `r8` |
+| Аргумент 6 | `r9` | `r9` |
+| Возвращаемое значение | `rax` | `rax` |
+| Разрушаемые регистры | Caller-saved | **`rcx`, `r11`** (всегда!) |
+| Callee-saved | `rbx`, `rbp`, `r12-r15` | `rbx`, `rbp`, `r12-r15` |
+
+**Важно:** Системный вызов **всегда разрушает `rcx` и `r11`**, даже если они не используются для аргументов!
+
+### Пример: сохранение регистров
+
+```asm
+my_function:
+ ; Хотим использовать rbx, r12, r13 (callee-saved)
+ push rbx
+ push r12
+ push r13
+
+ mov rbx, rdi ; Сохранить 1-й аргумент
+ mov r12, rsi ; Сохранить 2-й аргумент
+ mov r13, rdx ; Сохранить 3-й аргумент
+
+ ; Вызов другой функции (может разрушить caller-saved)
+ call some_other_function
+
+ ; rbx, r12, r13 всё ещё содержат исходные значения
+ mov rax, rbx
+ add rax, r12
+ add rax, r13
+
+ ; Восстановить callee-saved регистры
+ pop r13
+ pop r12
+ pop rbx
+ ret
+```
+
+---
+
+## 5. Локальные переменные на стеке
+
+### Зачем нужен фрейм стека?
+
+Когда функции требуются локальные переменные, массивы или буферы, их размещают на стеке. Для управления локальными данными используется **фрейм стека** (stack frame) — структура, ограниченная двумя регистрами:
+
+- **`rbp`** (base pointer) — указатель на базу текущего фрейма (фиксированная точка отсчёта)
+- **`rsp`** (stack pointer) — указатель на вершину стека (динамически меняется при push/pop)
+
+### Структура фрейма стека
+
+```mermaid
+graph TD
+ A["Высокие адреса"] --> B["[arg 8] ← rbp + 24"]
+ B --> C["[arg 7] ← rbp + 16"]
+ C --> D["[return address] ← rbp + 8"]
+ D --> E["[старый rbp] ← rbp (saved frame pointer)"]
+ E --> F["[локальная переменная 1] ← rbp - 8"]
+ F --> G["[локальная переменная 2] ← rbp - 16"]
+ G --> H["[временные данные] ← rsp (текущая вершина стека)"]
+ H --> I["Низкие адреса"]
+
+ style E fill:#ffd700
+ style D fill:#ff6b6b
+ style F fill:#4ecdc4
+ style G fill:#4ecdc4
+```
+
+**Ключевые особенности:**
+
+1. **Аргументы 1-6** передаются в регистрах, аргументы **7+** — на стеке (выше return address)
+2. **Return address** сохраняется автоматически инструкцией `call`
+3. **Старый rbp** сохраняется в прологе функции для восстановления фрейма вызывающей функции
+4. **Локальные переменные** располагаются ниже rbp (отрицательные смещения)
+5. **rbp** остаётся неизменным в течение выполнения функции, что упрощает адресацию локальных переменных
+
+### Пролог и эпилог функции
+
+**Стандартный пролог:**
+```nasm
+push rbp ; Сохранить старый базовый указатель
+mov rbp, rsp ; Установить rbp на текущую позицию стека
+sub rsp, N ; Выделить N байт для локальных переменных
+```
+
+**Стандартный эпилог:**
+```nasm
+mov rsp, rbp ; Освободить локальные переменные (восстановить rsp)
+pop rbp ; Восстановить старый базовый указатель
+ret ; Вернуться (pop return address в rip)
+```
+
+**Альтернативные инструкции:**
+- `enter N, 0` — эквивалент пролога (медленнее, редко используется)
+- `leave` — эквивалент `mov rsp, rbp; pop rbp` (часто используется)
+
+### Пример: функция с локальным массивом
+
+```nasm
+; Функция, которая создаёт локальный массив из 5 qword
+; и заполняет его квадратами чисел 0-4
+; Возвращает сумму элементов в rax
+
+square_sum:
+ ; === ПРОЛОГ ===
+ push rbp ; Сохранить старый rbp
+ mov rbp, rsp ; rbp теперь указывает на сохранённый rbp
+ sub rsp, 40 ; Выделить 40 байт (5 qword по 8 байт)
+ ; После пролога:
+ ; rbp+8 → return address
+ ; rbp+0 → старый rbp
+ ; rbp-8 → array[0]
+ ; rbp-16 → array[1]
+ ; rbp-24 → array[2]
+ ; rbp-32 → array[3]
+ ; rbp-40 → array[4] ← rsp
+
+ ; === ТЕЛО ФУНКЦИИ ===
+ ; Заполнить массив квадратами
+ xor rcx, rcx ; Счётчик (i = 0)
+.fill_loop:
+ cmp rcx, 5
+ je .fill_done
+
+ mov rax, rcx ; rax = i
+ imul rax, rax ; rax = i * i
+
+ ; Вычислить адрес array[i] = rbp - 8*(i+1)
+ lea rdx, [rcx + 1] ; rdx = i + 1
+ shl rdx, 3 ; rdx = (i + 1) * 8
+ neg rdx ; rdx = -(i + 1) * 8
+ mov [rbp + rdx], rax ; array[i] = i²
+
+ inc rcx
+ jmp .fill_loop
+
+.fill_done:
+ ; Вычислить сумму элементов
+ xor rax, rax ; sum = 0
+ xor rcx, rcx ; i = 0
+.sum_loop:
+ cmp rcx, 5
+ je .sum_done
+
+ lea rdx, [rcx + 1]
+ shl rdx, 3
+ neg rdx
+ add rax, [rbp + rdx] ; sum += array[i]
+
+ inc rcx
+ jmp .sum_loop
+
+.sum_done:
+ ; rax содержит сумму 0² + 1² + 2² + 3² + 4² = 30
+
+ ; === ЭПИЛОГ ===
+ mov rsp, rbp ; Освободить локальные переменные
+ pop rbp ; Восстановить старый rbp
+ ret ; Вернуться
+
+; Альтернативный эпилог:
+; leave ; Эквивалентно mov rsp, rbp; pop rbp
+; ret
+```
+
+### Пошаговая трассировка создания фрейма
+
+Предположим, функция `square_sum` вызвана из адреса `0x401234`. Проследим изменения стека:
+
+| Шаг | Инструкция | rsp | rbp | Стек (содержимое по адресам) |
+|-----|-----------|-----|-----|-----------------------------|
+| 0 | `call square_sum` | `0x7fff0000` | `0x7fff0100` | `[0x7fff0000] = 0x401239` (return addr) |
+| 1 | `push rbp` | `0x7ffefff8` | `0x7fff0100` | `[0x7ffefff8] = 0x7fff0100` (старый rbp) |
+| 2 | `mov rbp, rsp` | `0x7ffefff8` | `0x7ffefff8` | rbp теперь указывает на новый фрейм |
+| 3 | `sub rsp, 40` | `0x7ffeFFd0` | `0x7ffefff8` | Выделено 40 байт для массива |
+| 4 | Заполнение array[0] | `0x7ffeffd0` | `0x7ffefff8` | `[rbp-8] = 0` (0²) |
+| 5 | Заполнение array[1] | `0x7ffeffd0` | `0x7ffefff8` | `[rbp-16] = 1` (1²) |
+| ... | ... | ... | ... | ... |
+| N | `mov rsp, rbp` | `0x7ffefff8` | `0x7ffefff8` | Локальные переменные освобождены |
+| N+1 | `pop rbp` | `0x7fff0000` | `0x7fff0100` | rbp восстановлен |
+| N+2 | `ret` | `0x7fff0008` | `0x7fff0100` | Возврат по адресу 0x401239 |
+
+### Почему используется rbp?
+
+**Без rbp (использование только rsp):**
+- Каждая операция push/pop в теле функции смещает rsp
+- Смещения локальных переменных относительно rsp постоянно меняются
+- Компилятор должен отслеживать текущее смещение rsp на каждой инструкции
+- Усложняется отладка (стековые трейсы требуют сложного раскручивания)
+
+**С rbp:**
+- rbp фиксирован в течение выполнения функции
+- Локальные переменные имеют постоянные смещения: `[rbp-8]`, `[rbp-16]`, ...
+- Отладчики могут легко найти границы фрейма и параметры
+- Упрощается генерация debug-информации (DWARF)
+
+**Современная оптимизация:** компиляторы с флагом `-fomit-frame-pointer` могут не использовать rbp, освобождая ещё один регистр общего назначения. Это требует точного отслеживания глубины стека на этапе компиляции.
+
+---
+
+## 6. Выравнивание стека (Stack Alignment)
+
+### Зачем нужно выравнивание?
+
+Системное соглашение **System V AMD64 ABI** требует, чтобы стек был выровнен на **16-байтную границу** в момент выполнения инструкции `call`. Это означает, что `rsp` должен быть кратен 16 перед `call`.
+
+**Причины требования:**
+
+1. **SSE/AVX инструкции**: многие SIMD-инструкции (например, `movaps`, `movdqa`) требуют выравнивания на 16 байт для работы с памятью. Невыровненный доступ вызывает исключение `SIGSEGV` или `SIGBUS`.
+2. **Производительность**: даже инструкции, которые допускают невыровненный доступ (например, `movups`), работают медленнее при нарушении выравнивания.
+3. **Совместимость с библиотеками**: все системные библиотеки (libc, libm) ожидают выровненный стек.
+
+### Механика выравнивания
+
+```mermaid
+graph TD
+ A["До вызова функции: rsp выровнен на 16"] --> B["call: push return address (8 байт)"]
+ B --> C["Внутри функции: rsp = старое_значение - 8"]
+ C --> D{"rsp кратен 16?"}
+ D -->|Нет| E["rsp = (old_rsp - 8) mod 16 = 8"]
+ E --> F["Невыровненный стек!"]
+ D -->|Да| G["Функция должна сохранить выравнивание"]
+
+ style F fill:#ff6b6b
+ style G fill:#4ecdc4
+```
+
+**Важное следствие:** после `call` стек **смещён на 8 байт** (return address). Если до `call` было `rsp % 16 == 0`, то после `call` будет `rsp % 16 == 8`.
+
+### Кто отвечает за выравнивание?
+
+**Вызывающая функция (caller):**
+- Гарантирует, что `rsp % 16 == 0` перед инструкцией `call`
+- Если нужно передать аргументы через стек (7+), должна учесть их при выравнивании
+
+**Вызываемая функция (callee):**
+- Может предполагать, что `rsp % 16 == 8` в начале (после `call`)
+- Если нужно вызвать другую функцию, должна восстановить выравнивание на 16
+
+### Визуализация: выравнивание при вложенных вызовах
+
+```mermaid
+sequenceDiagram
+ participant Main as main (rsp=0x7fff0000)
+ participant FuncA as func_a
+ participant FuncB as func_b
+
+ Note over Main: rsp = 0x7fff0000 (кратно 16)
+ Main->>FuncA: call func_a
+ Note over FuncA: rsp = 0x7ffefff8 (0x7fff0000 - 8)
НЕ кратно 16!
+
+ Note over FuncA: push rbp (rsp -= 8)
rsp = 0x7ffefff0 (кратно 16)
+ Note over FuncA: sub rsp, 24
rsp = 0x7ffeffe8 (НЕ кратно 16)
+ Note over FuncA: Нужно выровнять перед вызовом!
+ Note over FuncA: sub rsp, 8
rsp = 0x7ffeffd8 (кратно 16)
+
+ FuncA->>FuncB: call func_b
+ Note over FuncB: rsp = 0x7ffeffD0 (0x7ffeffd8 - 8)
Смещение 8, как ожидалось
+ FuncB-->>FuncA: ret
+
+ Note over FuncA: add rsp, 8 (убрать padding)
+ Note over FuncA: add rsp, 24 (освободить локальные)
+ Note over FuncA: pop rbp
+ FuncA-->>Main: ret
+```
+
+### Пример: ручное выравнивание стека
+
+```nasm
+; Функция, которая вызывает printf с выравниванием стека
+section .data
+ format_str: db "Value: %d", 10, 0
+
+section .text
+extern printf
+
+print_value:
+ ; Аргумент в rdi = значение для печати
+
+ ; === ПРОЛОГ ===
+ push rbp ; rsp -= 8, теперь rsp % 16 == 0
+ mov rbp, rsp
+
+ ; Сохранить аргумент (rdi может быть испорчен)
+ push rdi ; rsp -= 8, теперь rsp % 16 == 8 (невыровнен!)
+
+ ; === ВЫРАВНИВАНИЕ ===
+ ; Перед вызовом printf нужно rsp % 16 == 0
+ ; Сейчас rsp % 16 == 8, поэтому нужно вычесть ещё 8
+ sub rsp, 8 ; Padding для выравнивания
+ ; Теперь rsp % 16 == 0
+
+ ; === ВЫЗОВ PRINTF ===
+ mov rdi, format_str ; 1-й аргумент: формат
+ mov rsi, [rbp - 8] ; 2-й аргумент: сохранённое значение
+ xor rax, rax ; printf - varargs, требует rax=0
+ call printf ; Стек выровнен!
+
+ ; === ОЧИСТКА ===
+ add rsp, 8 ; Убрать padding
+ pop rdi ; Восстановить (хотя уже не нужен)
+
+ ; === ЭПИЛОГ ===
+ pop rbp
+ ret
+```
+
+### Автоматическое выравнивание: формула
+
+Предположим, функции нужно:
+- Сохранить N callee-saved регистров (N × 8 байт)
+- Выделить M байт для локальных переменных
+
+**Алгоритм:**
+
+1. После `push rbp` стек выровнен на 16 (так как до `call` было кратно 16, `call` вычел 8, `push rbp` вычел ещё 8)
+2. Вычислить общий размер: `total = N*8 + M`
+3. Если `total % 16 != 0`, добавить padding: `padding = (16 - (total % 16)) % 16`
+4. Выделить: `sub rsp, total + padding`
+
+**Пример:**
+```nasm
+; Нужно: 3 callee-saved регистра (rbx, r12, r13) + 20 байт локальных
+; total = 3*8 + 20 = 44
+; 44 % 16 = 12, padding = 16 - 12 = 4
+
+push rbp
+mov rbp, rsp
+push rbx
+push r12
+push r13 ; Сохранено 3*8 = 24 байта
+sub rsp, 20 + 4 ; 20 локальных + 4 padding = 24
+; Теперь rsp % 16 == 0
+```
+
+### Битовая маска для выравнивания
+
+Часто используется трюк с битовой маской:
+
+```nasm
+; Выровнять rsp на 16-байтную границу (округлить вниз)
+and rsp, -16 ; Обнулить младшие 4 бита
+; -16 в дополнительном коде = 0xFFFFFFFFFFFFFFF0
+; AND с этой маской убирает биты [3:0]
+```
+
+**Пример использования:**
+```nasm
+_start:
+ ; rsp может быть любым при старте программы
+ and rsp, -16 ; Принудительно выровнять
+ call main ; Теперь main получит выровненный стек
+```
+
+### Типичная ошибка: забыть про выравнивание
+
+**Неправильно:**
+```nasm
+my_function:
+ push rbp
+ mov rbp, rsp
+ sub rsp, 32 ; Выделить 32 байта
+ ; rsp % 16 == 8 (невыровнен!)
+ call some_function ; SEGFAULT, если some_function использует SSE!
+```
+
+**Правильно:**
+```nasm
+my_function:
+ push rbp ; rsp % 16 == 0 после этого
+ mov rbp, rsp
+ sub rsp, 32 ; Всё ещё rsp % 16 == 0 (32 кратно 16)
+ call some_function ; OK!
+```
+
+**Или с нечётным размером:**
+```nasm
+my_function:
+ push rbp ; rsp % 16 == 0
+ mov rbp, rsp
+ sub rsp, 24 ; 24 % 16 = 8, теперь rsp % 16 == 8
+ sub rsp, 8 ; Добавить padding, rsp % 16 == 0
+ call some_function ; OK!
+ add rsp, 32 ; Освободить всё (24 + 8)
+ pop rbp
+ ret
+```
+
+### Проверка выравнивания в GDB
+
+```bash
+(gdb) p/x $rsp
+$1 = 0x7fffffffe4a8
+
+# Проверить выравнивание:
+(gdb) p $rsp % 16
+$2 = 8 # Не выровнен! Должно быть 0 перед call
+
+# Или:
+(gdb) p/t $rsp # Вывести в двоичном виде
+$3 = 111111111111111111111111111100101000
+# ^^^^ младшие 4 бита должны быть 0000
+```
+
+---
+
+## 7. Конечные автоматы: теория
+
+### Что такое конечный автомат?
+
+**Конечный автомат** (Finite State Machine, FSM) — математическая модель вычислений, состоящая из:
+
+1. **Конечного множества состояний** Q = {q₀, q₁, ..., qₙ}
+2. **Начального состояния** q₀ ∈ Q
+3. **Множества принимающих (конечных) состояний** F ⊆ Q
+4. **Входного алфавита** Σ (например, ASCII-символы)
+5. **Функции переходов** δ: Q × Σ → Q
+
+**Детерминированный автомат (DFA):** для каждого состояния и символа существует ровно один переход.
+
+**Недетерминированный автомат (NFA):** может иметь несколько переходов для одной пары (состояние, символ) или ε-переходы.
+
+### Пример 1: проверка чётности количества единиц
+
+**Задача:** принять строку из '0' и '1', если в ней чётное количество единиц.
+
+```mermaid
+stateDiagram-v2
+ [*] --> Even: начало
+ Even --> Odd: 1
+ Even --> Even: 0
+ Odd --> Even: 1
+ Odd --> Odd: 0
+ Even --> [*]: конец (принять)
+
+ note right of Even
+ Чётное количество 1
+ (включая 0)
+ end note
+
+ note right of Odd
+ Нечётное количество 1
+ end note
+```
+
+**Таблица переходов:**
+
+| Состояние | Символ '0' | Символ '1' |
+|-----------|------------|------------|
+| Even | Even | Odd |
+| Odd | Odd | Even |
+
+**Код на ассемблере:**
+
+```nasm
+; Проверка чётности единиц
+; Вход: rdi = указатель на строку
+; Выход: rax = 1 (чётно), 0 (нечётно)
+
+check_parity:
+ xor rax, rax ; Начальное состояние: Even (rax = 0)
+
+.loop:
+ movzx rcx, byte [rdi] ; Прочитать символ
+ test rcx, rcx ; Конец строки?
+ jz .done
+
+ cmp cl, '1' ; Если символ == '1'
+ jne .next
+ xor rax, 1 ; Переключить состояние (Even ↔ Odd)
+
+.next:
+ inc rdi
+ jmp .loop
+
+.done:
+ ; rax = 0 (Even, принять) или 1 (Odd, отклонить)
+ xor rax, 1 ; Инвертировать: 1 = принято, 0 = отклонено
+ ret
+```
+
+### Пример 2: распознавание целых чисел
+
+**Задача:** проверить, является ли строка корректным целым числом (возможен знак +/−, затем цифры).
+
+**Грамматика (регулярное выражение):** `[+-]?[0-9]+`
+
+**Автомат:**
+
+```mermaid
+stateDiagram-v2
+ [*] --> Start
+ Start --> Sign: +, -
+ Start --> Digit: 0-9
+ Sign --> Digit: 0-9
+ Digit --> Digit: 0-9
+ Digit --> [*]: конец строки (принять)
+
+ Start --> Error: другой символ
+ Sign --> Error: другой символ
+ Digit --> Error: другой символ
+ Error --> [*]: отклонить
+
+ note right of Start
+ Начальное состояние
+ end note
+
+ note right of Sign
+ Прочитан знак
+ end note
+
+ note right of Digit
+ Прочитана хотя бы
+ одна цифра
+ end note
+```
+
+**Состояния:**
+- **Start (A)**: начало, ожидаем знак или цифру
+- **Sign (B)**: прочитан знак, ожидаем цифру
+- **Digit (C)**: прочитана хотя бы одна цифра (принимающее состояние)
+- **Error (E)**: обнаружен недопустимый символ (отклонение)
+
+**Таблица переходов:**
+
+| Состояние | '+' или '−' | '0'–'9' | Конец строки | Другое |
+|-----------|-------------|---------|--------------|--------|
+| Start (A) | Sign (B) | Digit (C)| Error (E) | Error (E)|
+| Sign (B) | Error (E) | Digit (C)| Error (E) | Error (E)|
+| Digit (C) | Error (E) | Digit (C)| **Accept** | Error (E)|
+| Error (E) | Error (E) | Error (E)| **Reject** | Error (E)|
+
+### Реализация автомата в ассемблере: паттерн
+
+**Типичная структура:**
+
+```nasm
+; Шаблон для реализации DFA
+check_pattern:
+ ; Инициализация: начальное состояние
+ ; (можно использовать регистр для хранения состояния)
+
+.state_A:
+ call read_symbol ; Прочитать следующий символ в al
+ test al, al ; Конец ввода?
+ jz .reject ; В состоянии A конец = ошибка
+
+ ; Проверить переходы из состояния A
+ cmp al, '+'
+ je .state_B
+ cmp al, '-'
+ je .state_B
+ cmp al, '0'
+ jb .reject
+ cmp al, '9'
+ ja .reject
+ jmp .state_C ; Цифра: переход в C
+
+.state_B:
+ call read_symbol
+ test al, al
+ jz .reject ; Только знак без цифр
+
+ cmp al, '0'
+ jb .reject
+ cmp al, '9'
+ ja .reject
+ jmp .state_C
+
+.state_C:
+ call read_symbol
+ test al, al
+ jz .accept ; Конец в состоянии C = успех
+
+ cmp al, '0'
+ jb .reject
+ cmp al, '9'
+ ja .reject
+ jmp .state_C ; Петля: ещё цифры
+
+.accept:
+ mov rax, 1
+ ret
+
+.reject:
+ xor rax, rax
+ ret
+```
+
+### Свойства конечных автоматов
+
+**Преимущества:**
+- Эффективная проверка за O(n), где n — длина входа
+- Детерминированность: нет backtracking, каждый символ обрабатывается один раз
+- Просто реализовать на низком уровне (ассемблер, FPGA)
+- Легко доказать корректность (формальная верификация)
+
+**Ограничения:**
+- Не могут считать (например, проверить баланс скобок)
+- Не могут распознать язык `{aⁿbⁿ | n ≥ 0}` (нужен стек → автомат с магазинной памятью)
+- Не могут проверить, что число является палиндромом произвольной длины
+
+**Что могут DFA:**
+- Все регулярные языки (эквивалентны регулярным выражениям)
+- Лексический анализ (токенизация)
+- Валидация формата (email, URL, числа)
+- Протоколы с фиксированным числом состояний
+
+---
+
+## 8. Реализация FSM: анализ check_int.asm
+
+### Автомат из check_int.asm
+
+Программа `check_int.asm` реализует автомат для распознавания целых чисел (с опциональным знаком). Разберём его подробно.
+
+**Диаграмма состояний:**
+
+```mermaid
+stateDiagram-v2
+ [*] --> A: начало
+ A --> B: + или -
+ A --> C: 0-9
+ A --> E: другое
+
+ B --> C: 0-9
+ B --> E: другое
+
+ C --> C: 0-9
+ C --> D: конец ввода (\0 или \n)
+ C --> E: другое
+
+ D --> [*]: принять (Okay)
+ E --> [*]: отклонить (Fail)
+
+ note right of A
+ Начальное состояние:
+ ожидаем знак или цифру
+ end note
+
+ note right of B
+ Прочитан знак:
+ теперь нужна цифра
+ end note
+
+ note right of C
+ Прочитана хотя бы
+ одна цифра:
+ принимающее состояние
+ end note
+```
+
+### Код с комментариями
+
+```nasm
+global _start
+_start:
+ ; === СОСТОЯНИЕ A: начало ===
+ _A:
+ call getsymbol ; Прочитать символ в al
+
+ ; Проверить на знак '+'
+ cmp al, '+'
+ je _B ; Переход A → B
+
+ ; Проверить на знак '-'
+ cmp al, '-'
+ je _B ; Переход A → B
+
+ ; Проверить на цифру (ASCII '0' = 0x30, '9' = 0x39)
+ cmp al, '0'
+ jb _E ; Меньше '0' → ошибка
+ cmp al, '9'
+ ja _E ; Больше '9' → ошибка
+ jmp _C ; Цифра: переход A → C
+
+ ; === СОСТОЯНИЕ B: прочитан знак ===
+ _B:
+ call getsymbol ; Прочитать следующий символ
+
+ ; После знака ОБЯЗАТЕЛЬНО должна быть цифра
+ cmp al, '0'
+ jb _E
+ cmp al, '9'
+ ja _E
+ jmp _C ; Переход B → C
+
+ ; === СОСТОЯНИЕ C: читаем цифры ===
+ _C:
+ call getsymbol
+
+ ; Проверить на конец ввода
+ test al, al ; Проверить на NULL (\0)
+ jz _D ; Конец ввода → принять (C → D)
+
+ cmp al, 10 ; Проверить на \n (newline)
+ jz _D ; Newline → принять (C → D)
+
+ ; Проверить, ещё одна цифра?
+ cmp al, '0'
+ jb _E ; Не цифра → ошибка
+ cmp al, '9'
+ ja _E
+ jmp _C ; Ещё цифра → петля C → C
+
+ ; === СОСТОЯНИЕ D: принятие ===
+ _D:
+ mov rdi, ok_msg ; Указатель на "Okay\n"
+ mov rsi, 5 ; Длина строки
+ call print_string
+ call exit
+
+ ; === СОСТОЯНИЕ E: ошибка ===
+ _E:
+ mov rdi, fail_msg ; Указатель на "Fail\n"
+ mov rsi, 5
+ call print_string
+ call exit
+```
+
+### Ключевые особенности реализации
+
+1. **Метки = состояния**: каждой метке (`_A`, `_B`, `_C`) соответствует состояние автомата
+2. **Переходы = переходы**: `jmp`, `je`, `ja`, `jb` реализуют функцию переходов δ
+3. **Чтение символов**: функция `getsymbol` читает по одному символу из stdin
+4. **Обработка EOF**: `getsymbol` возвращает 0 при конце файла
+5. **Принимающее состояние**: только состояние C может перейти в D (принятие)
+
+### Функция getsymbol: чтение одного символа
+
+```nasm
+getsymbol:
+ xor rax, rax ; syscall 0 = read
+ xor rdi, rdi ; fd = 0 (stdin)
+ dec rsp ; Выделить 1 байт на стеке
+ mov rsi, rsp ; Буфер = вершина стека
+ mov rdx, 1 ; Читать 1 байт
+ syscall ; read(0, rsp, 1)
+
+ test rax, rax ; Проверить результат
+ jz .EOF ; Если rax == 0 → конец файла
+
+ mov al, [rsp] ; Загрузить прочитанный символ
+ jmp .END
+
+.EOF:
+ xor rax, rax ; Вернуть 0 (конец ввода)
+
+.END:
+ inc rsp ; Восстановить стек
+ ret
+```
+
+**Особенности:**
+- Использует стек как временный буфер (1 байт)
+- `dec rsp` / `inc rsp` выделяют и освобождают место на стеке
+- Возвращает символ в `al` (младший байт rax)
+- При EOF возвращает 0
+
+---
+
+## 9. Пошаговая трассировка работы FSM
+
+### Пример 1: ввод "123"
+
+**Входная строка:** `'1', '2', '3', '\n'`
+
+| Шаг | Состояние | Прочитанный символ (al) | Проверка | Переход |
+|------|----------|-------------------|-----------|---------|
+| 1 | **A** (начало) | '1' (0x31) | Не '+', не '−' | Проверка цифры |
+| | | | '0' ≤ '1' ≤ '9' | **A → C** |
+| 2 | **C** | '2' (0x32) | Не \0, не \n | Проверка цифры |
+| | | | '0' ≤ '2' ≤ '9' | **C → C** (петля) |
+| 3 | **C** | '3' (0x33) | Не \0, не \n | Проверка цифры |
+| | | | '0' ≤ '3' ≤ '9' | **C → C** |
+| 4 | **C** | '\n' (0x0A) | cmp al, 10 | **C → D** |
+| 5 | **D** (принятие) | — | Вывод "Okay" | exit(0) |
+
+**Результат:** принято ✓
+
+### Пример 2: ввод "-45"
+
+**Входная строка:** `'-', '4', '5', '\n'`
+
+| Шаг | Состояние | Прочитанный символ | Проверка | Переход |
+|------|----------|-------------------|-----------|---------|
+| 1 | **A** | '−' (0x2D) | cmp al, '-' | **A → B** (je _B) |
+| 2 | **B** | '4' (0x34) | '0' ≤ '4' ≤ '9' | **B → C** |
+| 3 | **C** | '5' (0x35) | Не \0, не \n | Проверка цифры |
+| | | | '0' ≤ '5' ≤ '9' | **C → C** |
+| 4 | **C** | '\n' (0x0A) | cmp al, 10 | **C → D** |
+| 5 | **D** | — | Вывод "Okay" | exit(0) |
+
+**Результат:** принято ✓
+
+### Пример 3: ввод "+" (ошибка)
+
+**Входная строка:** `'+', '\n'`
+
+| Шаг | Состояние | Прочитанный символ | Проверка | Переход |
+|------|----------|-------------------|-----------|---------|
+| 1 | **A** | '+' (0x2B) | cmp al, '+' | **A → B** |
+| 2 | **B** | '\n' (0x0A) | cmp al, '0' | al < '0' (0x0A < 0x30) |
+| | | | jb _E | **B → E** |
+| 3 | **E** (ошибка) | — | Вывод "Fail" | exit(0) |
+
+**Результат:** отклонено ✗ (знак без цифр)
+
+### Пример 4: ввод "12a3" (ошибка)
+
+**Входная строка:** `'1', '2', 'a', '3'`
+
+| Шаг | Состояние | Прочитанный символ | Проверка | Переход |
+|------|----------|-------------------|-----------|---------|
+| 1 | **A** | '1' | Цифра | **A → C** |
+| 2 | **C** | '2' | Цифра | **C → C** |
+| 3 | **C** | 'a' (0x61) | Не \0, не \n | Проверка цифры |
+| | | | cmp al, '9' | 'a' > '9' (0x61 > 0x39) |
+| | | | ja _E | **C → E** |
+| 4 | **E** | — | Вывод "Fail" | exit(0) |
+
+**Результат:** отклонено ✗ (недопустимый символ)
+
+### Детальная трассировка с регистрами: "-7"
+
+**Вход:** `echo "-7" | ./check_int`
+
+| Шаг | RIP (метка) | Инструкция | al | Флаги | Примечание |
+|------|---------|------------|-----|--------|-------------|
+| 1 | `_A` | `call getsymbol` | ? | — | Чтение символа |
+| 2 | `_A` | (after call) | 0x2D ('-') | — | Символ прочитан |
+| 3 | `_A` | `cmp al, '+'` | 0x2D | ZF=0 | 0x2D ≠ 0x2B |
+| 4 | `_A` | `je _B` | 0x2D | ZF=0 | Не переходит |
+| 5 | `_A` | `cmp al, '-'` | 0x2D | ZF=1 | 0x2D == 0x2D |
+| 6 | `_A` | `je _B` | 0x2D | ZF=1 | **Переход A → B** |
+| 7 | `_B` | `call getsymbol` | 0x2D | — | Читаем след. символ |
+| 8 | `_B` | (after call) | 0x37 ('7') | — | Прочитано '7' |
+| 9 | `_B` | `cmp al, '0'` | 0x37 | CF=0 | 0x37 ≥ 0x30 |
+| 10 | `_B` | `jb _E` | 0x37 | CF=0 | Не переходит |
+| 11 | `_B` | `cmp al, '9'` | 0x37 | CF=1 | 0x37 < 0x39 |
+| 12 | `_B` | `ja _E` | 0x37 | CF=1 | Не переходит |
+| 13 | `_B` | `jmp _C` | 0x37 | — | **Переход B → C** |
+| 14 | `_C` | `call getsymbol` | 0x37 | — | Читаем след. символ |
+| 15 | `_C` | (after call) | 0x0A ('\n') | — | Прочитан newline |
+| 16 | `_C` | `test al, al` | 0x0A | ZF=0 | 0x0A ≠ 0 |
+| 17 | `_C` | `jz _D` | 0x0A | ZF=0 | Не переходит |
+| 18 | `_C` | `cmp al, 10` | 0x0A | ZF=1 | 0x0A == 10 |
+| 19 | `_C` | `jz _D` | 0x0A | ZF=1 | **Переход C → D** |
+| 20 | `_D` | `mov rdi, ok_msg` | — | — | Подготовка вывода |
+| 21 | `_D` | `call print_string` | — | — | Вывод "Okay\n" |
+| 22 | — | `call exit` | — | — | exit(0) |
+
+**Результат:** программа выводит "Okay" и завершается с кодом 0 ✓
+
+### Визуализация переходов для "-7"
+
+```mermaid
+graph LR
+ Start([Запуск]) --> A
+ A[Состояние A
читает '-'] --> B[Состояние B
читает '7']
+ B --> C[Состояние C
читает newline]
+ C --> D[Состояние D
выводит Okay]
+ D --> End([exit 0])
+
+ style D fill:#51cf66
+ style End fill:#51cf66
+```
+
+---
+
+## 10. Типичные ошибки при работе со стеком и функциями
+
+### Ошибка 1: Неправильное выравнивание стека
+
+**Проблема:** забыли выровнять стек перед `call`, что приводит к SEGFAULT в функциях, использующих SSE.
+
+**Неправильный код:**
+```nasm
+my_function:
+ push rbp ; rsp % 16 == 0 после этого
+ mov rbp, rsp
+ sub rsp, 24 ; 24 % 16 = 8 → rsp % 16 == 8 (НЕВЫРОВНЕН!)
+
+ ; Передать параметры
+ mov rdi, format_str
+ mov rsi, 42
+ call printf ; ❌ SEGFAULT! printf ожидает выровненный стек
+
+ add rsp, 24
+ pop rbp
+ ret
+```
+
+**Правильный код:**
+```nasm
+my_function:
+ push rbp ; rsp % 16 == 0
+ mov rbp, rsp
+ sub rsp, 32 ; 32 % 16 == 0 → rsp % 16 == 0 (ВЫРОВНЕН!)
+
+ mov rdi, format_str
+ mov rsi, 42
+ call printf ; ✓ Работает корректно
+
+ add rsp, 32
+ pop rbp
+ ret
+```
+
+**Альтернатива с padding:**
+```nasm
+my_function:
+ push rbp ; rsp % 16 == 0
+ mov rbp, rsp
+ sub rsp, 24 ; Выделить 24 байта локальных
+ sub rsp, 8 ; Добавить 8 байт padding → итого 32
+ ; Теперь rsp % 16 == 0
+
+ call printf ; ✓ OK
+
+ add rsp, 32 ; Освободить всё (24 + 8)
+ pop rbp
+ ret
+```
+
+### Ошибка 2: Не сохранили callee-saved регистры
+
+**Проблема:** использовали rbx/r12-r15 без сохранения, вызывающая функция получила испорченные данные.
+
+**Неправильный код:**
+```nasm
+calculate:
+ ; Вызывающая функция полагается на rbx
+ mov rbx, rdi ; ❌ Испортили rbx!
+ add rbx, 10
+ mov rax, rbx
+ ret ; Вернулись, но rbx испорчен
+```
+
+**Правильный код:**
+```nasm
+calculate:
+ push rbx ; Сохранить rbx
+
+ mov rbx, rdi
+ add rbx, 10
+ mov rax, rbx
+
+ pop rbx ; Восстановить rbx
+ ret ; ✓ Теперь вызывающая функция видит исходный rbx
+```
+
+### Ошибка 3: Дисбаланс стека (push/pop не совпадают)
+
+**Проблема:** количество `push` не равно количеству `pop`, стек смещён при `ret`.
+
+**Неправильный код:**
+```nasm
+broken_function:
+ push rbx
+ push r12
+ push r13 ; Сохранили 3 регистра
+
+ ; ... тело функции ...
+
+ pop r13
+ pop r12 ; ❌ Забыли pop rbx!
+ ret ; Возврат по неправильному адресу → CRASH
+```
+
+**Правильный код:**
+```nasm
+correct_function:
+ push rbx
+ push r12
+ push r13
+
+ ; ... тело функции ...
+
+ pop r13
+ pop r12
+ pop rbx ; ✓ Все 3 регистра восстановлены
+ ret
+```
+
+**Диагностика в GDB:**
+```bash
+(gdb) break broken_function
+(gdb) run
+(gdb) info registers rsp
+# Запомнить значение rsp
+
+(gdb) stepi # Пошагово до ret
+(gdb) info registers rsp
+# Если значение отличается на неожиданную величину → дисбаланс!
+```
+
+### Ошибка 4: Неправильный порядок pop
+
+**Проблема:** `pop` в обратном порядке по отношению к `push`, регистры получают неправильные значения.
+
+**Неправильный код:**
+```nasm
+wrong_order:
+ push rbx ; Стек: [rbx]
+ push r12 ; Стек: [r12, rbx]
+ push r13 ; Стек: [r13, r12, rbx]
+
+ ; ...
+
+ pop rbx ; ❌ rbx получит значение r13!
+ pop r12 ; r12 получит значение r12 (случайно правильно)
+ pop r13 ; r13 получит значение rbx
+ ret
+```
+
+**Правильный код:**
+```nasm
+correct_order:
+ push rbx ; Стек: [rbx]
+ push r12 ; Стек: [r12, rbx]
+ push r13 ; Стек: [r13, r12, rbx]
+
+ ; ...
+
+ pop r13 ; ✓ r13 восстановлен
+ pop r12 ; ✓ r12 восстановлен
+ pop rbx ; ✓ rbx восстановлен
+ ret
+```
+
+**Мнемоника:** LIFO (Last In, First Out) — что последним положили, то первым достаём.
+
+### Ошибка 5: Использование caller-saved регистра без учёта его изменения
+
+**Проблема:** положились на сохранение значения в r10/r11 через вызов функции.
+
+**Неправильный код:**
+```nasm
+buggy_code:
+ mov r10, 100 ; Сохранили значение в r10
+ mov rdi, 42
+ call some_function ; some_function может испортить r10!
+
+ add rax, r10 ; ❌ r10 может содержать мусор!
+ ret
+```
+
+**Правильный код (вариант 1: использовать callee-saved):**
+```nasm
+fixed_code_v1:
+ push rbx ; Сохранить rbx
+ mov rbx, 100 ; Использовать rbx вместо r10
+ mov rdi, 42
+ call some_function ; rbx НЕ изменится
+
+ add rax, rbx ; ✓ rbx содержит 100
+ pop rbx
+ ret
+```
+
+**Правильный код (вариант 2: сохранить на стеке):**
+```nasm
+fixed_code_v2:
+ mov r10, 100
+ push r10 ; Сохранить r10 на стеке
+
+ mov rdi, 42
+ call some_function
+
+ pop r10 ; Восстановить r10
+ add rax, r10 ; ✓ r10 содержит 100
+ ret
+```
+
+### Ошибка 6: Неправильное использование локальных переменных
+
+**Проблема:** забыли выделить место на стеке для локальных переменных или обращаемся по неправильному смещению.
+
+**Неправильный код:**
+```nasm
+bad_locals:
+ push rbp
+ mov rbp, rsp
+ ; ❌ Забыли sub rsp, N!
+
+ mov qword [rbp - 8], 42 ; Перезаписываем старый rbp!
+ mov rax, [rbp - 8]
+
+ pop rbp
+ ret
+```
+
+**Правильный код:**
+```nasm
+good_locals:
+ push rbp
+ mov rbp, rsp
+ sub rsp, 16 ; ✓ Выделили 16 байт для локальных
+
+ mov qword [rbp - 8], 42 ; Локальная переменная 1
+ mov qword [rbp - 16], 100 ; Локальная переменная 2
+
+ mov rax, [rbp - 8]
+ add rax, [rbp - 16]
+
+ mov rsp, rbp ; Освободить локальные
+ pop rbp
+ ret
+```
+
+### Ошибка 7: Неполный FSM (отсутствующие переходы)
+
+**Проблема:** автомат не обрабатывает все возможные входные символы, программа падает или зависает.
+
+**Неправильный код:**
+```nasm
+incomplete_fsm:
+ _A:
+ call getsymbol
+ cmp al, '0'
+ jb _C ; Если < '0'
+ cmp al, '9'
+ ja _C ; Если > '9'
+ jmp _B ; Иначе цифра
+
+ _B:
+ ; ❌ Что дальше? Нет обработки следующего символа!
+ ret ; Немедленный возврат?
+```
+
+**Правильный код:**
+```nasm
+complete_fsm:
+ _A:
+ call getsymbol
+ test al, al ; ✓ Проверить EOF
+ jz _ERROR
+
+ cmp al, '0'
+ jb _ERROR
+ cmp al, '9'
+ ja _ERROR
+ jmp _B
+
+ _B:
+ call getsymbol ; ✓ Продолжить чтение
+ test al, al
+ jz _ACCEPT ; EOF после цифры = OK
+
+ cmp al, '0'
+ jb _ERROR
+ cmp al, '9'
+ ja _ERROR
+ jmp _B ; Петля для нескольких цифр
+
+ _ACCEPT:
+ mov rax, 1
+ ret
+
+ _ERROR:
+ xor rax, rax
+ ret
+```
+
+### Ошибка 8: Неправильный выбор условного перехода (signed vs unsigned)
+
+**Проблема:** использовали `jl`/`jg` для беззнаковых чисел или `jb`/`ja` для знаковых.
+
+**Неправильный код:**
+```nasm
+; Проверить, что ASCII код в диапазоне [0x30, 0x39]
+check_digit_wrong:
+ cmp al, 0x30
+ jl .not_digit ; ❌ jl для знакового сравнения!
+ cmp al, 0x39 ; ASCII коды беззнаковые
+ jg .not_digit ; ❌ jg для знакового сравнения!
+ ; ...
+```
+
+**Правильный код:**
+```nasm
+; ASCII коды — беззнаковые значения
+check_digit_correct:
+ cmp al, 0x30
+ jb .not_digit ; ✓ jb (below) для unsigned
+ cmp al, 0x39
+ ja .not_digit ; ✓ ja (above) для unsigned
+ ; ...
+```
+
+**Таблица выбора условных переходов:**
+
+| Тип данных | "Меньше" | "Меньше/равно" | "Больше" | "Больше/равно" |
+|------------|----------|----------------|----------|----------------|
+| Unsigned | `jb` | `jbe` | `ja` | `jae` |
+| Signed | `jl` | `jle` | `jg` | `jge` |
+
+**Пример ошибки:** `-1` (0xFF) как unsigned = 255, как signed = -1. Сравнение с 10:
+- `cmp al, 10; jb` → 255 > 10, не переходит (неправильно, если ожидали signed)
+- `cmp al, 10; jl` → -1 < 10, переходит (правильно для signed)
+
+---
+
+## 11. Практические задания
+
+### Задание 1: Функция с локальными переменными
+
+Напишите функцию `sum_array`, которая:
+- Принимает указатель на массив (rdi) и длину (rsi)
+- Создаёт локальную переменную-счётчик на стеке
+- Возвращает сумму элементов в rax
+- Корректно выравнивает стек
+
+**Требования:**
+- Использовать фрейм стека (rbp)
+- Сохранить callee-saved регистры при необходимости
+- Правильное выравнивание для возможных вложенных вызовов
+
+### Задание 2: FSM для проверки email
+
+Реализуйте конечный автомат для упрощённой проверки email формата: `[a-z]+@[a-z]+\.[a-z]+`
+
+**Состояния:**
+- A: начало, ожидаем букву (локальная часть)
+- B: читаем локальную часть
+- C: прочитан @, ожидаем букву (домен)
+- D: читаем домен
+- E: прочитана точка, ожидаем букву (TLD)
+- F: читаем TLD (принимающее состояние)
+- ERR: ошибка
+
+**Пример входов:**
+- `"user@example.com"` → принять
+- `"test@domain.org"` → принять
+- `"invalid"` → отклонить
+- `"@example.com"` → отклонить
+- `"user@.com"` → отклонить
+
+### Задание 3: Рекурсивная функция факториала
+
+Реализуйте рекурсивную функцию `factorial(n)`:
+
+```nasm
+; factorial(n) = n * factorial(n-1), factorial(0) = 1
+; Вход: rdi = n
+; Выход: rax = n!
+
+factorial:
+ ; TODO: реализовать с правильным управлением стеком
+```
+
+**Требования:**
+- Обработать базовый случай (n = 0)
+- Сохранить rdi перед рекурсивным вызовом (caller-saved!)
+- Правильно восстановить стек
+- Проверить для n = 5 (должно быть 120)
+
+### Задание 4: FSM с подсчётом
+
+Напишите автомат, который:
+- Читает строку из цифр
+- Подсчитывает количество нулей
+- Возвращает count в rax
+- Использует локальную переменную-счётчик на стеке
+
+**Пример:**
+- `"12003"` → возвращает 2
+- `"999"` → возвращает 0
+- `"00000"` → возвращает 5
+
+### Задание 5: Отладка с GDB
+
+Дан код с ошибкой:
+
+```nasm
+mystery_function:
+ push rbp
+ mov rbp, rsp
+ push rbx
+ push r12
+
+ mov rbx, rdi
+ mov r12, rsi
+
+ ; Какие-то вычисления...
+ add rbx, r12
+ mov rax, rbx
+
+ pop rbx ; ❌ Ошибка здесь!
+ pop r12
+ pop rbp
+ ret
+```
+
+**Задания:**
+1. Найдите ошибку визуально
+2. Проверьте с GDB, какие регистры испорчены
+3. Исправьте код
+4. Убедитесь, что все callee-saved регистры восстановлены правильно
+
+---
+
+## 12. Справочная информация
+
+### Calling Convention System V AMD64 ABI (краткая памятка)
+
+**Передача аргументов:**
+| Аргумент | Регистр | Тип |
+|----------|---------|-----|
+| 1 | rdi | integer/pointer |
+| 2 | rsi | integer/pointer |
+| 3 | rdx | integer/pointer |
+| 4 | rcx | integer/pointer |
+| 5 | r8 | integer/pointer |
+| 6 | r9 | integer/pointer |
+| 7+ | стек | push в обратном порядке |
+
+**Возвращаемое значение:** rax (или rax:rdx для 128-бит)
+
+**Caller-saved (может быть испорчен вызываемой функцией):**
+- rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11
+
+**Callee-saved (должен быть сохранён вызываемой функцией):**
+- rbx, rbp, r12, r13, r14, r15
+- rsp (указатель стека)
+
+**Выравнивание:** `rsp % 16 == 0` перед инструкцией `call`
+
+### Инструкции управления стеком
+
+| Инструкция | Описание | Эффект на rsp |
+|------------|----------|---------------|
+| `push reg` | Сохранить регистр на стеке | rsp -= 8 |
+| `pop reg` | Восстановить регистр со стека | rsp += 8 |
+| `call addr` | Вызвать функцию (push rip, jmp addr) | rsp -= 8 |
+| `ret` | Вернуться (pop rip) | rsp += 8 |
+| `leave` | Эквивалент `mov rsp, rbp; pop rbp` | rsp = rbp, затем rsp += 8 |
+| `enter N, 0` | Создать фрейм стека (медленно, не рекомендуется) | |
+
+### Условные переходы для FSM
+
+**После `cmp op1, op2`:**
+
+| Условие | Unsigned | Signed | Флаги |
+|---------|----------|--------|-------|
+| Равно | `je` / `jz` | `je` / `jz` | ZF=1 |
+| Не равно | `jne` / `jnz` | `jne` / `jnz` | ZF=0 |
+| op1 < op2 | `jb` | `jl` | CF=1 (unsigned) / SF≠OF (signed) |
+| op1 ≤ op2 | `jbe` | `jle` | CF=1 or ZF=1 / ZF=1 or SF≠OF |
+| op1 > op2 | `ja` | `jg` | CF=0 and ZF=0 / ZF=0 and SF=OF |
+| op1 ≥ op2 | `jae` | `jge` | CF=0 / SF=OF |
+
+**Мнемоника:**
+- **B**elow / **A**bove — для unsigned
+- **L**ess / **G**reater — для signed
+- **E**qual — для любых
+
+### Системные вызовы (частичный список)
+
+| Номер | Имя | rdi | rsi | rdx | Описание |
+|-------|-----|-----|-----|-----|----------|
+| 0 | read | fd | buf* | count | Читать из файла |
+| 1 | write | fd | buf* | count | Писать в файл |
+| 60 | exit | code | — | — | Завершить процесс |
+
+**Использование:**
+```nasm
+mov rax, syscall_number
+mov rdi, arg1
+mov rsi, arg2
+mov rdx, arg3
+syscall
+; Результат в rax
+```
+
+### Отладка с GDB
+
+**Основные команды:**
+
+```bash
+# Запуск
+gdb ./program
+(gdb) break _start # Точка останова
+(gdb) run # Запустить
+
+# Пошаговое выполнение
+(gdb) stepi # Выполнить одну инструкцию
+(gdb) nexti # Следующая инструкция (не заходить в call)
+(gdb) continue # Продолжить до следующей точки останова
+
+# Просмотр
+(gdb) info registers # Все регистры
+(gdb) info registers rax rbx rsp # Конкретные регистры
+(gdb) x/10gx $rsp # 10 qword в памяти от rsp (стек)
+(gdb) x/s $rdi # Строка по адресу в rdi
+(gdb) disassemble # Дизассемблировать текущую функцию
+
+# Точки останова
+(gdb) break _A # Остановиться на метке _A
+(gdb) break *0x401234 # Остановиться на адресе
+(gdb) delete 1 # Удалить точку останова №1
+
+# Проверка выравнивания
+(gdb) p $rsp % 16 # Должно быть 0 перед call
+```
+
+### Полезные макросы для NASM
+
+```nasm
+; Сохранить все callee-saved регистры
+%macro SAVE_CALLEE 0
+ push rbx
+ push r12
+ push r13
+ push r14
+ push r15
+%endmacro
+
+; Восстановить все callee-saved регистры
+%macro RESTORE_CALLEE 0
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop rbx
+%endmacro
+
+; Стандартный пролог функции
+%macro FUNC_PROLOGUE 1
+ push rbp
+ mov rbp, rsp
+ sub rsp, %1 ; %1 = размер локальных переменных
+%endmacro
+
+; Стандартный эпилог функции
+%macro FUNC_EPILOGUE 0
+ mov rsp, rbp
+ pop rbp
+ ret
+%endmacro
+```
+
+**Использование:**
+```nasm
+my_function:
+ FUNC_PROLOGUE 32 ; Выделить 32 байта локальных
+ SAVE_CALLEE ; Сохранить регистры
+
+ ; Тело функции
+
+ RESTORE_CALLEE
+ FUNC_EPILOGUE
+```
+
+### Ссылки на документацию
+
+- [System V AMD64 ABI](https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf) — официальное соглашение о вызовах
+- [Intel Software Developer Manual](https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html) — полное описание инструкций x86-64
+- [Linux System Call Table](https://filippo.io/linux-syscall-table/) — таблица системных вызовов
+- [NASM Documentation](https://www.nasm.us/doc/) — документация по ассемблеру NASM
+
+---
+
+**Конец методички для семинара 2**
diff --git a/ЯПы/ЯПы.md b/ЯПы/ЯПы.md
new file mode 100644
index 0000000..1df9e10
--- /dev/null
+++ b/ЯПы/ЯПы.md
@@ -0,0 +1,6 @@
+
+## Что это?
+Методички по семенарам (разжёванная теория для самых маленьких + доп материалы)
+
+[[Семинар-1]] - Окружение, инструменты, первая программа
+[[Семинар-2]] - Функции, стек, конечные автоматы
\ No newline at end of file