Книга RU
Единая книга: CPU + FASM (RU)
Эта книга объединяет материалы по архитектуре процессора, ассемблеру и полный FASM-справочный блок.
Оглавление
- Часть 1: Основная книга (CPU + ASM, 12 глав)
- Глава 1: Понимание работы компьютеров
- Глава 2: Основы CPU
- Глава 3: Введение в ассемблер
- Глава 4: Управление потоком в ассемблере
- Глава 5: От машинного кода к языкам высокого уровня
- Глава 6: Понимание выполнения программ
- Глава 7: Введение в Lisp
- Глава 8: Концепции функционального программирования
- Глава 9: Основы интерпретатора
- Глава 10: Продвинутые возможности интерпретатора
- Глава 11: Сопрограммы и современное программирование
- Глава 12: Объединяя всё вместе
- Часть 2: FASM Reference Guide
- Часть 3: Каталог примеров
- Часть 4: Полный Reference Guide
- Часть 5: AI FASM Rules
- Часть 6: Карта репозитория
Часть 1: Основная книга (CPU + ASM, 12 глав)
Создание компьютера с нуля
Путешествие через вычисления, ассемблер и Lisp
Образовательное руководство, основанное на практической реализации
Содержание
Часть 1: Основы вычислений
- Глава 1: Понимание работы компьютеров
- Глава 2: Основы CPU
Часть 2: Язык ассемблера - Первый шаг
- Глава 3: Введение в ассемблер
- Глава 4: Управление потоком в ассемблере
Часть 3: Движение вверх - Концепции языков программирования
- Глава 5: От машинного кода к языкам высокого уровня
- Глава 6: Понимание выполнения программ
Часть 4: Lisp - Красивая абстракция
- Глава 7: Введение в Lisp
- Глава 8: Концепции функционального программирования
Часть 5: Создание интерпретатора
- Глава 9: Основы интерпретатора
- Глава 10: Продвинутые возможности интерпретатора
Часть 6: Продвинутые концепции
- Глава 11: Сопрограммы и современное программирование
- Глава 12: Объединяя всё вместе
Глава 1: Понимание работы компьютеров
Цели обучения
К концу этой главы вы:
- Поймете фундаментальные компоненты компьютера
- Освоите представление данных в двоичной системе
- Изучите основы машинного кода
- Поймете организацию памяти и концепции обработки
1.1 Базовая архитектура компьютера
В своей основе компьютер - это машина, которая обрабатывает информацию. Эта обработка происходит через взаимодействие нескольких ключевых компонентов:
1.1.1 Основные компоненты
- Центральный процессор (CPU)
- “Мозг” компьютера
- Выполняет инструкции
- Содержит регистры для временного хранения данных
- Выполняет арифметические и логические операции
- Память (RAM)
- Временное хранилище для программ и данных
- Организована в адресуемые ячейки
- Прямое взаимодействие с CPU
- Энергозависимая память (очищается при выключении)
- Ввод/Вывод (I/O)
- Взаимодействие с внешним миром
- Обработка ввода и вывода данных
- Управление коммуникацией устройств
1.2 Двоичная система и представление данных
1.2.1 Понимание двоичной системы
Двоичная система - это фундаментальный язык компьютеров. Давайте разберем почему и как она работает:
- Основы двоичной системы
- Использует только 0 и 1
- Каждая цифра называется “бит”
- 8 бит = 1 байт
1.2.2 Представление чисел
Десятичное: 42
Двоичное: 00101010
Шестнадцатеричное: 2A
1.2.3 Представление текста (ASCII)
'A' = 01000001
'B' = 01000010
'C' = 01000011
1.3 Введение в машинный код
Машинный код - это низший уровень программирования, который выполняется непосредственно процессором. Рассмотрим простой пример:
; Представление машинного кода
10110000 ; Загрузить значение в регистр
00101010 ; Значение 42
00000001 ; Сохранить в памяти
1.4 Память и обработка
1.4.1 Организация памяти
- Память организована линейно
- Каждая ячейка имеет уникальный адрес
- Данные сохраняются и извлекаются с использованием этих адресов
1.4.2 Базовый цикл обработки
- Получение инструкции из памяти
- Декодирование инструкции
- Выполнение инструкции
- Сохранение результата
- Повторение
Практические упражнения
- Преобразование в двоичную систему
- Преобразуйте следующие числа в двоичную систему:
- 15
- 127
- 256
- Преобразуйте следующие числа в двоичную систему:
- Адресация памяти
- Если у вас 8 бит для адресации, сколько уникальных ячеек памяти вы можете адресовать?
- Сколько памяти вы можете адресовать с помощью 16 бит?
- Базовый машинный код
- Напишите двоичное представление для:
- Загрузки значения в регистр
- Сложения двух чисел
- Сохранения результата в памяти
- Напишите двоичное представление для:
Контрольные вопросы
- Каковы основные компоненты компьютерной системы?
- Как данные представляются в двоичной системе?
- Какова связь между CPU и памятью?
- Как работает базовый цикл инструкций?
Дополнительное чтение
- Организация компьютера и дизайн (Patterson & Hennessy)
- Код: Скрытый язык компьютерного оборудования и программного обеспечения (Charles Petzold)
[Продолжение следует…]
Глава 2: Основы CPU
Цели обучения
К концу этой главы вы:
- Поймете архитектуру CPU и его компоненты
- Изучите, как обрабатываются инструкции
- Реализуете базовый симулятор CPU
- Научитесь работать с регистрами и памятью
2.1 Углубленное изучение архитектуры CPU
2.1.1 Основные компоненты CPU
Рассмотрим нашу реализацию SimpleCPU для понимания основных компонентов:
# Из нашей реализации SimpleCPU
class SimpleCPU:
def __init__(self):
self.registers = {'A': 0, 'PC': 0} # Базовые регистры
self.memory = [0] * 256 # 256 байт памяти
Ключевые компоненты:
- Регистры
- Счетчик программы (PC): Отслеживает текущую инструкцию
- Аккумулятор (A): Основной регистр для вычислений
- Почему они важны для вычислений
- Интерфейс памяти
- Прямой доступ к памяти
- Размер памяти и ограничения
- Взаимодействие память-регистр
2.2 Обработка инструкций
2.2.1 Набор инструкций
Из нашей реализации:
# Базовый набор инструкций
instructions = {
"NOP": 0x00, # Нет операции
"LDA": 0x01, # Загрузить в аккумулятор
"ADD": 0x02, # Добавить к аккумулятору
"HLT": 0xFF, # Остановить выполнение
}
2.2.2 Цикл выполнения инструкций
def execute(self):
while True:
if self.registers['PC'] >= len(self.memory):
break
opcode = self.memory[self.registers['PC']]
# Обработка инструкции...
2.3 Создание симулятора CPU
2.3.1 Этапы реализации
- Настройка окружения
- Инициализация регистров
- Выделение памяти
- Определение базового набора инструкций
- Реализация инструкций
# Пример реализации инструкции elif opcode == 0x01: # LDA immediate self.registers['A'] = self.memory[self.registers['PC'] + 1] self.registers['PC'] += 2 - Выполнение программы
- Цикл получение-декодирование-выполнение
- Управление счетчиком программы
- Обработка различных типов инструкций
2.4 Работа с ассемблерным кодом
2.4.1 Преобразование ассемблера в машинный код
def assemble(self, assembly_code):
instructions = {
"NOP": 0x00,
"LDA": 0x01,
"ADD": 0x02,
"HLT": 0xFF,
}
# Процесс ассемблирования...
2.4.2 Запуск программ
Пример программы:
LDA #5 ; Загрузить 5 в аккумулятор
ADD #3 ; Добавить 3 к аккумулятору
HLT ; Остановить выполнение
Практические упражнения
- Улучшение симулятора CPU
- Добавьте инструкцию вычитания
- Реализуйте инструкцию умножения
- Добавьте новый регистр для временного хранения
- Программирование на ассемблере
- Напишите программу для сложения трех чисел
- Создайте программу, которая считает от 1 до 10
- Реализуйте простой цикл, используя новые инструкции
- Практика отладки
- Найдите ошибку в этом ассемблерном коде:
LDA #5 ADD #3 ADD #2 ; Отсутствует инструкция HLT
- Найдите ошибку в этом ассемблерном коде:
Контрольные вопросы
- Каковы основные компоненты нашего SimpleCPU?
- Как работает цикл выполнения инструкций?
- Почему нам нужны разные типы регистров?
- Как ассемблерный код преобразуется в машинный код?
Задачи программирования
- Расширьте SimpleCPU, добавив:
- Больше регистров
- Дополнительные инструкции
- Условное ветвление
- Создайте программу, которая:
- Вычисляет числа Фибоначчи
- Реализует базовую арифметику
- Эффективно использует несколько регистров
Дополнительное чтение
- Архитектура компьютера: Количественный подход
- Внутри машины (Jon Stokes)
- Элементы вычислительных систем (Nisan & Schocken)
| ← Предыдущая глава | Следующая глава: Введение в ассемблер → |
Глава 3: Введение в ассемблер
Цели обучения
К концу этой главы вы:
- Поймете основы ассемблера x86_64
- Напишете свои первые программы на ассемблере
- Изучите системные вызовы и ввод/вывод
- Реализуете математические алгоритмы на ассемблере
3.1 Понимание ассемблера x86_64
3.1.1 Базовая структура
Рассмотрим нашу реализацию чисел Фибоначчи на ассемблере:
format ELF64 executable ; Указание формата вывода
entry main ; Точка входа в программу
; Определения системных вызовов
SYS_write equ 1 ; Номер системного вызова write
STDOUT equ 1 ; Файловый дескриптор стандартного вывода
3.1.2 Регистры и их назначение
Общие регистры в x86_64:
rax: Аккумулятор, номера системных вызововrdi,rsi,rdx: Параметры функций/системных вызововr8-r15: Регистры общего назначения
Из нашей реализации:
mov r10, a ; Сохранить первое число
mov r11, b ; Сохранить второе число
mov r12, 1 ; Счетчик
3.2 Системные вызовы и ввод/вывод
3.2.1 Выполнение системных вызовов
Наша реализация показывает чистую обработку системных вызовов:
macro syscall3 number, a, b, c
{
mov rax, number
mov rdi, a
mov rsi, b
mov rdx, c
syscall
}
3.2.2 Операции ввода/вывода
Пример из нашей функции печати:
print:
mov r9, -3689348814741910323
sub rsp, 40
mov BYTE [rsp+31], 10
lea rcx, [rsp+30]
3.3 Реализация алгоритмов
3.3.1 Последовательность Фибоначчи
Разберем нашу реализацию:
macro fib a, b {
mov r10, a ; Первое число
mov r11, b ; Второе число
mov r12, 1 ; Счетчик
_loop:
add r10, r11 ; Вычислить следующее число
mov r11, r10 ; Обновить последовательность
mov r10, r12 ; Обновить счетчик
inc r12
cmp r12, 5
jle _loop
}
Ключевые концепции:
- Реализация цикла
- Управление регистрами
- Арифметические операции
- Условное ветвление
3.3.2 Операции с памятью и стеком
sub rsp, 40 ; Выделить место в стеке
mov BYTE [rsp+31], 10 ; Сохранить перевод строки
lea rcx, [rsp+30] ; Загрузить эффективный адрес
3.4 Управляющие конструкции
3.4.1 Циклы
Из нашей реализации:
loop_start:
syscall3 SYS_write, STDOUT, greet_msg, greet_msg_len
inc r8
cmp r8, 10
jle loop_start
3.4.2 Условное выполнение
cmp r12, 5 ; Сравнить счетчик с 5
jle _loop ; Перейти, если меньше или равно
Практические упражнения
- Базовый ассемблер
- Напишите программу для сложения двух чисел
- Реализуйте программу-счетчик
- Создайте простой цикл
- Системные вызовы
- Напишите программу, которая выводит “Привет, мир!”
- Считайте пользовательский ввод и выведите его обратно
- Реализуйте операции с файлами
- Реализация алгоритмов
- Напишите программу для вычисления факториала
- Реализуйте простой алгоритм сортировки
- Создайте игру угадывания чисел
Задачи программирования
- Расширенный Фибоначчи
- Измените программу Фибоначчи, чтобы:
- Обрабатывать большие числа
- Печатать последовательность
- Позволять пользователю задавать длину последовательности
- Измените программу Фибоначчи, чтобы:
- Математические операции
- Реализуйте деление
- Добавьте операции с плавающей точкой
- Создайте простой калькулятор
Контрольные вопросы
- Каковы основные регистры в ассемблере x86_64?
- Как работают системные вызовы в ассемблере?
- Какова цель стека?
- Как реализуются циклы и условия?
Советы по отладке
- Распространенные проблемы
- Отслеживание значений регистров
- Выравнивание стека
- Параметры системных вызовов
- Инструменты отладки
- Использование GDB
- Проверка стека
- Исследование состояния регистров
Дополнительное чтение
- Профессиональное программирование на ассемблере (Richard Blum)
- Современное программирование на ассемблере x86 (Daniel Kusswurm)
- Системное программирование в Linux (Robert Love)
Подсказки к решениям упражнений главы 3
Решения для базового ассемблера
- Сложение двух чисел: ```nasm section .data num1 db 5 num2 db 3
section .text mov al, [num1] ; Загрузить первое число add al, [num2] ; Добавить второе число ; Результат в al
2. **Программа-счетчик**:
```nasm
mov rcx, 0 ; Инициализировать счетчик
count_loop:
inc rcx ; Увеличить
cmp rcx, 10 ; Сравнить с пределом
jl count_loop ; Цикл, если меньше
- Простой цикл с вводом/выводом:
mov r8, 0 ; Счетчик print_loop: ; Ваша процедура печати здесь inc r8 cmp r8, 5 jle print_loop
Решения для системных вызовов
- Привет, мир: ```nasm section .data msg db “Привет, мир!”, 10 len equ $ - msg
section .text syscall3 SYS_write, STDOUT, msg, len
---
[← Предыдущая глава](#глава-2) | [Следующая глава: Управление потоком в ассемблере →](#глава-4)
<a name="глава-4"></a>
# Глава 4: Управление потоком в ассемблере
## Цели обучения
К концу этой главы вы:
- Освоите сложные управляющие конструкции
- Реализуете условное выполнение
- Поймете вызовы функций и кадры стека
- Научитесь работать с циклами и ветвлением
## 4.1 Продвинутые управляющие конструкции
### 4.1.1 Условное ветвление
Из нашей реализации Фибоначчи:
```nasm
cmp r12, 5 ; Сравнить счетчик с пределом
jle _loop ; Перейти, если меньше или равно
jmp exit ; Перейти к выходу, если больше
Общие инструкции сравнения:
je/jz: Переход если равно/нольjne/jnz: Переход если не равно/не нольjg/jl: Переход если больше/меньшеjge/jle: Переход если больше или равно/меньше или равно
4.1.2 Сложное принятие решений
cmp rax, 0
je handle_zero ; Если ноль
jl handle_negative ; Если отрицательное
; Обработка положительного случая
handle_zero:
; Код обработки нуля
handle_negative:
; Код обработки отрицательного числа
4.2 Реализация функций
4.2.1 Управление кадром стека
my_function:
push rbp ; Сохранить старый указатель базы
mov rbp, rsp ; Установить новый указатель базы
sub rsp, 16 ; Выделить локальные переменные
; Тело функции
mov rsp, rbp ; Восстановить стек
pop rbp ; Восстановить указатель базы
ret ; Вернуться к вызывающему
4.2.2 Передача параметров
Соглашение о вызовах Linux x86_64:
; Порядок параметров функции:
; rdi, rsi, rdx, rcx, r8, r9
; Дополнительные параметры в стеке
call_example:
mov rdi, first_param
mov rsi, second_param
call my_function
4.3 Оптимизация циклов
4.3.1 Развертывание циклов
; До развертывания
mov rcx, 4
loop1:
add rax, [rdi]
add rdi, 8
dec rcx
jnz loop1
; После развертывания
add rax, [rdi]
add rax, [rdi+8]
add rax, [rdi+16]
add rax, [rdi+24]
4.3.2 Условия циклов
Из нашей реализации:
_loop:
add r10, r11 ; Основная операция
mov rdi, r11 ; Подготовка к печати
call print ; Печать числа
mov r11, r10 ; Обновить последовательность
inc r12 ; Увеличить счетчик
cmp r12, 5 ; Проверить условие
jle _loop ; Продолжить если нужно
4.4 Обработка ошибок
4.4.1 Проверка ошибок
; Проверка деления на ноль
cmp rdx, 0
je error_handler
; Проверка переполнения
jo overflow_handler
error_handler:
mov rdi, error_msg
call print_error
ret
4.4.2 Обработка ошибок системных вызовов
syscall
cmp rax, 0
jl handle_error ; Отрицательный возврат = ошибка
Практические упражнения
- Реализация управления потоком
- Создайте структуру switch-case
- Реализуйте вложенные условия if-else
- Постройте конечный автомат
- Упражнения с функциями
- Напишите рекурсивную функцию
- Реализуйте функцию с локальными переменными
- Создайте функцию, использующую несколько параметров
- Продвинутые циклы
- Реализуйте структуру вложенных циклов
- Создайте оптимизированный цикл подсчета
- Постройте цикл с несколькими условиями выхода
Задачи программирования
- Конечный автомат
- Реализуйте простой калькулятор
- Создайте парсер текста
- Постройте простой игровой цикл
- Продвинутые функции
- Рекурсивный Фибоначчи с управлением стеком
- Реализация таблицы указателей на функции
- Система обработки исключений
Контрольные вопросы
- Как реализовать сложную логику ветвления?
- Какова цель управления кадром стека?
- Как оптимизировать циклы в ассемблере?
- Каковы лучшие практики обработки ошибок?
Советы по отладке
- Отладка управления потоком
- Эффективное использование точек останова
- Отслеживание состояний регистров
- Мониторинг изменений стека
- Распространенные проблемы
- Бесконечные циклы
- Повреждение стека
- Неправильные условия перехода
Дополнительное чтение
- Низкоуровневое программирование (Igor Zhirkov)
- Ассемблер шаг за шагом (Jeff Duntemann)
- Продвинутое программирование на x86 (Ray Seyfarth)
Подсказки к решениям упражнений главы 4
Решения для управления потоком
- Структура switch-case: ```nasm section .data jump_table: dq case_0, case_1, case_2, case_3
section .text ; Предполагаем значение в rax cmp rax, 3 ; Проверка границ ja default_case jmp [jump_table + rax * 8]
case_0: ; Обработка случая 0 jmp end_switch case_1: ; Обработка случая 1 jmp end_switch case_2: ; Обработка случая 2 jmp end_switch case_3: ; Обработка случая 3 jmp end_switch default_case: ; Обработка по умолчанию end_switch:
2. **Вложенные if-else**:
```nasm
cmp rax, 10
jg greater_than_10
je equals_10
; Случай меньше 10
jmp end_if
greater_than_10:
cmp rax, 20
jg greater_than_20
; Между 10 и 20
jmp end_if
greater_than_20:
; Больше 20
end_if:
Решения для реализации функций
- Рекурсивный Фибоначчи:
fib: push rbp mov rbp, rsp cmp rdi, 2 ; n <= 2? jle base_case ; n-1 dec rdi call fib mov rbx, rax ; Сохранить fib(n-1) ; n-2 dec rdi call fib add rax, rbx ; fib(n-1) + fib(n-2) jmp fib_end base_case: mov rax, 1 fib_end: mov rsp, rbp pop rbp ret
Сценарии продвинутой отладки
- Отладка повреждения стека: ```nasm ; Проблема: Несбалансированный стек bad_function: push rbx push r12 ; … код … pop rbx ; Упс! Неправильный порядок pop r12 ret
; Решение: Поддерживать порядок стека good_function: push rbx push r12 ; … код … pop r12 ; Правильный порядок pop rbx ret
2. **Сохранение регистров**:
```nasm
; Проблема: Значения регистров потеряны
bad_code:
mov rax, [data]
call helper ; helper изменяет rax
add rax, 5 ; Неправильный результат!
; Решение: Сохранять регистры
good_code:
mov rax, [data]
push rax
call helper
pop rax
add rax, 5
Дополнительные методы оптимизации
4.5 Продвинутая оптимизация
4.5.1 Оптимизация использования регистров
; Плохое использование регистров
mov rax, [data]
mov rbx, rax
add rbx, 5
mov [result], rbx
; Оптимизированная версия
mov rax, [data]
add rax, 5
mov [result], rax
4.5.2 Оптимизация доступа к памяти
; Плохой доступ к памяти
mov al, [array]
inc al
mov [array], al
mov al, [array]
add al, 5
mov [array], al
; Оптимизированная версия
mov al, [array]
inc al
add al, 5
mov [array], al
4.5.3 Подсказки для предсказания переходов
; Подсказка вероятного пути
cmp rax, 0
jg likely_positive ; Процессор ожидает этот путь
; Обработка отрицательного случая
likely_positive:
; Обработка положительного случая
| ← Предыдущая глава | Следующая глава: От машинного кода к языкам высокого уровня → |
Глава 5: От машинного кода к языкам высокого уровня
Цели обучения
К концу этой главы вы:
- Поймете переход от ассемблера к языкам высокого уровня
- Изучите уровни абстракции языков
- Исследуете компиляцию и интерпретацию
- Реализуете базовые возможности языка
5.1 Эволюция языков программирования
5.1.1 От машинного кода к ассемблеру
; Машинный код
10110000 00000101 ; Загрузить 5
00000011 00000011 ; Добавить 3
; Эквивалент на ассемблере
mov al, 5
add al, 3
5.1.2 От ассемблера к конструкциям высокого уровня
# Эквивалент на языке высокого уровня
x = 5
x += 3
# Наша реализация SimpleCPU
def execute(self):
while True:
opcode = self.memory[self.registers['PC']]
if opcode == 0x01: # LDA
self.registers['A'] = self.memory[self.registers['PC'] + 1]
5.2 Уровни абстракции языка
5.2.1 Управление памятью
Низкий уровень:
section .data
buffer: resb 1024
section .text
mov rdi, buffer
mov rcx, 1024
call allocate
Высокий уровень:
buffer = bytearray(1024)
5.2.2 Управляющие конструкции
Ассемблер vs Высокий уровень:
; Цикл на ассемблере
mov rcx, 0
loop_start:
; Тело цикла
inc rcx
cmp rcx, 10
jl loop_start
# Эквивалент на Python
for i in range(10):
# Тело цикла
5.3 Создание возможностей языка
5.3.1 Переменные и типы
class Variable:
def __init__(self, name, type, value):
self.name = name
self.type = type
self.value = value
class Environment:
def __init__(self):
self.variables = {}
def define(self, name, type, value):
self.variables[name] = Variable(name, type, value)
5.3.2 Функции и процедуры
class Function:
def __init__(self, params, body, env):
self.params = params
self.body = body
self.env = env
def call(self, args):
local_env = Environment()
for param, arg in zip(self.params, args):
local_env.define(param.name, param.type, arg)
return evaluate(self.body, local_env)
Практические упражнения
- Реализация возможностей языка
- Реализуйте простую систему типов
- Создайте базовый механизм области видимости переменных
- Постройте систему вызова функций
- Перевод управляющих конструкций
- Преобразуйте циклы ассемблера в конструкции высокого уровня
- Реализуйте операторы if-else
- Создайте базовую систему обработки исключений
- Управление памятью
- Реализуйте простой сборщик мусора
- Создайте систему выделения памяти
- Постройте механизм подсчета ссылок
Контрольные вопросы
- Как языки высокого уровня абстрагируют машинные операции?
- Каковы компромиссы между ассемблером и языками высокого уровня?
- Как работает реализация системы типов?
- Какую роль играет управление памятью в дизайне языка?
Дополнительное чтение
- Прагматика языков программирования (Michael Scott)
- Создание интерпретаторов (Robert Nystrom)
- Типы и языки программирования (Benjamin Pierce)
Подсказки к решениям упражнений главы 5
Решения для реализации возможностей языка
- Простая система типов: ```python class Type: def init(self, name, size): self.name = name self.size = size # Размер в байтах
Базовые типы
INTEGER = Type(“int”, 4) FLOAT = Type(“float”, 8) CHAR = Type(“char”, 1)
class TypeChecker: def check_operation(self, op, left_type, right_type): if op in [’+’, ‘-‘, ‘*’, ‘/’]: if left_type == INTEGER and right_type == INTEGER: return INTEGER elif left_type == FLOAT or right_type == FLOAT: return FLOAT raise TypeError(f”Недопустимая операция {op} между {left_type.name} и {right_type.name}”)
2. **Механизм области видимости переменных**:
```python
class Scope:
def __init__(self, parent=None):
self.symbols = {}
self.parent = parent
def define(self, name, value):
self.symbols[name] = value
def resolve(self, name):
if name in self.symbols:
return self.symbols[name]
if self.parent:
return self.parent.resolve(name)
raise NameError(f"Имя '{name}' не определено")
# Пример использования
global_scope = Scope()
function_scope = Scope(global_scope)
block_scope = Scope(function_scope)
- Система вызова функций: ```python class CallFrame: def init(self, function, return_addr): self.local_vars = {} self.function = function self.return_addr = return_addr
class VirtualMachine: def init(self): self.call_stack = [] self.current_frame = None
def call_function(self, func, args):
frame = CallFrame(func, self.instruction_pointer)
self.call_stack.append(frame)
self.current_frame = frame
# Настройка параметров
for param, arg in zip(func.params, args):
frame.local_vars[param] = arg
# Выполнение тела функции
result = self.execute(func.body)
# Восстановление предыдущего кадра
self.call_stack.pop()
self.current_frame = self.call_stack[-1] if self.call_stack else None
return result ```
Дополнительные сравнения языков
- Условные операторы:
; If-else на ассемблере cmp eax, 0 jle is_negative ; код для положительного случая jmp end_if is_negative: ; код для отрицательного случая end_if:
# Эквивалент на Python
if x > 0:
# код для положительного случая
else:
# код для отрицательного случая
- Вызовы функций: ```nasm ; Функция на ассемблере calculate: push rbp mov rbp, rsp ; тело функции mov rsp, rbp pop rbp ret
; Место вызова call calculate
```python
# Эквивалент на Python
def calculate():
# тело функции
return result
# Место вызова
result = calculate()
- Операции с массивами:
; Доступ к массиву на ассемблере mov rax, [array + rdi * 8] ; array[i]
# Эквивалент на Python
value = array[i]
| ← Предыдущая глава | Следующая глава: Понимание выполнения программ → |
Глава 6: Понимание выполнения программ
Цели обучения
К концу этой главы вы:
- Поймете, как программы загружаются и выполняются
- Изучите организацию памяти и управление ею
- Освоите концепции процессов и потоков
- Реализуете базовые системы времени выполнения
6.1 Загрузка и выполнение программ
6.1.1 Процесс загрузки
class ProgramLoader:
def __init__(self):
self.memory = bytearray(1024 * 1024) # 1MB памяти
self.segments = {
'text': 0x0000,
'data': 0x4000,
'heap': 0x8000,
'stack': 0xF000
}
def load_program(self, code, data):
# Загрузка сегмента кода
self.load_segment(code, self.segments['text'])
# Загрузка сегмента данных
self.load_segment(data, self.segments['data'])
# Инициализация указателя стека
self.registers['rsp'] = self.segments['stack']
6.1.2 Организация памяти
Высокий адрес +------------------+
| Стек |
| ↓ |
| |
| ↑ |
| Куча |
| |
| Данные (BSS) |
| Данные (Data) |
| Текст (Код) |
Низкий адрес +------------------+
6.2 Управление памятью времени выполнения
6.2.1 Управление стеком
class StackFrame:
def __init__(self, return_addr, base_ptr):
self.local_vars = {}
self.saved_registers = {}
self.return_addr = return_addr
self.base_ptr = base_ptr
class Stack:
def __init__(self, size=1024*1024):
self.memory = bytearray(size)
self.sp = size # Стек растет вниз
def push(self, value):
self.sp -= 8 # 64-бит
struct.pack_into('Q', self.memory, self.sp, value)
def pop(self):
value = struct.unpack_from('Q', self.memory, self.sp)[0]
self.sp += 8
return value
6.2.2 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
6.3 Управление процессами и потоками
6.3.1 Блок управления процессом
class ProcessControlBlock:
def __init__(self, pid):
self.pid = pid
self.state = 'NEW' # NEW, READY, RUNNING, WAITING, TERMINATED
self.registers = {
'rax': 0, 'rbx': 0, 'rcx': 0, 'rdx': 0,
'rsp': 0, 'rbp': 0, 'rip': 0
}
self.memory_segments = {
'text': None,
'data': None,
'heap': None,
'stack': None
}
6.3.2 Реализация потоков
class Thread:
def __init__(self, tid, entry_point):
self.tid = tid
self.state = 'NEW'
self.stack = Stack()
self.registers = {
'rip': entry_point,
'rsp': self.stack.sp
}
self.context = None
def save_context(self):
self.context = self.registers.copy()
def restore_context(self):
self.registers = self.context.copy()
6.4 Системные вызовы и прерывания
6.4.1 Реализация системных вызовов
class SystemCallHandler:
def __init__(self):
self.handlers = {
1: self.sys_write,
2: self.sys_read,
3: self.sys_open,
4: self.sys_close
}
def handle_syscall(self, number, *args):
if number in self.handlers:
return self.handlers[number](*args)
raise Exception(f"Недопустимый системный вызов {number}")
def sys_write(self, fd, buffer, size):
# Реализация
pass
6.4.2 Обработка прерываний
class InterruptHandler:
def __init__(self):
self.interrupt_vector = [None] * 256
self.setup_handlers()
def setup_handlers(self):
self.interrupt_vector[0x80] = self.syscall_handler
self.interrupt_vector[0x0E] = self.page_fault_handler
def handle_interrupt(self, number, error_code=None):
handler = self.interrupt_vector[number]
if handler:
return handler(error_code)
raise Exception(f"Необработанное прерывание {number}")
Практические упражнения
- Реализация управления памятью
- Реализуйте простой распределитель памяти
- Создайте сборщик мусора
- Постройте систему подсчета ссылок
- Управление процессами
- Реализуйте базовый планировщик
- Создайте механизм создания процессов
- Постройте систему межпроцессного взаимодействия
- Реализация системных вызовов
- Создайте системные вызовы для файловой системы
- Реализуйте вызовы управления процессами
- Постройте вызовы управления памятью
Контрольные вопросы
- Как работает загрузка программ?
- Для чего используются различные сегменты памяти?
- Как системные вызовы переключаются между пользовательским и режимом ядра?
- Какова роль блока управления процессом?
Дополнительное чтение
- Операционные системы: Три простых части
- Разработка ядра Linux (Robert Love)
- Продвинутое программирование в среде UNIX
Подсказки к решениям упражнений главы 6
Решения для управления памятью
- Простой распределитель памяти: ```python class MemoryBlock: def init(self, start, size): self.start = start self.size = size self.is_free = True self.next = None
class Allocator: def init(self, memory_size): self.memory = bytearray(memory_size) self.head = MemoryBlock(0, memory_size)
def allocate(self, size):
current = self.head
while current:
if current.is_free and current.size >= size:
if current.size > size + 16: # Разделить если достаточно места
new_block = MemoryBlock(current.start + size,
current.size - size)
new_block.next = current.next
current.size = size
current.next = new_block
current.is_free = False
return current.start
current = current.next
return None # Нет свободной памяти ```
- Подсчет ссылок для сборки мусора:
class Object: def __init__(self): self.ref_count = 0 self.references = [] def inc_ref(self): self.ref_count += 1 def dec_ref(self): self.ref_count -= 1 if self.ref_count == 0: self.cleanup() def cleanup(self): for ref in self.references: ref.dec_ref() # Освободить память
Решения для управления процессами
- Базовый планировщик:
class Scheduler: def __init__(self): self.ready_queue = [] self.current_process = None self.quantum = 100 # Квант времени def schedule(self): if self.current_process: self.current_process.save_state() self.ready_queue.append(self.current_process) if self.ready_queue: self.current_process = self.ready_queue.pop(0) self.current_process.restore_state() self.current_process.run(self.quantum)
| ← Предыдущая глава | Следующая глава: Введение в Lisp → |
Глава 7: Введение в Lisp
Цели обучения
К концу этой главы вы:
- Поймете фундаментальные концепции Lisp
- Реализуете основные возможности Lisp
- Освоите принципы функционального программирования
- Построите интерпретатор Lisp
7.1 Понимание красоты Lisp
7.1.1 Код как данные
; В Lisp код является данными
(define factorial
(lambda (n)
(if (<= n 1)
1
(* n (factorial (- n 1))))))
; Наша реализация на Python
def parse_lisp(code):
def tokenize(code):
return code.replace('(', ' ( ').replace(')', ' ) ').split()
def read_from_tokens(tokens):
if not tokens:
raise SyntaxError("Неожиданный конец файла")
token = tokens.pop(0)
if token == '(':
L = []
while tokens[0] != ')':
L.append(read_from_tokens(tokens))
tokens.pop(0) # Удалить ')'
return L
elif token == ')':
raise SyntaxError("Неожиданная )")
else:
return atom(token)
7.1.2 Сила S-выражений
class Expression:
def __init__(self, operator, operands):
self.operator = operator
self.operands = operands
def evaluate(self, env):
fn = env.lookup(self.operator)
args = [operand.evaluate(env) for operand in self.operands]
return fn(*args)
7.2 Основные возможности Lisp
7.2.1 Лямбда-функции
; Лямбда в Lisp
(lambda (x y) (+ x y))
; Наша реализация
class Lambda:
def __init__(self, params, body, env):
self.params = params
self.body = body
self.env = env
def __call__(self, *args):
local_env = Environment(self.env)
for param, arg in zip(self.params, args):
local_env.define(param, arg)
return self.body.evaluate(local_env)
7.2.2 Обработка списков
class Cons:
def __init__(self, car, cdr):
self.car = car
self.cdr = cdr
def car(cons):
return cons.car
def cdr(cons):
return cons.cdr
def list_(*args):
result = None
for arg in reversed(args):
result = Cons(arg, result)
return result
7.3 Создание интерпретатора
7.3.1 Цикл вычисления
def eval(expr, env):
if isinstance(expr, str): # Ссылка на переменную
return env.lookup(expr)
elif not isinstance(expr, list): # Константа
return expr
elif expr[0] == 'quote': # Цитирование
return expr[1]
elif expr[0] == 'if': # Условие
(_, test, conseq, alt) = expr
return eval(conseq, env) if eval(test, env) else eval(alt, env)
elif expr[0] == 'define': # Определение
(_, var, exp) = expr
env.define(var, eval(exp, env))
elif expr[0] == 'lambda': # Процедура
(_, params, body) = expr
return Lambda(params, body, env)
else: # Вызов процедуры
proc = eval(expr[0], env)
args = [eval(arg, env) for arg in expr[1:]]
return proc(*args)
7.3.2 Окружение
class Environment:
def __init__(self, parent=None):
self.bindings = {}
self.parent = parent
def lookup(self, name):
if name in self.bindings:
return self.bindings[name]
if self.parent:
return self.parent.lookup(name)
raise NameError(f"Символ '{name}' не найден")
def define(self, name, value):
self.bindings[name] = value
7.4 Продвинутые возможности
7.4.1 Макросы
class Macro:
def __init__(self, transformer):
self.transformer = transformer
def expand(self, expr, env):
expanded = self.transformer(expr)
return eval(expanded, env)
def define_macro(name, transformer, env):
env.define(name, Macro(transformer))
7.4.2 Оптимизация хвостовой рекурсии
def eval_tco(expr, env):
while True:
if isinstance(expr, str):
return env.lookup(expr)
elif not isinstance(expr, list):
return expr
elif expr[0] == 'if':
(_, test, conseq, alt) = expr
expr = conseq if eval_tco(test, env) else alt
elif expr[0] == 'define':
(_, var, exp) = expr
env.define(var, eval_tco(exp, env))
return None
else:
proc = eval_tco(expr[0], env)
args = [eval_tco(arg, env) for arg in expr[1:]]
if isinstance(proc, Lambda):
expr = proc.body
env = Environment(proc.env)
for param, arg in zip(proc.params, args):
env.define(param, arg)
else:
return proc(*args)
Практические упражнения
- Реализация основных возможностей
- Реализуйте базовые арифметические операции
- Создайте функции манипуляции списками
- Постройте условные выражения
- Продвинутые возможности
- Реализуйте систему макросов
- Добавьте оптимизацию хвостовой рекурсии
- Создайте сборщик мусора
- Расширения языка
- Добавьте сопоставление с образцом
- Реализуйте ленивые вычисления
- Создайте систему модулей
Контрольные вопросы
- Что делает Lisp уникальным среди языков программирования?
- Как Lisp обрабатывает код как данные?
- Какова роль S-выражений?
- Как работают макросы в Lisp?
Дополнительное чтение
- Структура и интерпретация компьютерных программ
- On Lisp (Paul Graham)
- Let Over Lambda (Doug Hoyte)
Подсказки к решениям упражнений главы 7
Решения для реализации основных возможностей
- Базовые арифметические операции: ```lisp ; Реализация на Lisp (define (add x y) (+ x y)) (define (subtract x y) (- x y)) (define (multiply x y) (* x y)) (define (divide x y) (/ x y))
; Реализация на Python class LispPrimitive: def init(self, fn): self.fn = fn def call(self, args): return self.fn(args)
def setup_arithmetic(env): env.define(‘+’, LispPrimitive(lambda x, y: x + y)) env.define(‘-‘, LispPrimitive(lambda x, y: x - y)) env.define(‘*’, LispPrimitive(lambda x, y: x * y)) env.define(‘/’, LispPrimitive(lambda x, y: x / y))
2. **Манипуляции со списками**:
```lisp
; Реализация на Lisp
(define (map fn lst)
(if (null? lst)
'()
(cons (fn (car lst))
(map fn (cdr lst)))))
; Реализация на Python
def lisp_map(fn, lst):
if not lst:
return []
return Cons(fn(lst.car), lisp_map(fn, lst.cdr))
def setup_list_ops(env):
env.define('map', Lambda(['fn', 'lst'],
['if', ['null?', 'lst'],
[],
['cons',
['fn', ['car', 'lst']],
['map', 'fn', ['cdr', 'lst']]]]))
Примеры реального использования Lisp
- Парсер JSON: ```lisp (define (parse-json str) (let ((tokens (tokenize str))) (parse-json-object tokens)))
(define (parse-json-object tokens) (case (car tokens) (‘{‘ (parse-object (cdr tokens))) (‘[’ (parse-array (cdr tokens))) (else (parse-primitive tokens))))
; Реализация на Python class JsonParser: def init(self, env): self.env = env
def parse(self, tokens):
if tokens[0] == '{':
return self.parse_object(tokens[1:])
elif tokens[0] == '[':
return self.parse_array(tokens[1:])
return self.parse_primitive(tokens) ```
- Веб-роутер: ```lisp (define-macro (route path handler) `(add-route router ,path ,handler))
(route “/users/:id” (lambda (req) (let ((user-id (get-param req :id))) (find-user user-id))))
; Реализация на Python class Router: def init(self): self.routes = {}
def add_route(self, path, handler):
self.routes[path] = handler
def handle_request(self, path, request):
handler = self.routes.get(path)
if handler:
return handler(request)
raise NotFoundError(f"Маршрут не найден: {path}") ```
Реализация продвинутых возможностей Lisp
-
Сопоставление с образцом: ```python class Pattern: def init(self, type_pattern, value_pattern=None): self.type_pattern = type_pattern self.value_pattern = value_pattern
def match(self, value): if not isinstance(value, self.type_pattern): return None
if self.value_pattern is None: return {} if callable(self.value_pattern): return self.value_pattern(value) return {'value': value} if value == self.value_pattern else None
def define_pattern_macro(env): def match_macro(pattern, expr): return [‘let’, [Pattern(pattern).match(expr, env)], [‘if’, [‘null?’, ‘bindings’], ‘false’, ‘bindings’]] env.define(‘match’, Macro(match_macro))
2. **Ленивые вычисления**:
```python
class Promise:
def __init__(self, expr, env):
self.expr = expr
self.env = env
self.evaluated = False
self.value = None
def force(self):
if not self.evaluated:
self.value = eval(self.expr, self.env)
self.evaluated = True
return self.value
def define_lazy_primitives(env):
env.define('delay',
lambda expr: Promise(expr, env))
env.define('force',
lambda promise: promise.force())
| ← Предыдущая глава | Следующая глава: Концепции функционального программирования → |
Глава 8: Концепции функционального программирования
Цели обучения
К концу этой главы вы:
- Освоите парадигмы функционального программирования
- Поймете неизменяемость и чистые функции
- Реализуете функции высшего порядка
- Научитесь работать с монадами и функторами
8.1 Чистые функции и неизменяемость
8.1.1 Понимание чистых функций
# Нечистая функция
total = 0
def add_to_total(x):
global total
total += x
return total
# Чистая функция
def add_numbers(x, y):
return x + y
# Реализация в нашем Lisp
def is_pure_function(fn, env):
# Проверка, изменяет ли функция внешнее состояние
original_env = env.copy()
result1 = fn(1)
env.restore(original_env)
result2 = fn(1)
return result1 == result2
8.1.2 Неизменяемые структуры данных
class ImmutableList:
def __init__(self, items):
self._items = tuple(items)
def append(self, item):
return ImmutableList(self._items + (item,))
def remove(self, item):
return ImmutableList(x for x in self._items if x != item)
8.2 Функции высшего порядка
8.2.1 Map, Filter и Reduce
; Реализация map
(define (map fn lst)
(if (null? lst)
'()
(cons (fn (car lst))
(map fn (cdr lst)))))
; Реализация filter
(define (filter pred lst)
(cond ((null? lst) '())
((pred (car lst))
(cons (car lst)
(filter pred (cdr lst))))
(else (filter pred (cdr lst)))))
; Реализация reduce
(define (reduce fn init lst)
(if (null? lst)
init
(reduce fn
(fn init (car lst))
(cdr lst))))
8.2.2 Композиция функций
def compose(*fns):
def composed(x):
result = x
for fn in reversed(fns):
result = fn(result)
return result
return composed
# В нашем Lisp
(define (compose f g)
(lambda (x)
(f (g x))))
8.3 Продвинутые функциональные концепции
8.3.1 Каррирование
def curry(fn):
def curried(*args):
if len(args) >= fn.__code__.co_argcount:
return fn(*args)
return lambda *more: curried(*(args + more))
return curried
# Пример использования
@curry
def add(x, y, z):
return x + y + z
add_five = add(5)
add_five_and_ten = add_five(10)
result = add_five_and_ten(15) # 30
8.3.2 Монады
class Maybe:
def __init__(self, value):
self.value = value
def bind(self, fn):
if self.value is None:
return Maybe(None)
return fn(self.value)
@staticmethod
def unit(value):
return Maybe(value)
# Пример использования
def safe_divide(x, y):
return Maybe(x / y if y != 0 else None)
def safe_sqrt(x):
return Maybe(math.sqrt(x) if x >= 0 else None)
Практические упражнения
- Реализация чистых функций
- Создайте чистые версии распространенных функций
- Реализуйте неизменяемые структуры данных
- Напишите референциально прозрачный код
- Функции высшего порядка
- Реализуйте map, filter, reduce
- Создайте утилиты для композиции функций
- Постройте систему каррирования
- Монадические операции
- Реализуйте монаду Maybe
- Создайте монаду List
- Постройте монадические преобразования
Контрольные вопросы
- Что делает функцию чистой?
- Как функции высшего порядка улучшают повторное использование кода?
- Каковы преимущества неизменяемости?
- Как монады помогают управлять побочными эффектами?
Дополнительное чтение
- Введение в функциональное программирование (Bird & Wadler)
- Теория категорий для программистов (Bartosz Milewski)
- Функциональное программирование в Scala (Chiusano & Bjarnason)
Подсказки к решениям упражнений главы 8
Решения для реализации чистых функций
- Простая система типов: ```python class Type: def init(self, name, size): self.name = name self.size = size # Размер в байтах
Базовые типы
INTEGER = Type(“int”, 4) FLOAT = Type(“float”, 8) CHAR = Type(“char”, 1)
class TypeChecker: def check_operation(self, op, left_type, right_type): if op in [’+’, ‘-‘, ‘*’, ‘/’]: if left_type == INTEGER and right_type == INTEGER: return INTEGER elif left_type == FLOAT or right_type == FLOAT: return FLOAT raise TypeError(f”Недопустимая операция {op} между {left_type.name} и {right_type.name}”)
2. **Механизм области видимости переменных**:
```python
class Scope:
def __init__(self, parent=None):
self.symbols = {}
self.parent = parent
def define(self, name, value):
self.symbols[name] = value
def resolve(self, name):
if name in self.symbols:
return self.symbols[name]
if self.parent:
return self.parent.resolve(name)
raise NameError(f"Имя '{name}' не определено")
# Пример использования
global_scope = Scope()
function_scope = Scope(global_scope)
block_scope = Scope(function_scope)
- Система вызова функций: ```python class CallFrame: def init(self, function, return_addr): self.local_vars = {} self.function = function self.return_addr = return_addr
class VirtualMachine: def init(self): self.call_stack = [] self.current_frame = None
def call_function(self, func, args):
frame = CallFrame(func, self.instruction_pointer)
self.call_stack.append(frame)
self.current_frame = frame
# Настройка параметров
for param, arg in zip(func.params, args):
frame.local_vars[param] = arg
# Выполнение тела функции
result = self.execute(func.body)
# Восстановление предыдущего кадра
self.call_stack.pop()
self.current_frame = self.call_stack[-1] if self.call_stack else None
return result ```
Дополнительные сравнения языков
- Условные операторы:
; If-else на ассемблере cmp eax, 0 jle is_negative ; код для положительного случая jmp end_if is_negative: ; код для отрицательного случая end_if:
# Эквивалент на Python
if x > 0:
# код для положительного случая
else:
# код для отрицательного случая
- Вызовы функций: ```nasm ; Функция на ассемблере calculate: push rbp mov rbp, rsp ; тело функции mov rsp, rbp pop rbp ret
; Место вызова call calculate
```python
# Эквивалент на Python
def calculate():
# тело функции
return result
# Место вызова
result = calculate()
- Операции с массивами:
; Доступ к массиву на ассемблере mov rax, [array + rdi * 8] ; array[i]
# Эквивалент на Python
value = array[i]
| ← Предыдущая глава | Следующая глава: Основы интерпретатора → |
Глава 9: Основы интерпретатора
Цели обучения
К концу этой главы вы:
- Поймете основные компоненты интерпретатора
- Реализуете лексический анализатор и парсер
- Создадите систему вычисления выражений
- Научитесь управлять окружением и областями видимости
9.1 Архитектура интерпретатора
9.1.1 Основные компоненты
class Interpreter:
def __init__(self):
self.lexer = Lexer()
self.parser = Parser()
self.evaluator = Evaluator()
self.global_env = Environment()
def interpret(self, source):
tokens = self.lexer.tokenize(source)
ast = self.parser.parse(tokens)
return self.evaluator.evaluate(ast, self.global_env)
9.1.2 Поток данных
Исходный код → Лексер → Токены → Парсер → AST → Вычислитель → Результат
9.2 Лексический анализ
9.2.1 Реализация лексера
class Token:
def __init__(self, type, value):
self.type = type
self.value = value
class Lexer:
def __init__(self):
self.pos = 0
self.text = ""
self.current_char = None
def tokenize(self, text):
self.text = text
self.pos = 0
self.current_char = self.text[0] if text else None
tokens = []
while self.current_char:
if self.current_char.isspace():
self.advance()
continue
if self.current_char.isdigit():
tokens.append(self.number())
elif self.current_char.isalpha():
tokens.append(self.identifier())
elif self.current_char in '+-*/()':
tokens.append(Token('OPERATOR', self.current_char))
self.advance()
else:
raise SyntaxError(f"Неизвестный символ: {self.current_char}")
tokens.append(Token('EOF', None))
return tokens
9.3 Синтаксический анализ
9.3.1 Построение AST
class ASTNode:
pass
class BinaryOp(ASTNode):
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
class Number(ASTNode):
def __init__(self, value):
self.value = value
class Parser:
def __init__(self):
self.tokens = []
self.pos = 0
self.current_token = None
def parse(self, tokens):
self.tokens = tokens
self.pos = 0
self.current_token = self.tokens[0]
return self.expr()
def expr(self):
node = self.term()
while self.current_token.type == 'OPERATOR' and \
self.current_token.value in '+-':
op = self.current_token.value
self.advance()
right = self.term()
node = BinaryOp(node, op, right)
return node
9.4 Вычисление выражений
9.4.1 Реализация вычислителя
class Evaluator:
def evaluate(self, node, env):
method = f'eval_{type(node).__name__}'
return getattr(self, method)(node, env)
def eval_Number(self, node, env):
return node.value
def eval_BinaryOp(self, node, env):
left = self.evaluate(node.left, env)
right = self.evaluate(node.right, env)
if node.op == '+':
return left + right
elif node.op == '-':
return left - right
elif node.op == '*':
return left * right
elif node.op == '/':
return left / right
9.4.2 Управление окружением
class Environment:
def __init__(self, parent=None):
self.values = {}
self.parent = parent
def get(self, name):
if name in self.values:
return self.values[name]
if self.parent:
return self.parent.get(name)
raise NameError(f"Имя '{name}' не определено")
def set(self, name, value):
self.values[name] = value
9.5 Обработка ошибок
9.5.1 Типы ошибок
class InterpreterError(Exception):
pass
class LexerError(InterpreterError):
pass
class ParserError(InterpreterError):
pass
class RuntimeError(InterpreterError):
pass
9.5.2 Обработка ошибок
def safe_evaluate(self, source):
try:
return self.evaluate(source)
except LexerError as e:
return f"Ошибка лексического анализа: {e}"
except ParserError as e:
return f"Ошибка синтаксического анализа: {e}"
except RuntimeError as e:
return f"Ошибка времени выполнения: {e}"
Практические упражнения
- Расширение лексера
- Добавьте поддержку строковых литералов
- Реализуйте многострочные комментарии
- Добавьте поддержку шестнадцатеричных чисел
- Улучшение парсера
- Добавьте поддержку функций
- Реализуйте условные выражения
- Добавьте циклы и итерации
- Оптимизация вычислителя
- Реализуйте константные выражения
- Добавьте кэширование результатов
- Оптимизируйте рекурсивные вызовы
Контрольные вопросы
- Каковы основные компоненты интерпретатора?
- Как работает процесс лексического анализа?
- Какова роль AST в интерпретаторе?
- Как реализуется вычисление выражений?
Дополнительное чтение
- Создание языков программирования (Thorsten Ball)
- Компиляторы: принципы, технологии и инструменты
- Реализация языков программирования (Terrence Parr)
Подсказки к решениям упражнений главы 9
Решения для расширения лексера
- Поддержка строковых литералов:
def string(self): result = '' self.advance() # Пропустить открывающую кавычку while self.current_char and self.current_char != '"': if self.current_char == '\\': self.advance() if self.current_char == 'n': result += '\n' elif self.current_char == 't': result += '\t' else: result += self.current_char else: result += self.current_char self.advance() if not self.current_char: raise LexerError("Незакрытая строка") self.advance() # Пропустить закрывающую кавычку return Token('STRING', result) - Многострочные комментарии:
def skip_comment(self): while self.current_char and \ not (self.current_char == '*' and self.peek() == '/'): self.advance() self.advance() # Пропустить * self.advance() # Пропустить /
Решения для улучшения парсера
- Поддержка функций: ```python class FunctionNode(ASTNode): def init(self, params, body): self.params = params self.body = body
def parse_function(self): self.match(‘FUNCTION’) params = self.parse_parameters() body = self.parse_block() return FunctionNode(params, body)
2. **Условные выражения**:
```python
class IfNode(ASTNode):
def __init__(self, condition, then_branch, else_branch=None):
self.condition = condition
self.then_branch = then_branch
self.else_branch = else_branch
def parse_if(self):
self.match('IF')
condition = self.parse_expression()
then_branch = self.parse_block()
else_branch = None
if self.current_token.type == 'ELSE':
self.match('ELSE')
else_branch = self.parse_block()
return IfNode(condition, then_branch, else_branch)
| ← Предыдущая глава | Следующая глава: Продвинутые возможности интерпретатора → |
Глава 10: Продвинутые возможности интерпретатора
Цели обучения
К концу этой главы вы:
- Поймете расширенные возможности интерпретатора
- Изучите механизмы оптимизации и эффективности
- Реализуете расширенные возможности языка
- Научитесь работать с различными типами данных
10.1 Расширенные возможности интерпретатора
10.1.1 Оптимизация и эффективность
# Простая оптимизация
def optimize(expr):
if isinstance(expr, BinaryOp) and expr.op == '+':
return Number(expr.left.value + expr.right.value)
return expr
# Эффективное использование памяти
def efficient_memory_usage(expr):
if isinstance(expr, Number):
return Number(expr.value)
elif isinstance(expr, BinaryOp):
left = efficient_memory_usage(expr.left)
right = efficient_memory_usage(expr.right)
return BinaryOp(left, expr.op, right)
return expr
10.1.2 Расширенные типы данных
# Добавление новых типов данных
class Float(Number):
def __init__(self, value):
self.value = float(value)
class String(Number):
def __init__(self, value):
self.value = str(value)
# Обработка новых типов
def evaluate_float(expr):
if isinstance(expr, Float):
return expr.value
elif isinstance(expr, BinaryOp):
left = evaluate_float(expr.left)
right = evaluate_float(expr.right)
return BinaryOp(left, expr.op, right)
return expr
def evaluate_string(expr):
if isinstance(expr, String):
return expr.value
elif isinstance(expr, BinaryOp):
left = evaluate_string(expr.left)
right = evaluate_string(expr.right)
return BinaryOp(left, expr.op, right)
return expr
10.1.3 Расширенные возможности языка
# Добавление новых возможностей
def add_new_features(expr):
if isinstance(expr, BinaryOp):
if expr.op == '+':
return BinaryOp(expr.left, '+', expr.right)
elif expr.op == '-':
return BinaryOp(expr.left, '-', expr.right)
return expr
10.2 Практические упражнения
- Оптимизация вычислений
- Реализуйте простую оптимизацию
- Добавьте эффективное использование памяти
- Создайте расширенные типы данных
- Расширенные возможности языка
- Добавьте новые возможности
- Реализуйте расширенные типы данных
- Постройте систему поддержки различных типов
- Улучшение интерпретатора
- Добавьте поддержку многострочных комментариев
- Реализуйте поддержку шестнадцатеричных чисел
- Добавьте поддержку строковых литералов
Контрольные вопросы
- Как расширить возможности интерпретатора?
- Какие механизмы оптимизации можно использовать?
- Как добавить новые возможности языку?
- Как работает расширенная система типов?
Дополнительное чтение
- Оптимизация программирования (Donald Knuth)
- Эффективное программирование (Brian W. Kernighan)
- Современные подходы к программированию (Martin Odersky)
Подсказки к решениям упражнений главы 10
Решения для оптимизации
- Простая оптимизация:
```python
Простая оптимизация
def optimize(expr): if isinstance(expr, BinaryOp) and expr.op == ‘+’: return Number(expr.left.value + expr.right.value) return expr
Эффективное использование памяти
def efficient_memory_usage(expr): if isinstance(expr, Number): return Number(expr.value) elif isinstance(expr, BinaryOp): left = efficient_memory_usage(expr.left) right = efficient_memory_usage(expr.right) return BinaryOp(left, expr.op, right) return expr
2. **Эффективное использование памяти**:
```python
# Эффективное использование памяти
def efficient_memory_usage(expr):
if isinstance(expr, Number):
return Number(expr.value)
elif isinstance(expr, BinaryOp):
left = efficient_memory_usage(expr.left)
right = efficient_memory_usage(expr.right)
return BinaryOp(left, expr.op, right)
return expr
Решения для расширения возможностей
- Добавление новых типов данных:
```python
Добавление новых типов данных
class Float(Number): def init(self, value): self.value = float(value)
class String(Number): def init(self, value): self.value = str(value)
Обработка новых типов
def evaluate_float(expr): if isinstance(expr, Float): return expr.value elif isinstance(expr, BinaryOp): left = evaluate_float(expr.left) right = evaluate_float(expr.right) return BinaryOp(left, expr.op, right) return expr
def evaluate_string(expr): if isinstance(expr, String): return expr.value elif isinstance(expr, BinaryOp): left = evaluate_string(expr.left) right = evaluate_string(expr.right) return BinaryOp(left, expr.op, right) return expr
2. **Добавление новых возможностей**:
```python
# Добавление новых возможностей
def add_new_features(expr):
if isinstance(expr, BinaryOp):
if expr.op == '+':
return BinaryOp(expr.left, '+', expr.right)
elif expr.op == '-':
return BinaryOp(expr.left, '-', expr.right)
return expr
| ← Предыдущая глава | Следующая глава: Сопрограммы и современное программирование → |
Глава 11: Сопрограммы и современное программирование
Цели обучения
К концу этой главы вы:
- Поймете основные концепции сопрограмм и асинхронного программирования
- Реализуете эффективные асинхронные системы
- Освоите работу с событийным циклом
- Научитесь создавать масштабируемые приложения
11.1 Основы асинхронного программирования
11.1.1 Понимание асинхронности
# Традиционный синхронный код
def fetch_data():
data = read_from_database() # Блокирует выполнение
process_data(data)
return data
# Асинхронный подход
async def fetch_data():
data = await read_from_database() # Освобождает поток
await process_data(data)
return data
11.1.2 Событийный цикл
class EventLoop:
def __init__(self):
self.tasks = deque()
self.running = False
def add_task(self, coro):
self.tasks.append(coro)
async def run(self):
self.running = True
while self.running and self.tasks:
task = self.tasks.popleft()
try:
await task
except Exception as e:
print(f"Error in task: {e}")
11.2 Реализация сопрограмм
11.2.1 Базовая структура
class Coroutine:
def __init__(self):
self.value = None
self.complete = False
def send(self, value):
self.value = value
# Логика выполнения
return self.value
def throw(self, exc):
# Обработка исключений
pass
11.2.2 Асинхронные генераторы
async def async_range(start, stop):
for i in range(start, stop):
await asyncio.sleep(0.1) # Имитация асинхронной работы
yield i
async def use_generator():
async for num in async_range(0, 5):
print(num)
11.3 Управление ресурсами
11.3.1 Асинхронные контекстные менеджеры
class AsyncResource:
async def __aenter__(self):
await self.connect()
return self
async def __aexit__(self, exc_type, exc, tb):
await self.cleanup()
async def use_resource():
async with AsyncResource() as res:
await res.process()
11.3.2 Пулы и очереди
class AsyncPool:
def __init__(self, size):
self.size = size
self.tasks = asyncio.Queue()
self.workers = []
async def worker(self):
while True:
task = await self.tasks.get()
try:
await task()
finally:
self.tasks.task_done()
11.4 Продвинутые паттерны
11.4.1 Публикация-подписка
class AsyncPubSub:
def __init__(self):
self.subscribers = defaultdict(set)
async def subscribe(self, topic, callback):
self.subscribers[topic].add(callback)
async def publish(self, topic, message):
for callback in self.subscribers[topic]:
await callback(message)
11.4.2 Асинхронные итераторы
class AsyncIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __aiter__(self):
return self
async def __anext__(self):
if self.index >= len(self.data):
raise StopAsyncIteration
value = self.data[self.index]
self.index += 1
return value
11.5 Оптимизация и масштабирование
11.5.1 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
11.5.2 Балансировка нагрузки
class LoadBalancer:
def __init__(self, workers):
self.workers = workers
self.current = 0
async def distribute(self, task):
worker = self.workers[self.current]
self.current = (self.current + 1) % len(self.workers)
return await worker.process(task)
Практические упражнения
- Базовые сопрограммы
- Создайте простой асинхронный веб-сервер
- Реализуйте асинхронный клиент базы данных
- Напишите систему асинхронной обработки задач
- Продвинутые паттерны
- Реализуйте асинхронный пул соединений
- Создайте систему распределенных вычислений
- Постройте асинхронный кэш
- Оптимизация
- Оптимизируйте использование памяти
- Улучшите производительность событийного цикла
- Реализуйте эффективную балансировку нагрузки
Контрольные вопросы
- В чем преимущества асинхронного программирования?
- Как работает событийный цикл?
- Какие паттерны проектирования подходят для асинхронных систем?
- Как оптимизировать асинхронные приложения?
Дополнительное чтение
- Асинхронное программирование на Python (Caleb Hattingh)
- Высоконагруженные приложения (Martin Kleppmann)
- Паттерны асинхронного программирования
| ← Предыдущая глава | Следующая глава: Объединяя всё вместе → |
Глава 12: Объединяя всё вместе
Цели обучения
К концу этой главы вы:
- Поймете, как все компоненты взаимодействуют друг с другом
- Освоите принципы проектирования и архитектуры
- Научитесь создавать сложные системы
- Реализуете базовую систему обработки исключений
12.1 Общая архитектура
12.1.1 Основные компоненты
- Центральный процессор (CPU)
- “Мозг” компьютера
- Выполняет инструкции
- Содержит регистры для временного хранения данных
- Выполняет арифметические и логические операции
- Память (RAM)
- Временное хранилище для программ и данных
- Организована в адресуемые ячейки
- Прямое взаимодействие с CPU
- Энергозависимая память (очищается при выключении)
- Ввод/Вывод (I/O)
- Взаимодействие с внешним миром
- Обработка ввода и вывода данных
- Управление коммуникацией устройств
12.2 Взаимодействие компонентов
12.2.1 Системные вызовы и прерывания
class SystemCallHandler:
def __init__(self):
self.handlers = {
1: self.sys_write,
2: self.sys_read,
3: self.sys_open,
4: self.sys_close
}
def handle_syscall(self, number, *args):
if number in self.handlers:
return self.handlers[number](*args)
raise Exception(f"Недопустимый системный вызов {number}")
def sys_write(self, fd, buffer, size):
# Реализация
pass
12.2.2 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.2.3 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.2.4 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.3 Проектирование и архитектура
12.3.1 Системные вызовы и прерывания
class SystemCallHandler:
def __init__(self):
self.handlers = {
1: self.sys_write,
2: self.sys_read,
3: self.sys_open,
4: self.sys_close
}
def handle_syscall(self, number, *args):
if number in self.handlers:
return self.handlers[number](*args)
raise Exception(f"Недопустимый системный вызов {number}")
def sys_write(self, fd, buffer, size):
# Реализация
pass
12.3.2 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.3.3 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.3.4 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.4 Обработка ошибок
12.4.1 Системные вызовы и прерывания
class SystemCallHandler:
def __init__(self):
self.handlers = {
1: self.sys_write,
2: self.sys_read,
3: self.sys_open,
4: self.sys_close
}
def handle_syscall(self, number, *args):
if number in self.handlers:
return self.handlers[number](*args)
raise Exception(f"Недопустимый системный вызов {number}")
def sys_write(self, fd, buffer, size):
# Реализация
pass
12.4.2 Обработка прерываний
class InterruptHandler:
def __init__(self):
self.interrupt_vector = [None] * 256
self.setup_handlers()
def setup_handlers(self):
self.interrupt_vector[0x80] = self.syscall_handler
self.interrupt_vector[0x0E] = self.page_fault_handler
def handle_interrupt(self, number, error_code=None):
handler = self.interrupt_vector[number]
if handler:
return handler(error_code)
raise Exception(f"Необработанное прерывание {number}")
12.4.3 Обработка ошибок системных вызовов
class SystemCallError(Exception):
pass
def handle_syscall_error(error_code):
if error_code < 0:
raise SystemCallError(f"Ошибка системного вызова: {error_code}")
12.4.4 Обработка ошибок памяти
class MemoryError(Exception):
pass
def handle_memory_error(error_code):
if error_code < 0:
raise MemoryError(f"Ошибка памяти: {error_code}")
12.4.5 Обработка ошибок потоков
class ThreadError(Exception):
pass
def handle_thread_error(error_code):
if error_code < 0:
raise ThreadError(f"Ошибка потока: {error_code}")
12.5 Масштабирование и оптимизация
12.5.1 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.5.2 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.5.3 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.6 Система обработки исключений
12.6.1 Базовая структура
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.6.2 Обработка исключений
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.6.3 Обработка исключений системных вызовов
class SystemCallError(Exception):
pass
def handle_syscall_error(error_code):
if error_code < 0:
raise SystemCallError(f"Ошибка системного вызова: {error_code}")
12.6.4 Обработка исключений памяти
class MemoryError(Exception):
pass
def handle_memory_error(error_code):
if error_code < 0:
raise MemoryError(f"Ошибка памяти: {error_code}")
12.6.5 Обработка исключений потоков
class ThreadError(Exception):
pass
def handle_thread_error(error_code):
if error_code < 0:
raise ThreadError(f"Ошибка потока: {error_code}")
12.7 Масштабирование и оптимизация
12.7.1 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.7.2 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.7.3 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.8 Система обработки исключений
12.8.1 Базовая структура
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.8.2 Обработка исключений
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.8.3 Обработка исключений системных вызовов
class SystemCallError(Exception):
pass
def handle_syscall_error(error_code):
if error_code < 0:
raise SystemCallError(f"Ошибка системного вызова: {error_code}")
12.8.4 Обработка исключений памяти
class MemoryError(Exception):
pass
def handle_memory_error(error_code):
if error_code < 0:
raise MemoryError(f"Ошибка памяти: {error_code}")
12.8.5 Обработка исключений потоков
class ThreadError(Exception):
pass
def handle_thread_error(error_code):
if error_code < 0:
raise ThreadError(f"Ошибка потока: {error_code}")
12.9 Масштабирование и оптимизация
12.9.1 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.9.2 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.9.3 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.10 Система обработки исключений
12.10.1 Базовая структура
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.10.2 Обработка исключений
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.10.3 Обработка исключений системных вызовов
class SystemCallError(Exception):
pass
def handle_syscall_error(error_code):
if error_code < 0:
raise SystemCallError(f"Ошибка системного вызова: {error_code}")
12.10.4 Обработка исключений памяти
class MemoryError(Exception):
pass
def handle_memory_error(error_code):
if error_code < 0:
raise MemoryError(f"Ошибка памяти: {error_code}")
12.10.5 Обработка исключений потоков
class ThreadError(Exception):
pass
def handle_thread_error(error_code):
if error_code < 0:
raise ThreadError(f"Ошибка потока: {error_code}")
12.11 Масштабирование и оптимизация
12.11.1 Управление памятью
class MemoryManager:
def __init__(self, limit):
self.limit = limit
self.current = 0
self.blocks = []
async def allocate(self, size):
if self.current + size > self.limit:
await self.cleanup()
self.current += size
block = MemoryBlock(size)
self.blocks.append(block)
return block
12.11.2 Управление кучей
class HeapManager:
def __init__(self, start_addr, size):
self.start = start_addr
self.size = size
self.blocks = [(start_addr, size, False)] # (адрес, размер, используется)
def allocate(self, size):
# Алгоритм первого подходящего
for i, (addr, block_size, used) in enumerate(self.blocks):
if not used and block_size >= size:
self.blocks[i] = (addr, size, True)
if block_size > size:
# Разделить блок
self.blocks.insert(i + 1,
(addr + size, block_size - size, False))
return addr
return None # Нет свободной памяти
12.11.3 Управление потоками
class ThreadManager:
def __init__(self):
self.threads = []
def create_thread(self, entry_point):
thread = Thread(entry_point)
self.threads.append(thread)
return thread
def run_threads(self):
for thread in self.threads:
thread.start()
thread.join()
12.12 Система обработки исключений
12.12.1 Базовая структура
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.12.2 Обработка исключений
class ExceptionHandler:
def __init__(self):
self.handlers = {}
def register_handler(self, exception_type, handler):
self.handlers[exception_type] = handler
def handle_exception(self, exception):
handler = self.handlers.get(type(exception))
if handler:
return handler(exception)
raise exception
12.12.3 Обработка исключений системных вызовов
class SystemCallError(Exception):
pass
def handle_syscall_error(error_code):
if error_code < 0:
raise SystemCallError(f"Ошибка системного вызова: {error_code}")
12.12.4 Обработка исключений памяти
class MemoryError(Exception):
pass
def handle_memory_error(error_code):
if error_code < 0:
raise MemoryError(f"Ошибка памяти: {error_code}")
12.12.5 Обработка исключений потоков
class ThreadError(Exception):
pass
def handle_thread_error(error_code):
if error_code < 0:
raise ThreadError(f"Ошибка потока: {error_code}")
12.13 Практическое применение
12.13.1 Создание мини-операционной системы
class MiniOS:
def __init__(self):
self.memory_manager = MemoryManager(1024 * 1024) # 1MB памяти
self.thread_manager = ThreadManager()
self.exception_handler = ExceptionHandler()
self.syscall_handler = SystemCallHandler()
async def boot(self):
# Инициализация системы
await self.memory_manager.initialize()
self.thread_manager.initialize()
self.setup_exception_handlers()
def setup_exception_handlers(self):
self.exception_handler.register_handler(
SystemCallError, handle_syscall_error)
self.exception_handler.register_handler(
MemoryError, handle_memory_error)
self.exception_handler.register_handler(
ThreadError, handle_thread_error)
12.13.2 Пример использования
async def main():
# Создание экземпляра мини-ОС
os = MiniOS()
await os.boot()
# Создание и запуск процесса
process = await os.create_process("hello_world.bin")
await process.run()
# Обработка системных вызовов
try:
await process.write_to_console("Hello, World!")
except SystemCallError as e:
print(f"Ошибка вывода: {e}")
if __name__ == "__main__":
asyncio.run(main())
12.14 Итоговый проект
12.14.1 Требования
- Реализация базовой операционной системы
- Управление процессами и потоками
- Система обработки прерываний
- Файловая система
- Консольный интерфейс
12.14.2 План реализации
- Создание базовой структуры
- Добавление управления памятью
- Реализация планировщика
- Добавление системных вызовов
- Создание файловой системы
- Тестирование и отладка
12.14.3 Пример кода проекта
class Project:
def __init__(self):
self.os = MiniOS()
self.file_system = FileSystem()
self.scheduler = Scheduler()
async def initialize(self):
await self.os.boot()
await self.file_system.mount()
self.scheduler.start()
async def run_demo(self):
# Создание файла
with await self.file_system.create_file("/hello.txt") as f:
await f.write("Hello from MiniOS!")
# Создание процесса
process = await self.os.create_process("user_program.bin")
self.scheduler.add_process(process)
# Запуск планировщика
await self.scheduler.run()
Заключение
В этой главе мы объединили все компоненты, изученные ранее, в единую систему. Мы увидели, как различные части компьютера работают вместе, как обрабатываются ошибки и исключения, и как создать простую, но функциональную операционную систему.
Практические задания
- Расширьте функциональность MiniOS:
- Добавьте поддержку многозадачности
- Реализуйте простую файловую систему
- Создайте базовый планировщик процессов
- Улучшите систему обработки ошибок:
- Добавьте логирование
- Реализуйте восстановление после сбоев
- Создайте систему отладки
- Оптимизируйте производительность:
- Улучшите управление памятью
- Оптимизируйте планировщик
- Реализуйте кэширование
Дополнительное чтение
- “Operating Systems: Three Easy Pieces” (Remzi H. Arpaci-Dusseau)
- “Modern Operating Systems” (Andrew S. Tanenbaum)
- “The Design and Implementation of the FreeBSD Operating System”
| ← Предыдущая глава | К содержанию → |
Часть 2: FASM Reference Guide
Справочник по FASM
Эта страница превращает текущий reference guide в формат книги для GitHub Pages.
Полные главы
1. Структура программы
format ELF64 executable
entry main
- Для Linux-исполняемых файлов используется
format ELF64 executable. - Сегменты лучше держать явными и предсказуемыми.
segment readable writeable
segment readable executable
2. Системные вызовы
SYS_read equ 0
SYS_write equ 1
SYS_close equ 3
SYS_exit equ 60
- В
RAXнаходится номер системного вызова. - Аргументы идут через
RDI,RSI,RDX,R10,R8,R9.
3. Работа с файлами
mov rax, 2
mov rdi, filename
mov rsi, 0
syscall
- Возврат системного вызова нужно проверять сразу.
- Отрицательное значение означает ошибку.
4. Данные и память
file_handle dq 0
filename db 'lol.txt', 0
buffer_size equ 1024
buffer rb buffer_size
- Сначала константы.
- Строки лучше держать с нулевым терминатором.
- Буферы резервируются явно через
rb,rw,rd,rq.
5. Вспомогательные макросы
macro funcall1 func, a
{
mov rdi, a
call func
}
- Макросы убирают повторяющийся код.
- Но соглашение по регистрам всё равно нужно соблюдать вручную.
6. Соглашение по регистрам
RAXиспользуется для системных вызовов и возвращаемых значений.RDI,RSI,RDX,RCXчасто несут параметры.RAX,RCX,RDX,R8-R11считаются volatile.
7. Обработка ошибок
syscall
test rax, rax
js error_handler
- После каждого потенциально ошибочного syscall лучше делать проверку.
- Освобождение ресурсов удобнее держать в одном обработчике ошибок.
8. Типовой шаблон: цикл чтения
read_loop:
mov rax, SYS_read
mov rdi, [file_handle]
mov rsi, buffer
mov rdx, buffer_size
syscall
- Это базовый шаблон из
mycat.asm. - После него обычно идут проверка
EOFи ветка ошибок.
9. Печать чисел
В репозитории есть компактные приёмы преобразования чисел в ASCII, особенно в fib.asm.
10. Заметки по оптимизации
- По возможности держать значения в регистрах.
- Перед
callсохранять выравнивание стека в 16 байт. - Для быстрых проверок удобно использовать флаги, например
test rax, rax.
11. Отладка
- Для точек останова можно вставлять
int3. readelf -h <binary>помогает быстро проверить ELF-заголовок и entry point.- Для GDB можно использовать графический frontend
gf.
12. Связанные страницы
Часть 3: Каталог примеров
Example Catalog
This page is the canonical map of executable and wrapper-based examples in the repository.
Basic Examples
| Example | Focus | Files |
|---|---|---|
mycat.asm |
file reading and writing | root |
arg.asm |
command-line argument handling | root |
fib.asm |
integer math and output | root |
two_sum.asm |
algorithmic search pattern | root |
file_ops.asm |
lower-level file operations | root |
quicksort.asm |
sorting example | root |
Integrated Examples
| Example | Focus | Files |
|---|---|---|
add/ |
FASM function exposed through C and Python | add.asm, wrapper.c, add.py |
binary_search/ |
standalone binary search and wrapper variant | bin_s.asm, bin_s.py, wrapper/ |
coroutines/ |
coroutine/context switch experiment | switch.asm, wrapper.c, coroutine.py |
vec/ |
vector math and dot product via shared library | dot_product.asm, wrapper.c, vec.py |
cadd/ |
pure C shared-library bridge example | add.c, add.py |
hex_editor/ |
practical binary inspection utility | hex_editor.asm |
oop_game/ |
OOP-style state model in FASM driven from Python | game.asm, wrapper.c, game.py |
Core Support Files
common.incfor reusable macros and helper routines.linux.incfor Linux syscall constants and wrappers.AI_FASM_RULES.mdfor repository-specific FASM generation conventions.FASM_REFERENCE_GUIDE.mdfor low-level reference material.
Suggested Reading Order
- Start with
fib.asm,arg.asm, andmycat.asm. - Move to
binary_search/,add/, andcadd/. - Continue with
coroutines/,vec/,hex_editor/, andoop_game/. - Use the handbook pages for concepts and the full reference pages for details.
Часть 4: Полный Reference Guide
FASM (Flat Assembler) Reference Guide
1. SYSTEM CALLS AND FILE OPERATIONS
From linux.inc and mycat.asm:
System Call Numbers
SYS_read equ 0 ; Read from file
SYS_write equ 1 ; Write to file
SYS_exit equ 60 ; Exit program
SYS_close equ 3 ; Close file
File Operations
Opening Files:
mov rax, 2 ; sys_open
mov rdi, filename ; File path
mov rsi, 0 ; O_RDONLY
syscall
Standard Descriptors
STDOUT equ 1
STDERR equ 2
2. FUNCTION MACROS
From linux.inc:
Function Call Wrappers
macro funcall1 func, a
{
mov rdi, a
call func
}
macro funcall2 func, a, b
{
mov rdi, a
mov rsi, b
call func
}
3. MEMORY AND DATA
From mycat.asm:
Data Definitions
file_handle dq 0 ; Quad-word (64-bit)
filename db 'lol.txt', 0 ; Null-terminated string
buffer_size equ 1024 ; Constant
buffer rb buffer_size ; Reserve bytes
Segments
segment readable executable
segment readable writeable
4. ARITHMETIC AND NUMBER PROCESSING
From fib.asm:
Number Printing
mov r9, -3689348814741910323
mul r9
shr rdx, 3
lea rsi, [rdx+rdx*4]
add rsi, rsi
sub rax, rsi
add eax, 48
5. CONTROL FLOW
Program Entry
format ELF64 executable
entry main
Error Checking
test rax, rax ; Check error
js error_handler ; Jump if negative
6. REGISTER USAGE
System Calls
- RAX: System call number
- RDI: First argument
- RSI: Second argument
- RDX: Third argument
Function Parameters
- RDI: First parameter
- RSI: Second parameter
- RDX: Third parameter
- RCX: Fourth parameter
7. MEMORY MANAGEMENT
Buffer Operations
- Fixed-size buffers
- Dynamic allocation
- Stack operations
8. EXIT CODES
EXIT_SUCCESS equ 0
EXIT_FAILURE equ 1
9. DEBUGGING
Common Debug Points
- Error checking after syscalls
- Buffer overflow prevention
- Memory alignment
10. OPTIMIZATION
Register Usage
- Minimize memory access
- Use registers efficiently
- Proper alignment
11. COMMON PATTERNS
File Reading Loop
From mycat.asm:
read_loop:
mov rax, SYS_read
mov rdi, [file_handle]
mov rsi, buffer
mov rdx, buffer_size
syscall
Function Structure
function_name:
; Preserve registers if needed
; Function body
; Restore registers
ret
12. SYSTEM INTEGRATION
Process Control
- Program exit
- File operations
- Standard I/O
13. BEST PRACTICES
Code Organization
- Clear sections
- Consistent naming
- Error handling
- Resource cleanup
14. QUICK REFERENCE
Essential Instructions
- mov: Data movement
- syscall: System calls
- call/ret: Function calls
- push/pop: Stack operations
Common Registers
- rax: System calls, return values
- rdi, rsi, rdx: Parameters
- rsp: Stack pointer
15. FUNDAMENTAL CONCEPTS
Registers
General Purpose Registers (64-bit)
- RAX: Accumulator, function return value
- RBX: Base register, preserved across function calls
- RCX: Counter register, loop/string operations
- RDX: Data register, I/O operations
- RSI: Source index, string operations
- RDI: Destination index, string operations
- RBP: Base pointer, frame reference
- RSP: Stack pointer
32-bit Versions
- EAX, EBX, ECX, EDX
- ESI, EDI, EBP, ESP
16-bit Versions
- AX, BX, CX, DX
- SI, DI, BP, SP
8-bit Access
- High: AH, BH, CH, DH
- Low: AL, BL, CL, DL
Memory Segments
- Text (Code) Segment: Instructions
- Data Segment: Initialized data
- BSS Segment: Uninitialized data
- Stack Segment: Runtime stack
Data Types
- Byte (db): 8 bits
- Word (dw): 16 bits
- Double Word (dd): 32 bits
- Quad Word (dq): 64 bits
- Ten Bytes (dt): 80 bits
16. INSTRUCTION SET
Data Movement
- MOV: Basic data transfer
- XCHG: Exchange data
- PUSH: Push to stack
- POP: Pop from stack
- LEA: Load effective address
Arithmetic Operations
- ADD: Addition
- SUB: Subtraction
- MUL: Unsigned multiply
- IMUL: Signed multiply
- DIV: Unsigned divide
- IDIV: Signed divide
- INC: Increment
- DEC: Decrement
Logical Operations
- AND: Bitwise AND
- OR: Bitwise OR
- XOR: Bitwise XOR
- NOT: Bitwise NOT
- SHL/SAL: Shift left
- SHR: Logical shift right
- SAR: Arithmetic shift right
- ROL: Rotate left
- ROR: Rotate right
Control Flow
- JMP: Unconditional jump
- CALL: Function call
- RET: Return from function
- Conditional Jumps:
- JE/JZ: Equal/Zero
- JNE/JNZ: Not equal/Not zero
- JG/JNLE: Greater
- JGE/JNL: Greater or equal
- JL/JNGE: Less
- JLE/JNG: Less or equal
17. MEMORY AND ADDRESSING
Addressing Modes
- Immediate: Direct value
- Register: Register content
- Direct: Memory location
- Register Indirect: [register]
- Base+Index: [base+index]
- Scale: [base+index*scale]
- Displacement: [base+displacement]
Memory Directives
- DB: Define byte
- DW: Define word
- DD: Define double
- DQ: Define quad
- DT: Define ten bytes
- RB: Reserve bytes
- RW: Reserve words
- RD: Reserve doubles
- RQ: Reserve quads
18. SYSTEM INTERFACE
Linux System Calls
- System Call Numbers
- Parameter Passing
- Return Values
- Error Handling
File Operations
- Opening Files
- Reading
- Writing
- Closing
- Error Checking
Process Control
- Program Termination
- Process Creation
- Signal Handling
- Memory Management
19. OPTIMIZATION TECHNIQUES
Code Optimization
- Register Usage
- Memory Access
- Loop Optimization
- Branch Prediction
- Instruction Selection
Memory Optimization
- Alignment
- Caching
- Memory Access Patterns
- Buffer Management
20. DEBUGGING AND TOOLS
Debugging
- GDB Commands
- Breakpoints
- Memory Inspection
- Register Examination
- Stack Tracing
Common Tools
- FASM Assembler
- Linker
- Debugger
- Binary Utilities
Часть 5: AI FASM Rules
FASM Code Generation Rules for AI
1. Program Structure Rules
1.1 Required Header
ALWAYS start with:
format ELF64 executable ; For executables
format ELF64 ; For libraries
include "common.inc" ; Common macros and patterns
1.2 Segment Order
ALWAYS define segments in this order:
segment readable writeable ; Data first
; Constants and variables here
segment readable executable ; Code second
entry main ; Entry point
2. Memory and Data Rules
2.1 Data Declarations
; CORRECT order:
buffer_size equ 1024 ; 1. Constants first
error_msg db 'Error', 0 ; 2. Initialized data
buffer rb buffer_size ; 3. Reserved space last
2.2 Size Constants
ALWAYS use predefined sizes:
BUFFER_TINY equ 128 ; For small strings
BUFFER_SMALL equ 1024 ; Standard buffer
BUFFER_MEDIUM equ 4096 ; Page size
BUFFER_LARGE equ 8192 ; Large operations
2.3 String Declarations
ALWAYS include terminators:
db 'Message', 0 ; Null-terminated
db 'Line', 0xA, 0 ; With newline and null
3. Register Usage Rules
3.1 System Call Parameters
NEVER mix up this order:
RAX : System call number
RDI : First argument
RSI : Second argument
RDX : Third argument
R10 : Fourth argument
R8 : Fifth argument
R9 : Sixth argument
3.2 Register Preservation
ALWAYS preserve in this order:
; On entry:
push rbp
push rbx ; If used
push r12-r15 ; If used
; On exit (REVERSE order):
pop r15-r12
pop rbx
pop rbp
3.3 Volatile Registers
NEVER assume these preserve values:
RAX, RCX, RDX ; Always volatile
R8-R11 ; Caller-saved
4. Error Handling Rules
4.1 System Calls
ALWAYS check returns:
syscall
test rax, rax ; Check result
js error ; Negative = error
4.2 Error Handler Structure
ALWAYS include these components:
error_handler:
; 1. Print error message
; 2. Clean up resources
; 3. Exit with failure
5. Function Implementation Rules
5.1 Function Structure
ALWAYS follow this pattern:
function_name:
; 1. Save registers
; 2. Setup stack frame
; 3. Function body
; 4. Restore stack
; 5. Restore registers
; 6. Return
5.2 Parameter Access
PREFER registers over stack:
; First 6 parameters in registers
; Additional parameters at [rbp+16], [rbp+24], etc.
6. Memory Safety Rules
6.1 Buffer Operations
ALWAYS check bounds:
cmp rax, buffer_size ; Check size
jae error_handler ; Handle overflow
6.2 String Operations
ALWAYS set maximum length:
mov rcx, BUFFER_SMALL ; Set max length
rep movsb ; Safe copy
7. Macro Usage Rules
7.1 System Call Macros
PREFER safe versions:
; Use these:
syscall3_safe SYS_write, STDOUT, msg, len
; Instead of:
mov rax, SYS_write
mov rdi, STDOUT
mov rsi, msg
mov rdx, len
syscall
7.2 Function Macros
USE provided helpers:
funcall2 print_string, message, length
8. File Operation Rules
8.1 File Opening
ALWAYS check mode and permissions:
; Reading
open_file filename, O_RDONLY
; Writing (with create)
open_file filename, O_WRONLY or O_CREAT, 0644o
8.2 File Handling
ALWAYS follow this sequence:
- Open file
- Check handle
- Perform operations
- Close file
- Handle errors
9. Memory Management Rules
9.1 Stack Alignment
ALWAYS maintain 16-byte alignment:
sub rsp, 32 ; Align to 16 bytes
9.2 Memory Access
PREFER structured access:
mov eax, [rbx + 4*rcx] ; Indexed
mov eax, [rbx + struct.field] ; Structure
10. Optimization Rules
10.1 Loop Optimization
; PREFER:
xor rcx, rcx ; Clear counter
rep movsb ; Use string ops
10.2 Condition Codes
PREFER flags over comparisons:
test rax, rax ; Instead of cmp rax, 0
11. Debug Support Rules
11.1 Debug Points
debug_break ; Insert breakpoint
11.2 Debug Messages
print_debug msg, len
12. Common Patterns
12.1 Command Line Arguments
main:
mov r9, [rsp + 16] ; argv[1]
test r9, r9 ; Check exists
12.2 Buffer Processing
read_loop:
read_file fd, buffer, buffer_size
test rax, rax
jz done
13. Safety Checklist
13.1 Before System Calls
- ✓ Correct registers loaded
- ✓ Valid pointers
- ✓ Buffer space available
- ✓ Error handling ready
13.2 Before Functions
- ✓ Parameters in order
- ✓ Stack aligned
- ✓ Registers preserved
- ✓ Return value location clear
14. Code Generation Template
14.1 Basic Program
format ELF64 executable
include "common.inc"
segment readable writeable
; Data here
segment readable executable
entry main
main:
; Code here
program_exit EXIT_SUCCESS
error_handler:
handle_error error_msg, error_msg_len
14.2 Library Module
format ELF64
public function_name
segment readable executable
function_name:
function_start
; Implementation
function_end
15. Testing Rules
15.1 Function Testing
- Test null/empty inputs
- Test maximum sizes
- Test error conditions
- Verify register preservation
15.2 System Testing
- Test file operations
- Test memory operations
- Test error handling
- Verify cleanup
16. Documentation Requirements
16.1 Function Headers
; Function: name
; Input: RDI - first parameter
; RSI - second parameter
; Output: RAX - result
; Affects: RCX, RDX
; Notes: Any special considerations
16.2 Error Messages
error_msg db 'Error: specific description', 0xA, 0
error_msg_len = $ - error_msg
17. Number Printing Rules
17.1 Integer to String Conversion
ALWAYS use this optimized pattern for printing numbers:
print:
mov r9, -3689348814741910323 ; Magic number for division optimization
sub rsp, 40 ; Reserve stack space
mov BYTE [rsp+31], 10 ; Store newline at end
lea rcx, [rsp+30] ; Point to buffer end
.convert_loop:
mov rax, rdi ; Number to convert in RDI
lea r8, [rsp+32] ; End of buffer pointer
mul r9 ; Optimized division by 10
mov rax, rdi
sub r8, rcx ; Calculate length
shr rdx, 3 ; Quick divide by 8
lea rsi, [rdx+rdx*4] ; Multiply by 5
add rsi, rsi ; Multiply by 2 (total *10)
sub rax, rsi ; Get remainder
add eax, 48 ; Convert to ASCII
mov BYTE [rcx], al ; Store digit
mov rax, rdi ; Preserve number
mov rdi, rdx ; Move quotient for next iteration
mov rdx, rcx ; Save current position
sub rcx, 1 ; Move buffer pointer
cmp rax, 9 ; Check if more digits
ja .convert_loop ; Continue if number > 9
lea rax, [rsp+32] ; Calculate string length
mov edi, 1 ; STDOUT
sub rdx, rax ; Calculate length
xor eax, eax ; Clear RAX
lea rsi, [rsp+32+rdx] ; Point to start of number
mov rdx, r8 ; Length to write
mov rax, 1 ; SYS_write
syscall ; Write number
add rsp, 40 ; Restore stack
ret
17.2 Number Printing Rules
- ALWAYS use the optimized division method with magic number
- NEVER use division instruction for base 10 conversion
- ALWAYS handle the full range of 64-bit integers
- PREFER stack buffer over static buffer for thread safety
- ALWAYS include newline handling option
- ALWAYS preserve all registers except RAX (syscall)
17.3 Optimization Techniques
- Use magic number
-3689348814741910323for division by 10 - Use
shr rdx, 3instead of division for quotient - Use
leafor multiplication by 10 (multiply by 5 then 2) - Build string in reverse order for efficiency
- Combine digit conversion with ASCII adjustment in one step
17.4 Buffer Management
- ALWAYS allocate sufficient stack space (40 bytes standard)
- PREFER stack allocation over static buffers
- ALWAYS handle buffer boundaries safely
- CALCULATE final string length correctly
- INCLUDE space for newline if needed
17.5 Register Usage for Print
RDI : Input number to print
R9 : Magic number constant
RAX : Working register / syscall
RCX : Buffer pointer
RDX : Division result / length
RSI : Multiplication result / buffer pointer
R8 : Length calculation
17.6 Print Function Integration
ALWAYS follow this pattern when using print:
; Before calling print:
mov rdi, number_to_print ; Load number in RDI
call print ; Call print function
; For multiple numbers:
push rdi ; Save current number if needed
call print
pop rdi ; Restore for next operation
14. Array Handling Rules
14.1 Array Declarations
; CORRECT array declarations:
array dq 64, 34, 25 ; Initialized array
buffer rb 1024 ; Reserved buffer
array_size equ ($ - array) / 8 ; Size calculation
14.2 Array Access Patterns
; ALWAYS use proper indexing:
mov rax, [array + rdi*8] ; 64-bit elements
mov eax, [array + rdi*4] ; 32-bit elements
mov al, [array + rdi] ; 8-bit elements
; ALWAYS check bounds:
cmp rdi, array_size
jae error_handler
14.3 Array Iteration
; Forward iteration
xor rbx, rbx ; Clear counter
.loop:
cmp rbx, length
jge .done
; Process array[rbx]
inc rbx
jmp .loop
; Reverse iteration
mov rbx, length
.loop:
dec rbx
; Process array[rbx]
test rbx, rbx
jnz .loop
15. Number Formatting Rules
15.1 Integer to String
; ALWAYS reserve sufficient buffer:
number_buffer rb 32 ; For 64-bit integers
; CORRECT conversion pattern:
mov rax, number ; Number to convert
mov r12, 10 ; Base (decimal)
.convert:
xor rdx, rdx
div r12 ; Divide by base
add dl, '0' ; Convert to ASCII
mov [buffer + rbx], dl
dec rbx
test rax, rax
jnz .convert
15.2 Output Formatting
; ALWAYS include spacing:
msg db ' ' ; Space between numbers
newline db 0xA ; Line endings
; String length calculation:
msg_len = $ - msg ; Without null terminator
16. Recursive Function Rules
16.1 Register Preservation
; ALWAYS save used registers:
push rbp
push rbx ; Callee-saved
push r12-r15 ; If used
mov rbp, rsp
; ALWAYS restore in reverse order:
mov rsp, rbp
pop r15-r12
pop rbx
pop rbp
16.2 Parameter Passing
; First 6 parameters:
rdi - First parameter
rsi - Second parameter
rdx - Third parameter
rcx - Fourth parameter
r8 - Fifth parameter
r9 - Sixth parameter
; Additional parameters on stack:
[rbp + 16] - Seventh parameter
[rbp + 24] - Eighth parameter
16.3 Recursive Calls
; ALWAYS save parameters before recursive call:
mov r12, rdi ; Save first param
mov r13, rsi ; Save second param
call recursive_func
mov rdi, r12 ; Restore params
mov rsi, r13
17. Safety Guidelines
17.1 Buffer Operations
; ALWAYS initialize buffers:
mov rcx, buffer_size
xor rax, rax
rep stosb ; Zero buffer
; ALWAYS check buffer space:
lea rax, [rbx + 1] ; Calculate needed space
cmp rax, buffer_size
ja error_handler
17.2 Error Handling
; ALWAYS check system calls:
syscall
test rax, rax
js error_handler
; ALWAYS provide error messages:
error_msg db 'Error: Buffer overflow', 0
Array Handling Rules
Array Declaration and Access
- Always declare array size as a constant or computed value
- Use proper element size multipliers (1, 2, 4, 8 bytes)
- Validate array indices before access
- Use macros for common array operations
Example:
; Good - Clear size and element type
array_size equ 100
array rq array_size ; quad-word array
; Good - Index validation
cmp rax, array_size
jae error_handler
mov rbx, [array + rax*8]
; Bad - No size checking
mov rbx, [array + rax*8] ; Potential buffer overflow
Array Iteration
- Initialize counters to zero or array bounds
- Use appropriate comparison instructions
- Preserve array bounds in registers
- Clear loop registers after use
Example:
; Good - Clear initialization and bounds checking
xor rcx, rcx ; Clear counter
mov rdx, array_size ; Load bound once
.loop:
mov rax, [array + rcx*8]
inc rcx
cmp rcx, rdx
jb .loop
xor rcx, rcx ; Clear after use
; Bad - Inefficient and unsafe
mov rcx, 0
.loop:
mov rax, [array + rcx*8]
inc rcx
cmp rcx, array_size ; Reloads size each iteration
jb .loop
Recursive Function Rules
Stack Frame Management
- Always set up and restore stack frames
- Align stack to 16 bytes
- Save and restore all used registers
- Clear registers after use
Example:
; Good - Complete frame management
function:
push rbp
mov rbp, rsp
push rbx
push r12
push r13
; Function body
pop r13
pop r12
pop rbx
mov rsp, rbp
pop rbp
ret
; Bad - Incomplete frame
function:
push rbp
; Missing register saves
; Function body
pop rbp
ret
Parameter Passing
- Use standard registers (rdi, rsi, rdx, rcx, r8, r9)
- Save parameters in callee-saved registers
- Restore parameters for recursive calls
- Document parameter usage
Example:
; Good - Clear parameter handling
quicksort: ; (array, low, high)
; Parameters:
; rdi = array base
; rsi = low index
; rdx = high index
push rbp
mov rbp, rsp
push r12 ; Save array
push r13 ; Save low
push r14 ; Save high
mov r12, rdi
mov r13, rsi
mov r14, rdx
; Bad - Unclear parameters
quicksort:
push rbp
mov rbp, rsp
; No parameter documentation
; No parameter saving
Number Formatting Rules
Integer Conversion
- Handle special cases first (zero, negative)
- Use appropriate buffer sizes
- Validate input ranges
- Clear temporary registers
Example:
; Good - Complete number handling
convert_number:
test rax, rax
jz .zero_case
js .negative_case
mov r12, 10 ; Base
lea rbx, [buffer+31] ; End of buffer
mov byte [rbx], 0 ; Null terminator
; Conversion loop
xor r12, r12 ; Clear temp register
; Bad - Incomplete handling
convert_number:
div r12 ; No special cases
add dl, '0'
mov [buffer], dl
Error Handling Rules
System Call Errors
- Check return values
- Handle specific error codes
- Provide error messages
- Clean up resources
Example:
; Good - Complete error handling
mov rax, SYS_write
syscall
test rax, rax
js .error
.error:
neg rax
mov rdi, error_msg
call print_error
jmp cleanup
; Bad - No error checking
mov rax, SYS_write
syscall
; Continue without checking
Memory Safety Rules
Buffer Operations
- Check buffer sizes before operations
- Use bounds-checked copy operations
- Maintain null termination
- Clear sensitive data
Example:
; Good - Safe buffer handling
mov rcx, dst_size
cmp rcx, src_size
jb .buffer_overflow
rep movsb
mov byte [rdi-1], 0
; Clear sensitive data
push rcx
mov rcx, src_size
xor rax, rax
rep stosb
pop rcx
; Bad - Unsafe copy
mov rcx, src_size
rep movsb ; No size check
Register Usage Rules
Register Allocation
- Use caller-saved registers for temporary values
- Save and restore callee-saved registers
- Clear sensitive data from registers
- Document register usage
Example:
; Good - Clear register usage
; rax = loop counter
; rbx = array element
; r12 = array base
push rbx
push r12
; Function body
xor rax, rax ; Clear sensitive data
pop r12
pop rbx
; Bad - Unclear usage
push rbx
; No documentation
; No clearing
pop rbx
Code Organization Rules
Function Structure
- Group related functions
- Document dependencies
- Maintain consistent calling conventions
- Use meaningful labels
Example:
; Good - Clear structure
section .text
; Array manipulation functions
array_init:
array_sort:
array_search:
; String handling functions
string_copy:
string_compare:
; Bad - Mixed functions
function1:
string_func:
array_func:
helper:
1. Symbol Conflict Prevention Rules
1.1 Common Include Files
ALWAYS check for symbol conflicts with common.inc:
; DON'T redefine these symbols (they're in common.inc):
; - System calls (SYS_*)
; - File descriptors (STDOUT, STDIN, etc.)
; - Exit codes (EXIT_SUCCESS, EXIT_FAILURE)
; - Common constants (SPACE, NEWLINE)
; - Common functions (print_string, etc.)
1.2 Symbol Naming
ALWAYS use unique prefixes for local symbols:
; CORRECT - Unique prefixes
msg_program_name db 'MyProgram', 0
array_buffer rb 1024
number_temp dq 0
; WRONG - Generic names that might conflict
msg db 'MyProgram', 0 ; Too generic
buffer rb 1024 ; Could conflict
temp dq 0 ; Too generic
1.3 Function Naming
ALWAYS use descriptive, specific names:
; CORRECT - Specific function names
print_array_numbers: ; Clear purpose
convert_number_decimal: ; Specific conversion
; WRONG - Generic names that might conflict
print: ; Too generic
convert: ; Too vague
2. Array Handling Rules
2.1 Array Declarations
ALWAYS declare size constants and use proper alignment:
ARRAY_SIZE equ 10 ; Size constant
array dq 64, 34, 25, 12 ; Aligned data
array_buffer rb ARRAY_SIZE * 8 ; Aligned buffer
2.2 Array Access
ALWAYS use proper indexing and bounds checking:
; Check bounds before access
cmp rsi, ARRAY_SIZE
jae error_handler
; Use correct scaling
mov rax, [array + rsi*8] ; For qwords
mov eax, [array + rsi*4] ; For dwords
2.3 Array Parameters
ALWAYS pass array parameters consistently:
; Standard parameter order:
; rdi = array base address
; rsi = array size or low index
; rdx = high index (for range operations)
3. Recursive Function Rules
3.1 Register Preservation
ALWAYS preserve registers in recursive functions:
recursive_function:
push rbp
push rbx
push r12-r15 ; Save all used registers
mov rbp, rsp
; Function body
mov rsp, rbp
pop r15-r12 ; Restore in reverse order
pop rbx
pop rbp
ret
3.2 Parameter Handling
ALWAYS save parameters before recursive calls:
mov r12, rdi ; Save array pointer
mov r13, rsi ; Save low index
mov r14, rdx ; Save high index
call recursive_function ; Recursive call
mov rdi, r12 ; Restore parameters
mov rsi, r13
mov rdx, r14
4. Number Formatting Rules
4.1 Buffer Management
ALWAYS use safe buffer practices:
number_buffer rb 32 ; Sufficient size for 64-bit
add rbx, 31 ; Start from end
mov byte [rbx], 0 ; Null terminator
mov byte [rbx-1], SPACE ; Use constants from common.inc
4.2 Number Conversion
ALWAYS handle special cases:
test rax, rax
jz .zero_case ; Handle zero
js .negative ; Handle negative
5. Common Include Usage Rules
5.1 Include Order
ALWAYS include common files first:
format ELF64 executable
include 'common.inc' ; First include
; ... other includes if needed
5.2 Using Common Functions
ALWAYS use common functions when available:
; Use these from common.inc:
call print_string ; For string output
syscall3_safe SYS_write ; For safe syscalls
5.3 Constants Usage
ALWAYS use common constants:
mov rdi, STDOUT ; Use standard FD
mov rdi, EXIT_SUCCESS ; Use exit codes
mov byte [rbx], NEWLINE ; Use character constants
6. Error Handling Rules
6.1 Array Bounds
ALWAYS check array bounds:
cmp rsi, ARRAY_SIZE
jae .error_handler
.error_handler:
mov rdi, error_msg
call print_string
jmp exit_error
6.2 Buffer Overflow Prevention
ALWAYS validate buffer sizes:
lea rax, [rbx + 1] ; Calculate needed space
cmp rax, buffer_size
ja .error_handler
7. Documentation Rules
7.1 Function Headers
ALWAYS document parameters and effects:
; Function: sort_array
; Input: rdi = array pointer
; rsi = array size
; Output: sorted array in-place
; Affects: rax, rbx, rcx, rdx
7.2 Complex Algorithms
ALWAYS document key steps:
quicksort:
; 1. Base case check
cmp rsi, rdx
jge .done
; 2. Partition array
call partition
; 3. Recursive sort left partition
; ... comments explaining the logic
18. Coroutines and Generators Implementation
18.1 Generator Structure
; ALWAYS define a clear generator structure
struc Generator {
.fresh db 0 ; Is this a fresh generator?
.dead db 0 ; Is this generator dead?
align 8 ; Ensure proper alignment
.rsp dq 0 ; Saved stack pointer
.stack_base dq 0 ; Base of generator's stack
.func dq 0 ; Function pointer
}
18.2 Generator Stack Management
; ALWAYS define a clear stack structure
struc GeneratorStack {
.items dq 0 ; Array of generator pointers
.count dq 0 ; Number of generators
.capacity dq 0 ; Capacity of items array
}
; ALWAYS initialize the stack properly
generator_stack: dq 0 ; Global pointer to stack
18.3 Generator Function Implementation
; ALWAYS check generator state first
generator_next:
; Check if generator is dead
cmp byte [rdi + Generator.dead], 0
jne .dead_generator
; Check if generator is fresh
cmp byte [rdi + Generator.fresh], 0
jne .fresh_generator
; Handle existing generator
; ...
.fresh_generator:
; Mark generator as not fresh
mov byte [rdi + Generator.fresh], 0
; Save generator pointer before calling function
push rdi
; Get and validate function pointer
mov rax, [rdi + Generator.func]
test rax, rax
jz .func_error
; Call function with argument
mov rdi, rsi
call rax
; Restore generator pointer
pop rdi
; Mark as dead after completion
mov byte [rdi + Generator.dead], 1
18.4 Yield Implementation
; ALWAYS keep yield implementation simple
generator_yield:
; Just return the argument for simplicity
mov rax, rdi
ret
18.5 Error Handling in Generators
; ALWAYS handle error cases
.func_error:
; Pop saved generator pointer
pop rdi
; Mark generator as dead
mov byte [rdi + Generator.dead], 1
; Return NULL
xor rax, rax
ret
.dead_generator:
; Return NULL for dead generators
xor rax, rax
ret
18.6 Debug Messages
; ALWAYS include debug messages for complex operations
section '.rodata' writeable
dbg_next db 'DEBUG: generator_next called', 0xA, 0
dbg_next_len = $ - dbg_next
dbg_yield db 'DEBUG: generator_yield called', 0xA, 0
dbg_yield_len = $ - dbg_yield
; Use debug print macro
debug_print dbg_next, dbg_next_len
18.7 Foreign Function Interface
; ALWAYS export symbols for FFI
public generator_init
public generator_next
public generator_yield
public generator__finish_current
; ALWAYS use standard calling convention
; RDI: First argument (generator pointer)
; RSI: Second argument (value/argument)
; RAX: Return value
Часть 6: Карта репозитория
Repository Map
The repository follows a simple split: root-level standalone examples, folder-based integrated projects, and Pages-backed documentation.
Root
common.inc,linux.inc: shared include files and syscall helpers.*.asm: standalone learning examples and algorithms.AI_FASM_RULES.md,FASM_REFERENCE_GUIDE.md: source docs mirrored into the Pages handbook.docs/: GitHub Pages site.
Project Folders
add/: minimal shared-library example with Python and C interop.binary_search/: algorithm sample plus wrapper variant.cadd/: pure C bridge example.coroutines/: context switching and coroutine bindings.hex_editor/: standalone utility with its own README.oop_game/: OOP-style game state experiment in FASM.vec/: vector math example focused on dot products.
Documentation Layers
README.md: short repository entry point.docs/index.md: website landing page.docs/examples.md: example catalog.docs/repository-map.md: structural overview.docs/book-en.mdanddocs/book-ru.md: handbook overview pages.docs/reference-guide-full.mdanddocs/ai-fasm-rules.md: full chapter pages.
Planned Consolidation
- Keep examples in their current paths to avoid breaking commands.
- Normalize README files and build instructions across example folders.
- Pull only FASM-specific material from other repositories, not unrelated Lisp or database content.