Урок 1.

Зачем нужен язык ассемблера? Ответ на данный вопрос содержал бы в себе рассуждение о том, что ассемблер может понадобиться для оптимизации кода программ, написания драйверов, трансляторов, программирования некоторых внешних устройств и т.д.

Инструментарий.

И так Вам понадобится пакет MASM32 версии 6.14 или выше. Из этого пакета нам пригодятся программы ML.EXE (транслятор), LINK32.EXE (компоновщик) или просто LINK.EXE, RC.EXE (транслятор ресурсов) и набор библиотек для программирования в Windows.

И так если у Вас есть программа prog1.asm и ресурс res1.rc, то для того, чтобы получить исполняемый модуль достаточно выполнить следующую последовательность команд.

  ML /c /coff prog1.asm
  RC res1.rc
  link32  /subsystem:windows prog1.obj res1.res

Дабы не усложнять рассмотрение я не буду пока разъяснять записанные строки, но замечу, что если ресурсы Вы не используете, то из трех команда, естественно останется две.

Что есть программирование в Windows?

Взаимодействие прикладной программы с операционной системой осуществляется посредством функций API (Application Program Interface) и сообщений. Начнем с вызова функций API.

Количество функций в операционной системе превышает две тысячи. Для того чтобы быстро ориентироваться в этом безбрежном море возможностей следует запастись каким-нибудь файлом помощи. От Borland, Microsoft или каким-нибудь справочником, коих в Интернете сейчас не счесть.

При описании API-функции используется Си-нотация. Рассмотрим, к примеру, описание функции MessageBox, служащей для выдачи оконного сообщения.

int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

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

Смысл параметров: hWnd – дескриптор окна, в котором будет появляться окно-сообщение, lpText – текст, который будет появляться в окне, lpCaption – текст в заголовке окна, uType – тип окна, в частности можно определить количество кнопок выхода. Теперь о типах параметров. Все они в действительности 32-битные целые числа: HWND - 32-х битное целое, LPCTSTR – 32-х битный указатель на строку, UINT - 32-х битное целое. Вот Вам существенное преимущество ассемблера. Не нужно размышлять о совместимости различных типов и указателей, а надо знать лишь размер операнда. Причем в большинстве случаев размер операнда будет 32-битным.

Следующий вопрос заключается в том, как указать значения параметров при вызове функции. Здесь все также просто – параметры помещаются в стек. В большинстве случаев действует «золотое» правило: «слева направо - снизу вверх». Это правило для функций API действует почти всегда (об исключениях будет сказано особо в последующих уроках). Поверьте, нет большего удовольствия, как собственноручно поместить параметры в стек. И так, что из себя представляет вызов функции MessageBox. Вот эти строки:

MB_OK equ 0
.
.
STR1        DB  “Не верный ввод! “,0 
STR2        DB  “Сообщение об ошибке.”,0
HW          DWORD ?
.
.
PUSH        MB_OK
PUSH        OFFSET  STR1
PUSH        OFFSET  STR2
PUSH        HW
CALL        MessageBoxA@16
.
.

Ну, надеюсь, первый вопрос у Вас возникнет по поводу MessageBoxA@16 Откуда этот суффикс: A@16 Во-первых, здесь целых два суффикса. Суффикс <> означает, что операнды данной функции записываются в ASCII кодировке. Часть функций API имеет два прототипа: с суффиксом A(ASCII) и суффиксом W(UNICODE). Второй суффикс @16 отражает просто особенность библиотек Microsoft. Число 16 означает количество байтов, помещаемых в стек, перед вызовом функции.

И так, отвечая на поставленный вопрос: «что есть программирование в Windows?» - можно сказать, что это написание текста, в значительной степени состоящего из вызовов функций API.

А вот и программа.

Чтобы пока не загромождать статью, первую программу напишем в так называемом консольном режиме. Назовем нашу первую программу consol1.asm. Для того чтобы получить из этого файла исполняемый модуль нужно выполнить следующие команды:

  ML /c /coff consol1.asm
  link32 /subsystem:console consol1.obj
Программа, работающая в консольном режиме, обладает более простой структурой, поэтому мы начинаем именно с нее. У вас не должно быть сомнений, что это какой-то ущербный режим. Это такое же 32-х битное приложение, только для работы такого приложения операционная система выделяет специальное окно – консоль. Наиболее известной консольной программой является FAR.

А вот и сама программа. Мы комментируем ее, но, разумеется, не все в ней понятно. Будьте спокойны и терпеливы. Постепенно все разъяснится.

;программа consol1.asm
.386P
;плоская модель
.MODEL FLAT, stdcall
;константа для функции GetStdHandle@4
STD_OUTPUT_HANDLE  equ -11
;прототипы внешних процедур
EXTERN    GetStdHandle@4:NEAR
EXTERN    WriteConsoleA@20:NEAR
EXTERN    ExitProcess@4:NEAR 
EXTERN    lstrlen@4:NEAR 
;директивы компоновщику для подключения библиотек
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
;---------------------------------------------
;сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
;строка в DOS-ой кодировке
      STR1 DB "Консольное приложение",0
;сюда помещается количество выведенных символов
      LENS DD ? 
      RES  DD 0
_DATA ENDS
;сегмент кода
_TEXT  SEGMENT DWORD PUBLIC USE32 'CODE'
START:
;получить дескриптор вывода
      PUSH STD_OUTPUT_HANDLE
      CALL GetStdHandle@4
      ;запомнить, полученный дескриптор в EBX
      MOV  EBX,EAX
      ;получить длину строки
      PUSH OFFSET STR1
      CALL lstrlen@4
      ;вывести строку
      PUSH OFFSET RES     ;резерв, всегда 0
      PUSH OFFSET LENS   ;выведено символов
      PUSH EAX                ;длина строки
      PUSH OFFSET STR1   ;адрес строки
      PUSH EBX                ;HANDLE вывода
      CALL WriteConsoleA@20
      ;вызвать функция выхода в операционную систему
      PUSH 0
      CALL ExitProcess@4
_TEXT ENDS
END START

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

Функции возвращают свое значение в регистре EAX.

В начале программы мы перечисляем функции API, которые будем использовать. Обычно для этой цели используют заголовочные файлы из пакета MASM. Мы отказываемся от этой практики чисто из педагогических соображений.

Директива includlib сохраняется в объектном файле и заставляет компоновщик автоматически подключать указанные библиотеки. Директива весьма удобна. Не будь ее, нам пришлось бы указывать библиотеки непосредственно в командной строке компоновщика.

Тот, кто программировал под MS DOS, знает, что такое сегменты. В нашей программе Вы тоже увидите два сегмента – данных и кода. Однако здесь это не то, что в DOS. Правильнее было бы назвать это секциями. Но больше я Вам ничего об этом не скажу. Ждите следующих уроков.

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

Назад