Предисловие
С этой статьи я начинаю цикл статей «Изучаем Ассемблер». Основная идея состоит в том, что я знаю ассемблер не лучше вас и я как бы изучаю ассемблер вместе с вами. Предполагается, что вы уже хотя бы немного знакомы с любым языком программирования. Итак, начнем!
Основы
.386
.model flat, stdcall
option casemap :none
include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\masm32.inc
include \MASM32\INCLUDE\gdi32.inc
include \MASM32\INCLUDE\user32.inc
include \MASM32\INCLUDE\kernel32.inc
includelib \MASM32\LIB\masm32.lib
includelib \MASM32\LIB\gdi32.lib
includelib \MASM32\LIB\user32.lib
includelib \MASM32\LIB\kernel32.lib
wsprintfA PROTO C :DWORD,:VARARG
wsprintf equ
.data
bufferforstring db 10 dup(0)
titlestring db "Результат",0
szformat db "%u",0
.code
start:
mov eax,7 ;заносим в EAX 5
mov ecx,30 ;заносим в ECX 25
mul ecx ;умножаем EAX на ECX
mov ebx,eax ;заносим содержимое EAX в EBX
mov eax,40 ;заносим в EAX 40
mov ecx, 2 ;заносим в ECX 2
mul ecx ;заносим в ECX 2
sub ebx,eax ;отнимаем от EBX EAX
invoke wsprintf,addr bufferforstring,addr szformat,eax
invoke MessageBox,0,ADDR bufferforstring,ADDR titlestring,MB_OK ;выводим резульат
invoke ExitProcess,0
end start
Какой компилятор выбрать? Компиляторов ассемблера существует очень
много. Самые популярные из них: MASM, TASM, FASM, NASM. Большинство
примеров будет на MASM’е, но большинство компиляторов мало чем
отличаются по синтаксису. Скачать MASM можно на http://www.masm32.cjb.net/.
Вместе с MASM поставляется редактор для него. Перед тем, как
компилировать программу нужно ее сохранить(File->Save). Теперь можно
откомпилировать(Project->Build All). Чтобы запустить вашу программу
нажмите Project->Run Program.
Теперь о структуре программы. Вот «скелет» стандартной программы:
.model flat, stdcall
option casemap :none
;подключение библиотек,необходимых нам
include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\masm32.inc
include \MASM32\INCLUDE\gdi32.inc
include \MASM32\INCLUDE\user32.inc
include \MASM32\INCLUDE\kernel32.inc
includelib \MASM32\LIB\masm32.lib
includelib \MASM32\LIB\gdi32.lib
includelib \MASM32\LIB\user32.lib
includelib \MASM32\LIB\kernel32.lib
;раздел, где объявляются все константы
.const
;раздел, где объявляются переменные, уже имеющие какое-то значение
.data
;раздел, где объявляются переменные, еще не имеющие значения
.data?
.code
start: ;с этого слова начинается код программы
invoke ExitProcess,0
end start ;с этого слова заканчивается код программы
Советую сохранить этот «скелет» в отдельном файле для удобства. В большинстве наших программ мы будем использовать функции WinAPI. Давайте напишем программу, которая будет выводить всплывающее окно с каким-либо сообщением. Для вывода этого окна мы будем пользоваться функцией MessageBox:
.model flat, stdcall
option casemap :none
include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\masm32.inc
include \MASM32\INCLUDE\gdi32.inc
include \MASM32\INCLUDE\user32.inc
include \MASM32\INCLUDE\kernel32.inc
includelib \MASM32\LIB\masm32.lib
includelib \MASM32\LIB\gdi32.lib
includelib \MASM32\LIB\user32.lib
includelib \MASM32\LIB\kernel32.lib
.data
message db "Sources.RU Magazine",0 ;переменная с сообщением
mestitle db " Sources.RU Magazine ",0 ;переменная с заголовком
.code
start:
invoke MessageBox,0,ADDR message,ADDR mestitle,MB_OK ;выводим сообщение
invoke ExitProcess,0 ;завершаем программу
end start
Как можно увидеть в примере, все API-функции вызываются с помощью
команды invoke. Сначала нужно написать имя функции, а потом все ее
параметры, то есть:
invoke имя_функции, [параметр1, параметр2, параметр3, ... ...]
У многих сразу возникнет вопрос: где взять справочник по API? Я отвечу: а искать не пробовали? Но если все-таки не нашли, то я пользуюсь API-Guide(http://www.allapi.net/). Это справочник по API для VB, но он подходит и для ассемблера. А вообще сети их полно, просто нужно хорошо поискать.
Математика
На любом языке программирования высокого уровня, чтобы сложить два числа нужно сделать что-то вроде этого:
переменная=число1 + число2
В ассемблере все по-другому. Чтобы сложить, вычесть, умножить или
разделить два числа он использует не переменные, а регистры процессора.
Регистр процессора - это как бы переменные, которые находятся в
процессоре. Вот таблица с основными регистрами:
EAX используется для сохранения числа и для счета
ECX используется для счета
EDX сохраняет остаток от деления
EBX может быть использован для всего
ESI используется для управления строками
EDI также используется для управления строками
И таблица с основными операторами, которые используются для счета:
MOV EAX,число1 копирует число1 в регистр EAX
ADD EAX,число1 прибавляет к содержимому EAX число1
SUB EAX,число1 отнимает от содержимого EAX число1
MUL регистр1 используется для умножения, умножает
содержимое регистр1 на число, которое всегда хранится в EAX и сохраняет
результат в EAX
DIV регистр1 Делит содержимое EAX на содержимое регистр1 и сохраняет результат в EAX
Вообще все арифметические операции в ассемблере производятся между
регистром и аккумулятором, то есть регистром EAX. В операторах MUL и
DIV только один параметр, потому что ассемблер сразу знает, что второе
число хранится в EAX. Но почему же в операторах SUB и ADD два
параметра, а не один? Об этом мы поговорим позже после того, как
рассмотрим пример в конце статьи.
Теперь немного подробнее об арифметике. В регистре EDX всегда хранится
остаток от деления и вы должны запомнить, что перед делением нужно
обязательно очистить этот регистр. Это можно сделать так:
MOV EDX,0
или так:
XOR EDX,EDX
Между этими двумя способами нет никакой разницы, но второй способ быстрее. Привожу пример программы, вычисляющей число 7*30-40*2: