
- •1. Регистры
- •1.1. Регистры общего назначения
- •1.2. Указатель команд
- •1.3. Регистр флагов
- •1.3.1. Флаги состояния
- •1.3.2. Управляющий флаг
- •1.3.3. Системные флаги и поле iopl
- •1.4. Сегментные регистры
- •1.5. Использование стека
- •2. Основные понятия языка ассемблера
- •2.1. Идентификаторы
- •2.2. Целые числа
- •2.3. Символьные данные
- •2.4. Комментарии
- •2.5. Директива эквивалентности
- •2.6. Директивы определения данных
- •2.7. Команды
- •2.8. Операнды команд
- •3. Пересылка и арифметические команды
- •3.1. Команды пересылки и обмена
- •3.2. Оператор указания типа
- •3.3. Команды сложения и вычитания
- •3.4. Команды умножения и деления
- •3.4.1. Команды умножения
- •3.4.2. Команды деления
- •3.5. Изменение размера числа
- •4. Переходы и циклы
- •4.1. Безусловный переход
- •4.1.1. Прямой переход
- •4.1.2. Косвенный переход
- •4.2. Команды сравнения и условного перехода
- •4.3. Команды управления циклом
- •4.3.1. Команда loop
- •4.3.2. Команды loope/loopz и loopne/loopnz
- •5. Массивы
- •5.1. Модификация адресов
- •5.2. Команда lea
- •5.3. Обработка массивов
- •6. Поразрядные операции
- •6.1. Логические команды
- •6.2. Команды сдвига
- •6.3. Умножение и деление с помощью поразрядных операций
- •6.3.1. Умножение
- •6.3.2. Деление
- •6.3.3. Получение остатка от деления
- •7. Программа. Процедуры
- •7.1. Структура программы на языке ассемблера
- •7.2. Команды работы со стеком
- •7.3. Синтаксис процедуры
- •7.4. Вызов процедуры и возврат из процедуры
- •7.5. Передача параметров процедуры
- •7.6. Передача результата процедуры
- •7.7. Сохранение регистров в процедуре
- •7.8. Локальные данные процедур
- •7.9. Рекурсивные процедуры
- •8. Оптимизация программ, написанных на языке ассемблера
- •8.1. Высокоуровневая оптимизация
- •8.2. Оптимизация среднего уровня
- •8.2.1. Вычисление констант вне цикла
- •8.2.2. Перенос проверки условия в конец цикла
- •8.2.3. Выполнение цикла задом наперёд
- •8.2.4. Разворачивание циклов
- •8.3. Низкоуровневая оптимизация
- •8.3.1. Основные принципы
- •8.3.2. Использование команды lea
- •8.3.3. Замена команд
- •8.3.4. Выравнивание
- •9. Примеры
3. Пересылка и арифметические команды
3.1. Команды пересылки и обмена
Одна из основных команд языка ассемблер – это команда пересылки. С её помощью можно записать в регистр значение другого регистра, константу или значение ячейки памяти, а также можно записать в ячейку памяти значение регистра или константу. Команда имеет следующий синтаксис:
MOV <операнд1>, <операнд2>
По команде MOV значение второго операнда записывается в первый операнд. Операнды должны иметь одинаковый размер. Команда не меняет флаги.
mov eax, ebx ; Пересылаем значение регистра EBX в регистр EAX mov eax, 0ffffh ; Записываем в регистр EAX шестнадцатеричное значение ffff mov x, 0 ; Записываем в переменную x значение 0 mov eax, x ; Переслать значение из одной ячейки памяти в другую нельзя. mov y, eax ; Но можно использовать две команды MOV.
На самом деле процессор имеет много команд пересылки – код команды зависит от того, куда и откуда пересылаются данные. Но компилятор языка ассемблера сам выбирает нужный код в зависимости от операндов, так что, с точки зрения программиста, команда пересылки только одна.
Для перестановки двух величин используется команда обмена:
XCHG <операнд1>, <операнд2>
Каждый из операндов может быть регистром или ячейкой памяти. Однако переставить содержимое двух регистров можно, а двух ячеек памяти – нет. Операнды должны иметь одинаковый размер. Команда не меняет флаги.
3.2. Оператор указания типа
Как было сказано, операнды команды MOV должны иметь одинаковый размер. В некоторых случаях компилятор может определить размер операнда. Например, регистр EAX имеет размер 32 бита, а регистр DX – 16 бит. Размер переменной определяется по директиве, указанной в её объявлении. Если можно определить размер только одного операнда, то размер второго операнда подгоняется под размер первого, если это возможно. Если же можно определить размеры обоих операндов, то они должны совпадать.
x db ? mov x, 0 ; 0 может иметь любой размер, в данном случае берётся 1 байт mov eax, 0 ; 0 может иметь любой размер, в данном случае берётся 4 байта mov al, 1000h ; Ошибка – попытка записать 2-байтное число в 1-байтный регистр mov eax, cx ; Ошибка – размеры операндов не совпадают
Однако не всегда бывает возможно определить размер пересылаемой величины по операндам команды MOV. Например, если один из операндов является ячейкой памяти, адрес которой записан в регистре, то по этому адресу можно записать и 1 байт, и 2 байта, и 4 байта. Если второй операнд является регистром, то размер пересылаемых данных определяется по размеру регистра. Если же второй операнд является константой, то размер пересылаемых данных определить нельзя, и компилятор фиксирует ошибку. Для того чтобы избежать этой ошибки, надо явно указать размер пересылаемых данных. Для этого используется оператор PTR:
<тип> PTR <выражение>
В качестве типа используется BYTE, WORD или DWORD.
mov [ebx], 0 ; Ошибка, т.к. 0 может иметь любой размер mov byte ptr [ebx], 0 ; Пересылаем 1 байт mov dword ptr [ebx], 0 ; Пересылаем 4 байта