Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование на Ассемблере (masm32) под Windows. #3


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

c а й т   и з о б р е т а т е л е й   ф о н а р и к о в   н а    с о л н е ч н ы х   б а т а р е й к а х
  - Колонка ведущего
  - Машинные коды и ассемблер
  - Команды пересылки данных
  - Использование указателей в операциях пересылки данных
  - О других командах пересылки данных
  - Анонс!
  Меня часто спрашивают, что будет дальше в нашей рассылке. Представляю приблизительный тематический план.
  1. Введение
  2. Архитектура 32-разрядного процессора
  3. Команды пересылки данных
  4. Работа со стеком
  5. Управление ходом программы
  6. Макросы
  7. Двоичная арифметика
  8. Побитовые и логические операции
  9. Структуры данных
  10. Файловый ввод-вывод
  11. Окна, диалоги, элементы управления
  12. Динамически подключаемые библиотеки
  13. Обработка исключений
  14. Объектно-ориентированное программирование
  15. Использование Component Object Model (COM)
  16. Вычисления с плавающей точкой
  17. Управление простым графическим выводом
  18. 3D-графика в программах на ассемблере
  19. Разработка драйверов виртуальных устройств
  20. Техника дизассемблирования
  21. Вопросы оптимизации
  22. Вопросы защиты приложений

Ваш vkim
  Как заставить процессор выполнить какую-то работу? Надо дать ему соответствующие инструкции. Напишем программу:
01 .386
02 .model flat, stdcall
03 option casemap: none
04 .code
05 start:
06 main proc
07     db 0b8h, 01h, 00, 00, 00h
08     db 8bh, 0d8h
09     db 0c3h
10 main endp
11 end start
  Сохраните программу как mov.asm и cкомпилируйте с отладочной информацией, а затем запустите на исполнение. Вроде бы ничего не происходит, но давайте теперь просмотрим ее работу в отладчике. Запустите программу из командной строки db mov.exe. Если вы правильно настроили SoftICE, то отладчик всплывет и прервет исполнение программы на точке входа. В окне кода вы увидите набранную вами программу. Нажмите два раза клавишу F8. Обратите внимание, что cначала регистр EAX стал равен 00000001H, а потом и EBX. Нажмите F5 для выхода из отладчика.
  Мы написали программу в машинном коде. Инструкция B801000000 строке 7 устанавливает регистр EAX равным 00000001H. Инструкция 8BD8 в строке 8 пересылает значение регистра EAX в регистр EBX. Инструкция C3 в строке 9 завершает программу, возвращая управление операционной системе.
  Теперь посмотрим на нашу программу более внимательно. Запустите еще ее раз в отладчике. Когда отладчик всплывет, нажмите F3 для просмотра программы в смешанном режиме - с символьной информацией и дизассемблированном виде одновременно. Затем нажмите клавишу F8, чтобы исполнить первую инструкцию. В окне кода вы увидите подобный фрагмент:


  Что означают непонятные инструкции "MOV EAX, 00000001", "MOV EBX,EAX", "RET"? Для того чтобы ответить на этот вопрос, дадим отладчику команду CODE ON. Теперь этот же фрагмент в окне кода выглядит примерно так:


  Команда CODE ON/OFF включает и выключает показ машинного представления дизассемблированного кода. Теперь мы видим, что машинным инструкциям соответствуют ассемблерные инструкции. Оттрассируйте программу, обращая внимание на то, как изменяются значения регистров.   Конечно, процессор исполняет именно машинные инструкции. Инструкции составляются по определенным правилам и в виде последовательности байтов записываются в секцию кода. Вы заметили, что писать программы в машинных кодах очень тяжело. Поэтому были придуманы мнемонические конструкции, которые гораздо легче запомнить и которые специальная программа - ассемблер - переводит в машинный код. Кстати, вот почему язык ассемблера называется языком низкого уровня: ассемблерные инструкции имеют непосредственный и однозначный эквивалент машинных инструкций. В дальнейшем мы будем писать программы с помощью мнемонических инструкций ассемблера.
  Процессор имеет набор встроенных команд, комбинация которых собственно и составляет программу. Рассмотрим группу команд пересылки данных.   Когда мы хотим переслать данные из одного места в другое в программе, написанной на языке высокого уровня, мы просто использует оператор присваивания "=". Например, variable1 = 10. Для того, чтобы произвести эту операцию на языке ассемблера, необходимо воспользоваться специальной командой - MOV (от английского слова MOVe). Команда производит действие над операндами, которые записываются после нее. Левый операнд называется приемником (destination), правый - источником (source). Приемником может быть регистр или переменная, а источником может быть регистр, переменная или константа. Причем в одной команде переменные не могут выступать в качестве приемника и источника. Например:
mov EAX, 10H
mov EAX, ECX
mov mem32, EAX
mov mem32, 20H
mov EBX, mem32
mov mem32, mem32 - ошибка!
  Теперь мы можем переписать программу mov.asm, используя команду mov:
01 .386
02 .model flat, stdcall
03 option casemap: none
04 .code
05 start:
06 main proc
07 comment /*
08     db 0b8h, 01h, 00, 00, 00h
09 db 8bh, 0d8h
10 1db 0c3h
11 */
12      mov eax, 1h ; eax = 1h
13      mov ebx, eax ; ebx = eax
14      ret
15 endp
16 end start
  Как видите, программу и писать и читать стало намного проще. Кроме того, в программу можно добавлять свои комментарии, которые при трансляции игнорируются ассемблером. Я продемонстрировал два вида комментариев, которые поддерживает MASM. Для того чтобы закомментировать часть текста, я использовал директиву comment. Символ, который находится после нее, открывает комментарий. В данном случае это прямой слэш, но в этом качестве можно назначить практически любой символ (за исключением пробела, символа табуляции и некоторых других), букву или цифру. Чтобы закрыть комментарий, нужно вставить в текст такой же символ. Таким образом, весь текст, который расположен между символами "/" является комментарием. Я еще вставил звездочки, чтобы комментарий выглядел как в языке С, но они несущественны и их можно опустить. Также комментарии можно открывать с помощью точки с запятой. Тогда вся строка после точки с запятой до конца строки, будет являться комментарием. Если вы последовали моему совету и используете для написания программ редактор FAR с установленным плагином Colorer, то вы можете видеть, что Colorer выделил комментарии серым цветом.
  В строке 14 вы видите команду ret, которая нам уже встречалась. В первом варианте программы mov.asm мы записывали ее в машинном коде 0C3H, но теперь заменили мнемоническим (ассемблерным) эквивалентом.
  Скомпилируйте программу и выполните в отладчике, чтобы убедиться, что программа работает точно так же, как и предыдущий вариант.
  Довольно часто в программах надо, чтобы два объекта обменялись значениями. К примеру, нам нужно значение регистра EAX переслать в регистр ECX, а значение регистра ECX - в EAX. Мы могли бы написать:
mov EBX, EAX
mov EAX, ECX
mov ECX, EBX
  Но для этой распространенной операции имеется команда XCHG, которая занимает меньше места в памяти. Да и вообще использовать ее проще. В качестве операндов могут выступать регистры или переменные, но опять нельзя обменивать значения двух переменных в одной инструкции. Дополните в секцию кода в файле mov.asm строку "xchg eax, edx" и посмотрите на работу ее работу в отладчике.
  С этой командой связан один интересный факт. Допишите в файл mov.asm строку "xchg eax, eax", скомпилируйте и загрузите в отладчик. Нажмите один раз F8 и два раза F3, чтобы просмотреть дизассемблированный код. Инструкцию "xchg eax, edx" вы видите, но где "xchg eax, eax"? Вместо нее появилась команда NOP. Дело в том, что машинный код инструкции "xchg eax, eax" равен 90H. Но точно такой же код имеет команда NOP, означающая отсутствие операции. Встретив такую команду, процессор просто пробездельничает какое-то время - от 1 до трех тактов, в зависимости от модели процессора. В основном она используется в целях оптимизации кода. Таким образом, инструкцию 90Н отладчик дизассемблировал в то, что ему показалось ближе - команду NOP. Эта особенность инструкции "xchg eax, eax" документирована в руководстве 24319101 корпорации Intel.
  Вы знаете уже достаточно о регистрах, памяти и командах процессора, чтобы мы могли затронуть тему указателей. Возьмем пример из повседневной жизни. Я могу сказать "Здание МУРа" или "Петровка, 38". И то и другое обозначает одно и то же место на карте Москвы, только в первом случае это место я указал по имени, а во втором по адресу. Точно так же дело обстоит с переменными. Как я уже говорил, каждая ячейка памяти имеет свой адрес. Поэтому к ней можно обращаться как по имени, так и по адресу. Если адрес ячейки поместить в переменную или регистр, то в дальнейшем мы можем использовать их в качестве указателей.
  Напишем программу для демонстрации работы с указателями:
01 .386
02 .model flat, stdcall
03 option casemap: none
04 .data
05 var1 dword 10
06 .code
07 start:
08      mov eax, offset var1
09      mov eax, [eax]
10      ret
11 end start
  Сохраните ее как pointer.asm и откомпилируйте с отладочной информацией. Все в этой программе для вас уже знакомо, за исключением того, что происходит в строках 8-9. Ключевое слово offset в строке 8 означает "смещение", поэтому эту инструкцию можно прочитать так: "Поместить в регистр EAX смещение переменной var1". "Смещение" - это старый термин, наследие 16-битных процессоров и DOS. В статье "Архитектура 32-битного процессора" я уже говорил вам, что при программировании под DOS адрес вычисляется так: адрес сегмента плюс смещение относительно этого сегмента. 32- разрядные процессоры позволили операционным системам, в частности Windows, использовать плоскую модель памяти, в которой для адресации достаточно указать только смещение. Таким образом, инструкция в строке 8 загружает в EAX адрес переменной var1. То же самое можно проделать с помощью команды lea (load effective address), но без применения ключевого слова offset: lea eax, var1. Однако эта инструкция будет занимать память на 1 байт больше и исполняться за большее количество тактов, чем предыдущая инструкция. В строке 9 в регистр EAX пересылается значение, которое расположено по адресу, находящемуся в EAX. А так как в EAX у нас сейчас хранится адрес переменной val1, то эта инструкция устанавливает регистр EAX равным 10. Квадратные скобки, в которые заключен регистр EAX, как раз и означает, что требуется переслать не непосредственное значение, хранящееся в нем, а значение, на которое он указывает. Такая адресация называется косвенной. Имейте в виду, что для косвенной адресации можно использовать регистры общего назначения, но не переменные. Если вы, например, напишите так:
 .data
var1 dword 10
var2 dword 0
 .code
start:
    mov eax, offset var1
    mov var2, eax
    mov eax, [var2]
    ret
end start
то в итоге регистр EAX будет равен не 10, а 00404000H, то есть адресу, по которому находится переменная var1 в памяти. Дело в том, что квадратные скобки вокруг имени переменной ничего не значат, поэтому инструкции "mov eax, [var2]" и "mov eax, var2" полностью идентичны. Однако вы, конечно, понимаете, что адрес можно сохранить в переменной, а потом перед инструкцией косвенной адресации загрузить этот адрес из переменной в регистр общего назначения.

Расположение переменной var1 в памяти

  Выполните программу pointer в отладчике и обратите внимание, на то, как изменяется значение регистра EAX. Для чего нужны указатели? Николас Вирт - создатель языка Pascal - сказал, что программу составляют структуры данных и алгоритмы. Вам понадобится изучить и то, и другое. Наиболее эффективные алгоритмы возможно реализовать только с помощью указателей. Да и работа со структурами данных, как вы позже убедитесь, тоже требует знания концепции указателей.
  Мы будем еще много говорить об алгоритмах, структурах данных и указателях. Сейчас вы получили только первое представление об указателях. Не волнуйтесь, если тема указателей вам показалась сложной. Она только кажется такой, и вы в этом убедитесь, когда мы начнем применять указатели в реальных проектах. "Суха всякая теория, но вечно зелено древо жизни".
  По правде сказать, процессор имеет намного больше команд пересылки данных, чем мы рассмотрели в этой статье. Однако некоторые из них не применяются прикладными программистами на ассемблере под Windows. Например, команды чтения и записи в порт - in, out. Другие настолько важны, что я решил рассмотреть их в отдельной главе. В частности, команды работы со стеком.
  Некоторые команды, незнание которых, на мой взгляд, не является критичным, вообще рассматриваться не будут, хотя их применение в прикладных программах вполне оправдано. Это касается не только команд пересылки данных. Если в чужих программах вам встретится незнакомая команда, то для справки используйте Microsoft MASM Programmer's Guide или руководства фирмы Intel.
  Работа со стеком:
  - команды работы со стеком;
  - вызов функций Win32 API
Рассылки Subscribe.Ru
Низкоуровневое программирование для дZeнствующих
Программирование на Ассемблере под Win32
Низкоуровневое программирование для дZeнствующих (FAQ)
Copyright 2001 №vkimHI-TECH. All Rights Reserved And Reversed ;).

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное