Какой ассемблер для Windows в 2020 году лучше? Какая IDE?
Подписчикам моего канала эта тема скорее всего покажется странной и незнакомой. Но она тоже имеет непосредственное отношение к технике (а канал посвящён не только электрике, но и всему техническому) а также она (эта тема) занимает существенную часть моей жизни.
В начале 90-х годов я много программировал на ассемблере. Сначала это был ассемблер МИКРО11 для процессоров архитектуры DEC PDP-11 (процессор К1801ВМ1 бытового компьютера БК0010-01), а с 1994 года я начал писать на ассемблере TASM для процессоров архитектуры x86 (персональный компьютер IBM PC AT i80386).
В то время я писал исключительно под DOS, и использовал для этого режим Ideal ассемблера TASM . Этот ассемблер был разработан компанией Borland. Синтаксис MASM от Microsoft мне нравился меньше, чем в TASM режима Ideal. В общем, на нём я и остановился.
Никаких IDE, конечно, тогда не было, и я использовал обычный текстовый редактор. Кажется, даже без подсветки синтаксиса. Компилировал и линковал утилитами командной строки, для удобства прописанными в пакетных файлах.
Когда наступила эра Windows, я попробовал написать на ассемблере пару тестовых приложений под эту ОС, и перешёл для этого на MASM, как чуть более подготовленный к этому процессу. Но писать на ассемблере под Windows мне показалось не очень интересным, и я перешёл на языки более высокого уровня.
На днях я решил посмотреть, что же изменилось с программированием на ассемблере за четверть века? Так вот, принципиально — ничего )))
Выбор ассемблера
Существует довольно много разных ассемблеров. При выборе я использовал только 2 критерия — живость проекта и популярность ассемблера. Остальные критерии, такие, как дополнительные возможности и фичи, я не рассматривал, это было уже не так важно.
TASM, к сожалению, отпал сразу, поскольку он, к сожалению, мёртв.
MASM тоже давно не обновлялся.
В общем, из всех ассемблеров меня привлекли два — NASM и FASM .
На настоящий момент FASM мне показался более интересным, и я решил пока выбрать его. Если вам больше нравятся другие варианты, напишите, пожалуйста, в комментариях, почему ваш выбор пал на другой ассемблер.
Выбор интегрированной среды разработки (IDE)
Конечно, можно пользоваться обычным редактором с подсветкой синтаксиса, например, Notepad++ . Но, честно говоря, хочется уже как-то автоматизировать некоторые рутинные действия, а также иметь функцию автодополнения и какие-то подсказки по коду.
Я думал, что уже существуют хорошие, мощные IDE для ассемблеров. Но не тут то было. Подающий в своё время большие надежды RadASM заброшен, остальные среды очень простенькие, глючащие, вылетающие при каждом чихе. В общем, проблема. Если вы знаете какую-нибудь действительно достойную среду для написания ассемблерного кода, отпишитесь, пожалуйста, в комментариях.
Поскольку в программах предполагается использование кириллицы, важно, чтобы редактор имел возможность сохранять файлы в UTF8 .
В общем, после поисков чего-то более-менее стоящего я сначала остановился на SASM .
Но он почему-то отказывается компилировать, не сообщая почему. Автодополнения и подсказок по коду там тоже нет.
Сейчас другой автор начал активно рекламировать ASM Visual :
Текущая версия имеет глючки, которые автор пообещал исправить. В этой IDE уже есть автодополнение и подсказки, но, к сожалению, только в платной версии. Как бесплатная IDE она была бы относительно неплоха вместе с этим функционалом, но как платная, она, на мой взгляд, ещё сыровата.
Вышеперечисленные IDE хороши для начинающих, потому что в них довольно легко разобраться, и они при создании нового проекта не предлагают шаблоны навороченного кода. Но если вы уже опытный программист, и знаете, как писать на ассемблере под Windows, то вам может подойти очень мощная и абсолютно бесплатная IDE « Easy Code Visual »:
Как видите, она позволяет даже визуальное конструирование интерфейса. Пожалуй, это самая мощная среда, которую я нашёл. Но она тоже глючит и вылетает.
Все три IDE активно поддерживаются, это одно из важных условий моего выбора. Так что от глюков можно избавиться, если активно сотрудничать с авторами этих программ.
Если вы знаете другие достойные варианты IDE с автодополнением и подсказками, напишите о них, пожалуйста, в комментариях.
На сегодня всё, ставьте лайки, подписывайтесь на канал, пока!
MASM, TASM, FASM, NASM под Windows и Linux
В данной статье я хочу рассмотреть вопросы, которые могут возникнуть у человека, приступившего к изучению ассемблера, связанные с установкой различных трансляторов и трансляцией программ под Windows и Linux, а также указать ссылки на ресурсы и книги, посвященные изучению данной темы.
Используется для создания драйверов под Windows.
По ссылке переходим на сайт и скачиваем пакет (masm32v11r.zip). После инсталляции программы на диске создается папка с нашим пакетом C:\masm32. Создадим программу prog11.asm, которая ничего не делает.
Произведём ассемблирование (трансляцию) файла prog11.asm, используя ассемблер с сайта masm32.
Ключ /coff используется здесь для трансляции 32-битных программ.
Линковка производится командой link /subsystem:windows prog11.obj (link /subsystem:console prog11.obj)
MASM — один из немногих инструментов разработки Microsoft, для которых не было отдельных 16- и 32-битных версий.
Также ассемблер версии 6. можно взять на сайте Кипа Ирвина kipirvine.com/asm, автора книги «Язык ассемблера для процессоров Intel».
Кстати, вот ссылка на личный сайт Владислава Пирогова, автора книги “Ассемблер для Windows”.
MASM с сайта Microsoft
Далее скачаем MASM (версия 8.0) с сайта Microsoft по ссылке. Загруженный файл носит название «MASMsetup.exe». При запуске этого файла получаем сообщение -«Microsoft Visual C++ Express Edition 2005 required».
Открываем этот файл архиватором (например 7zip). Внутри видим файл setup.exe, извлекаем его, открываем архиватором. Внутри видим два файла vc_masm.msi,vc_masm1.cab. Извлекаем файл vc_masm1.cab, открываем архиватором. Внутри видим файл FL_ml_exe_____X86.3643236F_FC70_11D3_A536_0090278A1BB8. Переименовываем его в файл fl_ml.exe, далее, произведём ассемблирование файла prog11.asm, используя ассемблер fl_ml.exe.
MASM в Visual Studio
Также MASM можно найти в папке с Visual Studio (у меня VS 10) вот здесь: C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe.
Для того, чтобы запустить на 32- или 64-разрядной системе и создавать программы, работающие как под 32-, так и под 64-разрядной Windows, подходит MASM32 (ml.exe, fl_ml.exe). Для того, чтобы работать на 32- и 64-разрядных системах и создавать программы, работающие под 64-разрядной Windows, но неработающие под 32-разрядной нужен ассемблер ml64.exe. Лежит в папке C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64 и вот здесь — C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64.
Программный пакет компании Borland, предназначенный для разработки программ на языке ассемблера для архитектуры x86. В настоящее время Borland прекратила распространение своего ассемблера.
Скачать можно, например, здесь. Инсталлятора нет, просто извлекаем программу. Вот исходник из книги Питера Абеля (рис. 3.2) «Язык Ассемблера для IBM PC и программирования».
Выполним ассемблирование (трансляцию) файла abel32.asm.
Корректность работы программы можно проверить, произведя линковку (tlink.exe) объектного файла и запустив полученный файл в отладчике.
Как было сказано выше, MASM можно использовать для работы с 16-битными программами. Выполним ассемблирование (трансляцию) программы abel32.asm с помощью ассемблера MASM:
Ключ /coff здесь не используется.
Линковка производится файлом link16.exe
В статье Криса Касперски «Сравнение ассемблерных трансляторов» написано, что «FASM — неординарный и весьма самобытный, но увы, игрушечный ассемблер. Пригоден для мелких задач типа „hello, world“, вирусов, демок и прочих произведений хакерского творчества.»
Скачаем FASM с официального сайта. Инсталлятора нет, просто извлекаем программу. Откроем fasm editor — C:\fasm\fasmw.exe. В папке C:\fasm\EXAMPLES\HELLO есть файл HELLO.asm.
Откроем файл HELLO.asm из fasmw.exe. Изменим строку include ‘win32ax.inc’ на строку include ‘c:\fasm\INCLUDE\WIN32AX.INC’. Запускаем из меню Run → Run.
Вот ссылки на ресурсы, посвященные FASM:
Для того, использовать FASM в Linux (у меня Ubuntu), скачаем соответствующий дистрибутив (fasm-1.71.60.tgz), распакуем его, в папке у нас будет бинарный файл fasm, копируем этот файл в /usr/local/bin для того, чтобы можно было запускать его из консоли, как любую другую команду.Выполним ассемблирование программы hello.asm из папки fasm/examples/elfexe/hello.asm.
Корректность работы программы можно проверить в отладчике.
Nasm успешно конкурирует со стандартным в Linux- и многих других UNIX-системах ассемблером Gas.
Nasm в Linux можно установить его с помощью менеджера пакетов или из командной строки: в дистрибутиве Debian (Ubuntu) командой apt-get install nasm, в дистрибутивах Fedora, CentOS, RedHat командой yum install nasm.
Создадим программу, которая 5 раз выводит сообщение “Hello”. Пример взят из книги Андрея Викторовича Столярова “Программирование на языке ассемблера NASM для ОС UNIX”. Учебник, а также библиотека “stud_io.inc” есть на личном сайте автора.
Выполним ассемблирование и линковку и запустим файл hello.asm.
Для 64bit необходимо использовать команду nasm -f elf64 hello.asm
NASM для Windows можно установить, скачав соответствующий дистрибутив с соответствующего сайта.
Ассемблирование:
nasm -f bin имя_файла.asm -o имя_файла.com
Ссылки на ресурсы, посвященные Nasm:
Стандартный ассемблер практически во всех разновидностях UNIX, в том числе Linux и BSD. Свободная версия этого ассемблера называется GAS (GNU assembler). Позволяет транслировать программы с помощью компилятора GCC.
Из учебников удалось найти только книгу на английском «Programming from the ground up». На русском удалось найти только одну главу из книги С. Зубкова «Assembler для DOS, Windows и UNIX».
Возьмем пример программы, которая ничего не делает, с сайта. Создадим программу gas.s
Выполним ассемблирование (трансляцию), линковку и запуск программы:
Если в данной программе изменить _start на main, то можно выполнить ассемблирование (трансляцию) и линковку компилятором gcc.
Выполним ассемблирование (трансляцию), линковку и запуск программы:
Выводы: если вы изучаете программирование под Windows, то вы можете остановить свой выбор на Masm; Tasm больше не поддерживается, но для обучения по старым классическим учебникам подойдёт.
Под Linux Gas подойдет тем, кто использует GCC, а тем, кому не нравится синтаксис Gas, подойдёт Nasm.
Программирование на ассемблере под windows
WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE OR WS_EX_CLIENTEDGE
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED OR \
PROCTYPE ptGetModuleHandle stdcall \
PROCTYPE ptLoadIcon stdcall \
PROCTYPE ptLoadCursor stdcall \
PROCTYPE ptLoadMenu stdcall \
PROCTYPE ptRegisterClassEx stdcall \
PROCTYPE ptCreateWindowEx stdcall \
PROCTYPE ptShowWindow stdcall \
PROCTYPE ptUpdateWindow stdcall \
PROCTYPE ptGetMessage stdcall \
PROCTYPE ptTranslateMessage stdcall \
PROCTYPE ptDispatchMessage stdcall \
PROCTYPE ptSetMenu stdcall \
PROCTYPE ptPostQuitMessage stdcall \
PROCTYPE ptDefWindowProc stdcall \
PROCTYPE ptSendMessage stdcall \
PROCTYPE ptMessageBox stdcall \
PROCTYPE ptExitProcess stdcall \
extrn GetModuleHandleA :ptGetModuleHandle
extrn LoadIconA :ptLoadIcon
extrn LoadCursorA :ptLoadCursor
extrn RegisterClassExA :ptRegisterClassEx
extrn LoadMenuA :ptLoadMenu
extrn CreateWindowExA :ptCreateWindowEx
extrn ShowWindow :ptShowWindow
extrn UpdateWindow :ptUpdateWindow
extrn GetMessageA :ptGetMessage
extrn TranslateMessage :ptTranslateMessage
extrn DispatchMessageA :ptDispatchMessage
extrn SetMenu :ptSetMenu
extrn PostQuitMessage :ptPostQuitMessage
extrn DefWindowProcA :ptDefWindowProc
extrn SendMessageA :ptSendMessage
extrn MessageBoxA :ptMessageBox
extrn ExitProcess :ptExitProcess
classTitle db ‘Menu demo’, 0
wndTitle db ‘Demo program’, 0
msg_open_txt db ‘You selected open’, 0
msg_open_tlt db ‘Open box’, 0
msg_save_txt db ‘You selected save’, 0
msg_save_tlt db ‘Save box’, 0
Start: call GetModuleHandleA, 0 ; не обязательно, но желательно
sub esp,SIZE WndClassEx ; отведём место в стеке под структуру
mov [(WndClassEx esp).cbSize],SIZE WndClassEx
mov [(WndClassEx esp).style],CS_HREDRAW or CS_VREDRAW
mov [(WndClassEx esp).lpfnWndProc],offset WndProc
mov [(WndClassEx esp).cbWndExtra],0
mov [(WndClassEx esp).cbClsExtra],0
mov [(WndClassEx esp).hInstance],eax
call LoadIconA, 0, IDI_APPLICATION
mov [(WndClassEx esp).hIcon],eax
call LoadCursorA, 0, IDC_ARROW
mov [(WndClassEx esp).hCursor],eax
mov [(WndClassEx esp).hbrBackground],COLOR_WINDOW
mov [(WndClassEx esp).lpszMenuName],MyMenu
mov [(WndClassEx esp).lpszMenuName],0
mov [(WndClassEx esp).lpszClassName],offset classTitle
mov [(WndClassEx esp).hIconSm],0
call RegisterClassExA, esp ; зарегистрируем класс окна
add esp,SIZE WndClassEx ; восстановим стек
call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style
offset classTitle, \ pointer to registered class name
offset wndTitle,\ pointer to window name
WS_OVERLAPPEDWINDOW, \ window style
CW_USEDEFAULT, \ horizontal position of window
CW_USEDEFAULT, \ vertical position of window
CW_USEDEFAULT, \ window width
CW_USEDEFAULT, \ window height
0, \ handle to parent or owner window
0, \ handle to menu, or child-window identifier
[hInst], \ handle to application instance0 ; pointer to window-creation data
call LoadMenu, hInst, MyMenu
call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style
offset classTitle, \ pointer to registered class name
offset wndTitle, \ pointer to window name
WS_OVERLAPPEDWINDOW, \ window style
CW_USEDEFAULT, \ horizontal position of window
CW_USEDEFAULT, \ vertical position of window
CW_USEDEFAULT, \ window width
CW_USEDEFAULT, \ window height
0, \ handle to parent or owner window
eax, \ handle to menu, or child-window identifier
[hInst], \ handle to application instance0 ; pointer to window-creation data
call ShowWindow, eax, SW_SHOW ; show window
call UpdateWindow, [hWnd] ; redraw window
call LoadMenuA, [hInst], MyMenu
call SetMenu, [hWnd], eax
call GetMessageA, offset msg, 0, 0, 0
call TranslateMessage, offset msg
call DispatchMessageA, offset msg
exit: call ExitProcess, 0
public stdcall WndProc
proc WndProc stdcall
arg @@hwnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword
call PostQuitMessage, 0
call DefWindowProcA, [@@hwnd], [@@msg], [@@wPar], [@@lPar]
call SendMessageA, [@@hwnd], WM_CLOSE, 0, 0
@@open: mov eax, offset msg_open_txt
mov edx, offset msg_open_tlt
@@save: mov eax, offset msg_save_txt
mov edx, offset msg_save_tlt
@@mess: call MessageBoxA, 0, eax, edx, MB_OK
Комментарии к программе
Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32 . Конечно их (а также описание констант и структур из API Win32 ) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно.
Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1) . В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx . Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном.
Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции – наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле.
Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp) . Именно это продемонстрировано при заполнении структуры WndClassEx . Выделение места в стеке (фрейма) делается простым перемещением esp :
sub esp,SIZE WndClassEx
Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека. При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать “кучи” (heap) или виртуальную память ( virtual memory ).
Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений.
Мне достаточно редко приходилось серьёзно заниматься разработкой макроопределений при программировании под DOS . В Win32 ситуация принципиально иная. Здесь грамотно написанные макроопределения способны не только облегчить чтение и восприятие программ, но и реально облегчить жизнь программистов. Дело в том, что в Win32 фрагменты кода часто повторяются, имея при этом не принципиальные отличия. Наиболее показательна, в этом смысле, оконная и/или диалоговая процедура. И в том и другом случае мы определяем вид сообщения и передаём управление тому участку кода, который отвечает за обработку полученного сообщения. Если в программе активно используются диалоговые окна, то аналогичные фрагменты кода сильно перегрузят программу, сделав её малопригодной для восприятия. Применение макроопределений в таких ситуациях более чем оправдано. В качестве основы для макроопределения, занимающегося диспетчеризацией поступающих сообщений на обработчиков, может послужить следующее описание.
macro MessageVector message1, message2:REST
dd offset @@&message1
@@VecCount = @@VecCount + 1
macro WndMessages VecName, message1, message2:REST
label @@&VecName dword
MessageVector message1, message2
cmp eax,[dword e cx * 8 + offset @@&VecName ]
jmp [dword e c x + offset @@&VecName + 4]
@@default: call DefWindowProcA, [@@hWnd], [@@msg], [@@wPar], [@@lPar]
@@ret_false: xor eax,eax
Комментарии к макроопределениям
При написании процедуры окна Вы можете использовать макроопределение WndMessages , указав в списке параметров те сообщения, обработку которых намерены осуществить. Тогда процедура окна примет вид:
proc WndProc stdcall
arg @@hWnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword
WndMessages WndVector, WM_CREATE, WM_SIZE, WM_PAINT, WM_CLOSE, WM_DESTROY
; здесь обрабатываем сообщение WM_CREATE
; здесь обрабатываем сообщение WM_SIZE
; здесь обрабатываем сообщение WM_PAINT
; здесь обрабатываем сообщение WM_CLOSE
; здесь обрабатываем сообщение WM_DESTROY
Обработку каждого сообщения можно завершить тремя способами:
— вернуть значение TRUE , для этого необходимо использовать переход на метку @@ret_true;
— вернуть значение FALSE, для этого необходимо использовать переход на метку @@ret_false;
— перейти на обработку по умолчанию, для этого необходимо сделать переход на метку @@default.
Отметьте, что все перечисленные метки определены в макро WndMessages и Вам не следует определять их заново в теле процедуры.
Теперь давайте разберёмся, что происходит при вызове макроопределения WndMessages . Вначале производится обнуление счётчика параметров самого макроопределения (число этих параметров может быть произвольным). Теперь в сегменте данных создадим метку с тем именем, которое передано в макроопределение в качестве первого параметра. Имя метки формируется путём конкатенации символов @@ и названия вектора. Достигается это за счёт использования оператора & . Например, если передать имя TestLabel , то название метки примет вид: @@TestLabel . Сразу за объявлением метки вызывается другое макроопределение MessageVector , в которое передаются все остальные параметры, которые должны быть ничем иным, как списком сообщений, подлежащих обработке в процедуре окна. Структура макроопределения MessageVector проста и бесхитростна. Она извлекает первый параметр и в ячейку памяти формата dword заносит код сообщения. В следующую ячейку памяти формата dword записывается адрес метки обработчика, имя которой формируется по описанному выше правилу. Счётчик сообщений увеличивается на единицу. Далее следует рекурсивный вызов с передачей ещё не зарегистрированных сообщений, и так продолжается до тех пор, пока список сообщений не будет исчерпан.
Сейчас в макроопределении WndMessage можно начинать обработку. Теперь существо обработки, скорее всего, будет понятно без дополнительных пояснений.
Обработка сообщений в Windows не является линейной, а, как правило, представляет собой иерархию. Например, сообщение WM_COMMAND может заключать в себе множество сообщений поступающих от меню и/или других управляющих элементов. Следовательно, данную методику можно с успехом применить и для других уровней каскада и даже несколько упростить её. Действительно, не в наших силах исправить код сообщений, поступающих в процедуру окна или диалога, но выбор последовательности констант, назначаемых пунктам меню или управляющим элементам ( controls ) остаётся за нами. В этом случае нет нужды в дополнительном поле, которое сохраняет код сообщения. Тогда каждый элемент вектора будет содержать только адрес обработчика, а найти нужный элемент весьма просто. Из полученной константы, пришедшей в сообщении, вычитается идентификатор первого пункта меню или первого управляющего элемента, это и будет номер нужного элемента вектора. Остаётся только сделать переход на обработчик.
Вообще тема макроопределений весьма поучительна и обширна. Мне редко доводится видеть грамотное использование макросов и это досадно, поскольку с их помощью можно сделать работу в ассемблере значительно проще и приятнее.
Для того, чтобы писать полноценные приложения под Win32 требуется не так много:
— собственно компилятор и компоновщик ( я использую связку TASM32 и TLINK32 из пакета TASM 5.0) . Перед использованием рекомендую “наложить” patch , на данный пакет. Patch можно взять на site www.borland.com или на нашем ftp сервере ftp.uralmet.ru.
— редактор и компилятор ресурсов (я использую Developer Studio и brcc32.exe );
— выполнить перетрансляцию header файлов с описаниями процедур, структур и констант API Win32 из нотации принятой в языке Си, в нотацию выбранного режима ассемблера: Ideal или MASM.
В результате у Вас появится возможность писать лёгкие и изящные приложения под Win32 , с помощью которых Вы сможете создавать и визуальные формы, и работать с базами данных, и обслуживать коммуникации, и работать multimedia инструментами. Как и при написании программ под DOS, у Вас сохраняется возможность наиболее полного использования ресурсов процессора, но при этом сложность написания приложений значительно снижается за счёт более мощного сервиса операционной системы, использования более удобной системы адресации и весьма простого оформления программ.
Приложение 1. Файлы, необходимые для первого примера