Основы.
Когда-то очень давно программы писались в машинных кодах.В шестнадцитиричном счислении это выглядит так:
b409ba0901cd21cd2048656c6c6f20776f726c642124
Затем
люди решили приблизить язык програмирования к людскому(раз люди
тупые,значит машина должна поумнеть-так думали корифеи программерской
науки) и изобрели Ассемблер.Вот эта же прога на Ассемблере FASM.
; fasm example of writing 16-bit COM program
org 100h ; code starts at offset 100h
use16 ;
use 16-bit code
;display_text = 9
mov ah,9;display_text
mov dx,hello
int 21h
int 20h
hello db 'Hello world!',24h
Конечно люди на этом не остановились.Языков програмирования и их диалектов становитя всё больше и больше.Из
них наибольшее распространение получил С.Кроме того до сих пор живы
ADA,FORTRAN,PASCAL.Появились C++,JAVA,C# и другие приблуды.Но до сих
пор есть вещи,которые может сделать только Ассемблер.Поэтому он до сих
пор востребован и несмотря на то что большинство старых языков давно
забыты,он и теперь нужен и о нём помнят (Конечно некоторые используют
его для создания вирусов,кейгенов,кряков,модификаторов и иных гадких
програм,но большинство использует его в мирных целях.)
Коды ассемблера представляют непосредственно команды процессора, которые он
может выполнять. Для примера:
add eax, edx
Эта инструкция, add, складывает вместе два значения. Eax и
edx называются регистрами, они могут содержать значения, которые хранятся
внутри процессора. Этот код будет преобразован в 66 03 C2
(шестнадцатиричный код). Процессор читает эти коды, и выполняет операцию, которую они представляют. Языки
высокого уровня, например С, при компиляции преобразовывается в ассемблер, а
ассемблер в двоичный код.
Коды операции (опкоды)
Программы Ассемблера пишутся в опкодах. Опкоды это команды, которые
понимает процессор. Например:
ADD eax, 3
ADD имеет 2 операнда. В в этом случае, это источник и приемник. Он(Опкод ADD) добавляет
значение источника к значению приемника и сохраняет результат в приемнике.
Операнды могут быть разных типов: регистры, ячейки памяти, непосредственные
значения .Подробней изучить опкоды вы сможете прочитав Fasmbook от i2k.
Регистр
Регистр - это область памяти в процессоре.
Значит так, регистры:
eax/ax/ah/al,ebx/bx/bh/bl,edx/dx/dh/dl,ecx/cx/ch/cl,ebp/bp,esi/si,edi/di,esp/sp -это ячейки памяти,которые могут содержать следующее:
- операнды логических и арифметических операций
- компоненты адреса
- указатели на ячейки памяти
Регистры классифицируются по размерам и назначению.
Размеры регистров:
Регистры
бывают разных размеров : 8 бит, 16 бит, 32 бит (и больше ).
Вы можете использовать регистры как 8 и 16 размером бит.
Некоторые
регистры являются частью других; например, если EAX содержит значение 12345678h, вот то, что содержат другие регистры.
EAX |
12 |
34 |
56 |
78 |
AX |
12 |
34 |
56 |
78 |
AH |
12 |
34 |
56 |
78 |
AL |
12 |
34 |
56 |
78 |
регистры ax, ah, al - части регистра eax. Eax это 32-битный
регистр , ax содержит младшие 16 бит (2 байта)
регистра eax, ah содержит старший байт регистра ax, и al
содержит младший байт регистра ax. Регистр ax - 16 битный,
al и ah - 8 битные. Получается:
eax = 12345678h
(32-бит)
ax = 5678h (16-бит)
ah = 56h (8-бит )
al = 78h (8-бит )
Назначение регистров:
Универсальные регистры:
Большинство регистров вы можете использовать как хотите (например я люблю хранить что-то в
ebx).Вот они:
eax/ax/ah/al
ebx/bx/bh/bl
edx/dx/dh/dl
ecx/cx/ch/clВы спросите как их можно ипользовать в своих программах?Вот так:
mov eax,32 -Помещаем в eax 32.
mov ebx,20 -Помещаем в ebx 20.
sub eax,ebx -Из eax вычитаем ebx.Теперь в eax у нас 12(32-20=12).
Стоит
отметить что в некоторых командах использование какого-то из этих
регистров обязательно.Вот назначения этих регистров для некоторых
команд.
eax/ax/ah/al -(Accumulator register) Аккумулятор.
Применяется для хранения промежуточных данных.
ebx/bx/bh/bl - (Base register)Базовый регитр.
Применяется для хранения базового адреса некоторого обьекта в памяти.
edx/dx/dh/dl -(Data register) - Регистр данных.Применяется для хранения промежуточных данных. В некоторых командах
его использование обязательно, для некоторых команд это происходит неявно.
ecx/cx/ch/cl-(Count
register) Регистр-счетчик. Применяется в командах, производящих
некоторые повторяющиеся действия.Его использование зачастую неявно и
скрыто в алгоритме работы соответствующей команды.Например команда
оргинизации цикла loop, кроме передачи управления команде, находящейся
по некоторому адресу, анализирует и уменьшает на еденицу значение
регистра ecx/cx.
Регистры
указателя
esi (si) -Индекс источникаedi (di) -Индекс приемникаeip (ip) -Указатель команды
Вы можете использовать регистры указателя, как универсальные
регистры (
eip - исключение), пока вы сохраняете их первоначальные
значения. Регистры указателя называются так потому, что их часто используют для
сохранения адресов памяти. Некоторые опкоды (movb,scasb, и т.д.) также их
используют.
esi (si) - (Soutce Index register) - Индекс источника.Этот регистр в цепочных операциях содержит текущий адрес в цепочке-источнике.
edi (di) - (DestinationIndex register) - Индекс приемника.Этот регистр в цепочных операциях содержит текущий адрес в цепочке-приемнике.
Оба
регистра используются для строковых операций.В них адресс
строки-источника помещается в esi,а адресс строки-приёмника в
edi.Пример:
cld
- Сброс флага DF
mov esi,STR1 - Загрузка str в si
mov edi,STR2 - Загрузка str2 в di
mov
ecx,5
- Загрузка в cx, 5 повторений для rep
rep movsb - Пересылка строк
Этот фрагмент кода загружает 5 байт из STR1 в STR2.
Регистр EIP (или IP в 16-разрядных программах) содержит
указатель на команду, которую собирается выполнить процессор. Так что вы не
можете использовать регистр eip как универсальный.
Регистры
указателя стека
ebp/bp -(Base Pointer register)Регстр указателя базы кадра стека.
Предназначен для организации произвольного доступа к данным внутри стека(используется как указатель на локальные
переменные).
esp/sp -(Stack Pointer register)Регистр указателя стека.
Содержит указатель вершины стека в текущем сегменте стека( текущую позицию стека в памяти).
Сегментные
регистры
Сегментные регистры определяют сегмент(участок) памяти, которая используется.
Вероятнее всего, они вам не понадобятся в win32, потому, что windows
использует flat систему памяти. В DOS, память разделена на сегменты по 64kb,
так, что если вы хотите определить адрес памяти, вы определяете сегмент и
смещение (например 0172:0500 (сегмент:смещение)). В windows,
сегменты имеют размер 4Gb.Так что при адреассации просто надо указать смещение . Сегментные регистры
всегда - 16-битные.
Каждый сегментный регистр обеспечивает адресацию 64К памяти, которая
называется текущим сегментом. Как показано ранее, cегмент выравнен на
границу параграфа и его адрес в сегментном pегистре предполагает наличие
справа четырех нулевых битов.
Р е г и с т р CS. Регистр сегмента кода содержит начальный
адрес сегмента кода. Этот адрес плюс величина смещения в командном
указателе (IP) определяет адрес команды, которая должна быть выбрана
для выполнения. Для обычных программ нет необходимости делать ссылки
на регистр CS.
Р е г и с т р DS. Регистр сегмента данных содержит начальный
адрес сегмента данных. Этот адрес плюс величина смещения,
определенная в команде, указывают на конкретную ячейку в сегменте
данных.
Р е г и с т р SS. Регистр сегмента стека содержит начальный
адрес в сегменте стека.
Р е г и с т р ES. Некоторые операции над строками используют
дополнительный сегментный регистр для управления адресацией памяти. В
данном контексте регистр ES связан с индексным регистром DI. Если
необходимо использовать регистр ES, ассемблерная программа должна его
инициализировать.
Регистры FS (только с 286+) и GS (только с 386+) -Универсальные сегментные регистры.С чем их едят,не знаю...
Память.
В
16-разрядных программах(в DOS) память была
разделена на сегменты. Эти сегменты имели размер 64kb. Для доступа к памяти были
необходимы: указатель на сегмент и указатель смещения. Указатель сегмента
указывал, какой сегмент (секцию 64kb) использовать, указатель смещения указывал
непосредственно на место в сегменте. Смотрите таблицу:
Память |
Сегмент 1 (64kb) |
Сегмент 2 (64kb) |
Сегмент 3 (64kb) |
Сегмент 4(64kb) |
и так
далее |
Учтите, что это объяснение для 16-разрядных программ, о 32-разрядных я
расскажу позже, (но не пропускайте эту часть, важно понять, что такое
32-разрядность).
Таблица выше иллюстрирует общую память, разделенную на
сегменты по 64kb. Здесь максимум 65536 сегментов. Теперь возмите один из
сегментов:
Сегмент 1(64kb) |
Смещение 1 |
Смещение 2 |
Смещение 3 |
Смещение 4 |
Смещение 5 |
и так далее |
Чтобы указать на местоположение в сегменте используется смещение. Смещение -
это местоположение внутри сегмента. Здесь в сегменте максимум 65536 смещений.
Запись адреса в памяти:
СЕГМЕНТ:СМЕЩЕНИЕ
(SEGMENT:OFFSET)
Например:
0030:4012
(все числа шестнадцатиричные)
Это означает: сегмент
30, смещение
4012. Чтобы узнать, что
находится в том адресе, вы сначала переходите на сегмент
30, а затем в
сегменте смещаетесь на
4012.
В 32-х разрядных программах(Windows)есть только смещения и они теперь 32-разрядные (в диапазоне от 0 до
4,294,967,295). Каждая ячейка в памяти указывается смещением,так
как один сегмент равен 4ГБ(ГигаБайт) и возиться с сегментами нам не
надо.(Ура!Работы меньше!!).