Меню сайта

Урок 1. Начало программирования на ассемблере для Windows

Создание полноценного приложения для Windows,
использующего все возможности данной системы,-
довольно сложная задача. В то же время возможно
написание каких-то небольших приложений или
подпрограмм на ассемблере.

Создание приложение для Windows в общем случае имеет
больше стадий чем для Dos. Это объясняется тем, что при
компоновке Windows-программы в нее включается не
только код исходных модулей, но и ресурсы. Итак создание
программы для Windows должно содержать следующие
этапы :

разработка исходного текста программы
создание ресурсов, которые используются
программой
создание текстового файла описания используемых
ресурсов
создание текстового файла описания программного
модуля
компиляция и компоновка программы с
использованием файла описания программного
модуля
компиляция файла ресурсов и включение его в
готовый исполняемый файл

В данной статье будет использоваться компилятор и
компоновщик фирмы Borland — Turbo Assembler и Turbo Link.
Конечно возможно некоторым покажеться странным
использование данного продукта, но могу вас уверить на
мой взгляд удобнее инструмента для программирования на
ассемблере для Windows вы не найдете. Я использую
версию Turbo Assembler 5.0.

В этой статье я также не буду рассказывать о создании
ресурсов и присоеденении их к исполняемому файлу, мы
рассмотрим только простой пример программы, и
попробуем найти какие-то сходства с программированием
под Dos.

Итак маленький пример:

Файл hello.asm

.model large, WINDOWS PASCAL
— Подключаем файл где описаны константы (типа «MB_OK»,
«MB_ICONEXCLAMATION»)
include windows.inc
— Говорим что будем использовать функцию API MessageBox
extrn MESSAGEBOX:proc
— Сегмент данных
.data
— Пустое место для информации Program Manager’a
freespace db 16 dup(0)
— Заголовок диалогового окна
lpszTitle db ‘Generic Sample Assembly Application’,0
— Текст диалогового окна
lpszText db ‘Hello World !’,0
— Сегмент кода
.code
— Наш старый «добрый» start (На самом деле WinMain)
start:
— Инициализируем задачу и получаем входные параметры
call INITTASK
or ax,ax
— Если инициализация прошла успешно
jnz @@OK
— Если ошибка
jmp @@Fail
@@OK:
— Сохраняем HINSTANCE
mov hInstance,di
— Инициализируем приложение
call INITAPP,hInstance
or ax,ax
jnz @@InitOK
@@Fail:
— Если инициализация завершилась неудачно
mov ax, 4CFFh
int 21h
@@InitOK:
— Выводим на экран диалоговое окно
call MESSAGEBOX,0,ds offset lpszText,ds offset
lpszTitle,MB_OK+MB_ICONEXCLAMATION
— Хе-хе а вот и выход
mov ax,4c00h
int 21h
end start

Как видите разработка простых приложений для Windows
практически не отличается от программирования для Dos.
Однако есть маленькие особенности которые мы сейчас
рассмотрим:

.model large, WINDOWS PASCAL

Про эту строчку много говорить и не надо, но она имеет
одну особенность. Система Windows и ее функции
используют алгоритм «прямой» передачи параметров через
стэк, как в языке Pascal. Поэтому мы и вводим этот
модификатор в директиву modal.

include windows.inc

Подключение файлов определений тоже не представляет
ничего нового, но если вы не хотите самостоятельно
перекапывать файлы Windows и выяснять какие константы
имеют какие значение просто включите эту строчку в вашу
программу.

extrn MESSAGEBOX:proc

Наверно одна из самых существенных строк этого
примера. Она говорит компилятору, что мы будем
использовать внешнюю функцию MessageBox. Я думаю у
вас не возникает никаких сомнений — существование этой
функции в Windows API.

freespace db 16 dup(0)

Зачем в начале сегмента данных стоят 16 байт с нулями ?
Для приложений Win16 эти 16 байт являются
обязательным условием нормальной работы программы.
При загрузке Вашего приложения Windows заменяет эти 16
байт своей информацией. Для более подробного
разъяснения этой проблеммы обратитесь к книге Мэта
Патрика (Matt Patrick) «Внутри Windows» («Windows
Internals»), 1993, Addison Wesley.

Всем известно, что каждое приложение для Windows
начинается с WinMain. Через параметры переданные этой
функции можно было получить командную строку,
идентификатор предидущего экземпляра программы и так
далее и тому подобнее. Немного сложнее процедура
инициализации приложения происходит в Assembler.

Первым шагом в инициализации приложения под
Windows будем считать вызов функции INITTASK. После
выполнения эта функция возвращает:

При успешном завершении AX=1
DX содержит nCmdShow, то есть параметр,
указывающий на стиль просмотра окна
ES:BX содержит адрес командной строки
SI содержит идентификатор ранее загруженной
программы hPrevInstance
DI содержит идентификатор загруженной программы
hInstance

После обработки входных параметров нужно
инициализировать приложение,
используя функцию
INITAPP. Единственным параметром этой функции
служит идентификатор приложения hInstance.Функция
INITAPP возвращает в AX=1 если приложение успешно
проинициализировано.

Для того, что бы посмотреть, что же эта программа умеет
делать вы должны создать еще один файл .def

Файл hello.def

NAME HELLO
EXETYPE WINDOWS

CODE MOVABLE DISCARDABLE
DATA MOVABLE MULTIPLE DISCARDABLE
STACKSIZE 5120
HEAPSIZE 4096
DESCRIPTION ‘Copyright(C) 1999 by BlackWolf’

Содержимое этого файла мы рассмотрим подробнее :

NAME HELLO

Имя получаемого модуля «HELLO».

EXETYPE WINDOWS

Тип получаемого модуля — исполняемый файл Windows.

CODE MOVABLE DISCARDABLE

Ключевым словом CODE мы можем выставлять
параметры нашего кодового сегмента. О возможных
параметрах сегмента смотрите ниже.

DATA MOVABLE MULTIPLE DISCARDABLE

Тоже самое только для сегмента данных.

STACKSIZE 5120

Устанавливаем размер стэка равным 5120.

HEAPSIZE 4096

Устанавливаем размер «кучи» равным 4096.

DESCRIPTION ‘Copyright(C) 1999 by BlackWolf’

Директива DESCRIPTION вставляет в файл кода
указанный текст и обычно применяется для примечания о
авторских правах.

Теперь рассмотрим параметры сегментов. Они могут быть
различных типов и в принципе формат инструкций CODE
и DATA выглядит таким образом:

CODE [FIXED|MOVABLE]
[DISCARDABLE|NONDISCARDABLE]
[PRELOAD|LOADONCALL]

Скобки в данном случае показывают, что элемент не
обязателен. Вертикальная черта означает «ИЛИ». Опции
директивы CODE означают:

FIXED — Сегмент имеет фиксированный адрес.
MOVABLE — Сегмент может быть перемещен, что бы
освободить пространство в памяти.
DISCARDABLE — Сегмент может быть выгружен,
чтобы освободить пространство.
NONDISCARDABLE — Сегмент не может быть
выгружен.
PRELOAD — Сегмент загружается в память при
запуске приложения.
LOADONCALL — Сегмент загружается в память
только когда происходит обращение к некоторому его
элементу.

DATA [NONE|SINGLE|MULTIPLE]
[READONLY|READWRITE] [PRELOAD|LOADONCALL]
[SHARED|NONSHARED]

Скобки в данном случае показывают, что элемент не
обязателен. Вертикальная черта означает «ИЛИ». Опции
директивы DATA означают:

NONE — Cегмент данных отсутствует (применяется
только для DLL).
SINGLE — Единственный сегмент данных,
разделяемый всеми процессами (применяется по
умолчанию для DLL).
MULTIPLE — Несколько сегментов данных
(применяется по умолчанию для исполняемых
файлов).
READONLY — Данные в сегменте можно только
читать, но не изменять.
READWRITE — Данные в сегменте можно как читать,
так и изменять.
PRELOAD — Сегмент заранее автоматически
загружается в память.
LOADONCALL — Сегмент загружается в память при
обращении к нему.
SHARED — Одна копия сегмента данных разделяется
между всеми процессами (применяется по
умолчанию для 16-битных DLL)
NONSHARED — Отдельная копия сегмента данных
загружается для каждого процесса (применяется по
умолчанию для приложений и 32-битных DLL).

Существует также еще одна очень интересная директива —
STUB. Она вставляет в файл .EXE кода для Windows
программу Dos. Эта директива никогда не применяется для
библиотек. Если вы не указываете директиву STUB Turbo
Assembler вставит в ваш исполняемый файл программу
WINSTUB.EXE. Эта программа является заглушкой для
запуска Windows приложений под Dos. При запуске вашего
приложения под Dos вы получите примерно такое
сообщение :

This program must be run under Microsoft Windows.

Используя данную директиву вы можете написать
программу которая будет прекрасно работать как под Dos
так и под Windows.

После написания файлов hello.asm и hello.def вы должны
скомпилировать и скомпоновать программу используя
такие параметры для Turbo Assembler:

tasm hello.asm

После компиляции вы должны получить .OBJ файл.

tlink hello,hello,,import.lib(*),hello

После компоновки вы получите готовый исполняемы файл
Windows. 🙂

(*) Если вы используете импорт функций API или импорт
функций из других DLL, вы обязательно должны включить
в компоновку библиотеку import.lib для 16-битных
приложений, или библиотеку import32.lib для 32-битных
приложений.

На этом я закончу первую вводную статью о
программировании на ассемблере для Windows.


Меню раздела
Блок