- •1. Предварительные сведения 6
- •2. Процессоры intel в реальном режиме 11
- •3. Директивы и операторы ассемблера 51
- •Введение
- •1. Предварительные сведения
- •1.1. Подготовка программ на языке ассемблера
- •1.2. Представление данных в компьютерах
- •1.2.1. Двоичная система счисления
- •1.2.2. Биты, байты и слова
- •1.2.3. Шестнадцатеричная система счисления
- •1.2.4. Числа со знаком
- •1.2.5. Логические операции
- •1.2.6. Коды символов
- •2. Процессоры intel в реальном режиме
- •2.1. Регистры процессора
- •2.1.1. Регистры общего назначения
- •2.1.2. Модели памяти и сегментные регистры
- •2.1.3. Стек
- •2.1.4. Регистр флагов
- •2.2.4. Косвенная адресация
- •2.2.5. Адресация по базе со смещением
- •2.2.6. Косвенная адресация с масштабированием
- •2.2.7. Адресация по базе с индексированием
- •2.2.8. Адресация по базе с индексированием и масштабированием
- •2.3. Основные непривилегированные команды
- •2.3.1. Пересылка данных
- •2.3.2. Двоичная арифметика
- •2.3.3. Десятичная арифметика
- •2.3.4. Логические операции
- •2.3.5. Сдвиговые операции
- •2.3.6. Операции над битами и байтами
- •2.3.7. Команды передачи управления
- •2.3.8. Строковые операции
- •2.3.9. Управление флагами
- •2.3.10. Загрузка сегментных регистров
- •2.3.11. Другие команды
- •3. Директивы и операторы ассемблера
- •3.1. Структура программы
- •3.2. Директивы распределения памяти
- •3.2.1. Псевдокоманды определения переменных
- •3.2.2. Структуры
- •3.3. Организация программы
- •3.3.1. Сегменты
- •3.3.2. Модели памяти и упрощенные директивы определения сегментов
- •3.3.4. Процедуры
- •3.3.5. Конец программы
- •3.3.6. Директивы задания набора допустимых команд
- •3.3.7. Директивы управления программным счетчиком
- •3.3.8. Глобальные объявления
- •3.3.9. Условное ассемблирование
- •3.4. Выражения
- •3.5. Макроопределения
- •3.5.1. Блоки повторений
- •3.5.2. Макрооператоры
- •3.5.3. Другие директивы, используемые в макроопределениях
- •3.6. Другие директивы
- •3.6.1. Управление файлами
- •3.6.2. Управление листингом
- •3.6.3. Комментарии
- •Литература
2.2.7. Адресация по базе с индексированием
В этом методе адресации смещение операнда в памяти вычисляется как сумма чисел, содержащихся в двух регистрах, и смещения, если оно указано. Все следующие команды — это разные формы записи одного и того же действия:
mov ax [bx+si+2]
mov ax,[bx][si]+2
mov ax,[bx+2][si]
mov ax,[bx][si+2]
mov aх,2[bx][si]
В регистр АХ помещается слово из ячейки памяти со смещением, равным сумме чисел, содержащихся в ВХ, SI, и числа 2. Из 16-битных регистров так можно складывать только ВХ + SI, ВХ + DI, ВР + SI и ВР + DI, а из 32-битных — все восемь регистров общего назначения. Так же как и для прямой адресации, вместо непосредственного указания числа можно использовать имя переменной, заданной одной из директив определения данных. Так можно считать, например, число из двумерного массива: если задана таблица 1010 байт, 2 — смещение ее начала от начала сегмента данных (на практике будет использоваться имя этой таблицы), ВХ = 20, а SI = 7, приведенные команды прочитают слово, состоящее из седьмого и восьмого байт третьей строки. Если таблица состоит не из одиночных байт, а из слов или двойных слов, удобнее использовать следующую, наиболее полную форму адресации.
2.2.8. Адресация по базе с индексированием и масштабированием
Это самая полная возможная схема адресации, в которую входят все случаи, рассмотренные ранее, как частные. Полный адрес операнда можно записать как выражение, представленное на рисунке 6.
-
CS:
SS:
DS:
ES:
FS:
GS:
EAX
EBX
ECX
EDX
EBP
ESP
EDI
ESI
+
EAX
EBX
ECX
EDX
EBP
EDI
ESI
1
2
4
8
+ смещение
Рис. 6. Полная форма адресации
Смещение может быть байтом или двойным словом. Если ESP или ЕВР используются в роли базового регистра, селектор сегмента операнда берется по умолчанию из регистра SS, во всех остальных случаях из DS.
2.3. Основные непривилегированные команды
В этой главе описаны все непривилегированные команды процессоров Intel серии х86, исключая команды расширений для работы с числами повышенной точности (NPX и MMX). Для каждой команды указана форма записи, название и модель процессоров Intel, начиная с которой она поддерживается: 8086, 80186, 80286, 80386, 80486, Р5 (Pentium), Р6 (Pentium Pro и Pentium II).
2.3.1. Пересылка данных
Команды пересылки данных осуществляют условный или безусловный обмен данными между регистрами и ячейками памяти, в том числе стековой, а также ввод и вывод данных через порты устройств.
• Команда: MOV приемник,источник
• Назначение: Пересылка данных
• Процессор: 8086
Базовая команда пересылки данных. Копирует содержимое источника в приемник, источник не изменяется. Команда MOV действует аналогично операторам присваивания из языков высокого уровня, то есть команда
mov dx, bx
эквивалентна выражению
ax:=bx;
языка Паскаль или
ах=bх;
языка С, за исключением того, что команда ассемблера позволяет работать не только с переменными в памяти, но и со всеми регистрами процессора.
В качестве источника для MOV могут использоваться: число (непосредственный операнд), регистр общего назначения, сегментный регистр или переменная (то есть операнд, находящийся в памяти). В качестве приемника — регистр общего назначения, сегментный регистр (кроме CS) или переменная. Оба операнда должны быть одного и того же размера — байт, слово или двойное слово.
Нельзя выполнять пересылку данных с помощью MOV из одной переменной в другую, из одного сегментного регистра в другой и нельзя помещать в сегментный регистр непосредственный операнд — эти операции выполняют двумя командами MOV (из сегментного регистра в обычный и уже из него в другой сегментный) или парой команд PUSH/POP.
Загрузка регистра SS командой MOV автоматически запрещает прерывания до окончания следующей за этим команды MOV, так что можно загрузить SS и ESP двумя последовательными командами MOV, не опасаясь, что в этот момент произойдет прерывание, обработчик которого получит неправильный стек. В любом случае для загрузки значения в регистр SS предпочтительнее команда LSS.
• Команда: CMOVcc приемник,источник
• Назначение: Условная пересылка данных
• Процессор: Р6
Это набор команд, которые копируют содержимое источника в приемник, если удовлетворяется то или иное условие (см. табл. 5). Источником может быть регистр общего назначения или переменная, а приемником — только регистр. Условие, которое должно удовлетворяться, — просто равенство нулю или единице тех или иных флагов из регистра FLAGS, но, если использовать команды CMOVcc сразу после команды СМР (сравнение) с теми же операндами, условия приобретают особый смысл, например:
cmp ax,bx ; сравнить ax и bx
cmovl ax,bx ; если ax < bx, скопировать bx в ах
Слова «выше» и «ниже» в таблице 5 относятся к сравнению чисел без знака, слова «больше» и «меньше» учитывают знак.
• Команда: XCHG операнд1,операнд2
• Назначение: Обмен операндов между собой
• Процессор: 8086
Содержимое операнда 2 копируется в операнд 1, а старое содержимое операнда 1 — в операнд 2. XCHG можно выполнять над двумя регистрами или над регистром и переменной.
xchg еах,ebx ; то же, что три команды на языке С:
; temp = eax; eax = ebx; ebx = temp,
xchg аl,al ; а эта команда не делает ничего
• Команда: BSWAP регистр32
• Назначение: Обмен байт внутри регистра
• Процессор: 80486
Обращает порядок байт в 32-битном регистре. Биты 0 – 7 (младший байт младшего слова) меняются местами с битами 24 – 31 (старший байт старшего слова), а биты 8 – 15 (старший байт младшего слова) меняются местами с битами 16 – 23 (младший байт старшего слова).
mov eax,12345678h
bswap eax ; теперь в eax находится 78563412h
Таблица 5. Разновидности команды CMOVcc
-
Код команды
Реальное условие
Условие для СМР
CMOVA
CMOVNBE
СР = 0 и ZF = 0
если выше
если не ниже или равно
CMOVAE CMOVNB CMOVNC
CF = 0
если выше или равно
если не ниже
если нет переноса
CMOVB
CMOVNAE CMOVC
CF = 1
если ниже
если не выше или равно
если перенос
CMOVBE CMOVNA
CF = 1 и ZF = 1
если ниже или равно
если не выше
CMOVE
CMOVZ
ZF = 1
если равно
если ноль
CMOVG CMOVNLE
ZF = 0 и SF = OF
если больше
если не меньше или равно
CMOVGE CMOVNL
SF = OF
если больше или равно
если не меньше
CMOVL
CMOVNGE
SF <> OF
если меньше
если не больше или равно
CMOVLE CMOVNG
ZF = 1и SF<> OF
если меньше или равно
если не больше
CMOVNE CMOVNZ
ZF = 0
если не равно
если не ноль
CMOVNO
OF = 0
если нет переполнения
CMOVO
OF = 1
если есть переполнение
CMOVNP CMOVPO
PF = 0
если нет четности
если нечетное
CMOVP
CMOVPE
PF = 1
если есть четность
если четное
CMOVNS
SF = 0
если нет знака
CMOVS
SF = 1
если есть знак
Чтобы обратить порядок байт в 16-битном регистре, следует использовать команду XCHG:
xchg al,ah ; обратить порядок байт в АХ
• Команда: PUSH источник
• Назначение: Поместить данные в стек
• Процессор: 8086
Помещает содержимое источника в стек. Источником может быть регистр, сегментный регистр, непосредственный операнд или переменная. Фактически эта команда уменьшает ESP на размер источника в байтах (2 или 4), а затем копирует содержимое источника в память по адресу SS:[ESP]. Команда PUSH практически всегда используется в паре с POP (считать данные из стека). Так, например, чтобы скопировать содержимое одного сегментного регистра в другой (что нельзя выполнить одной командой MOV), можно использовать такую последовательность команд:
push cs
pop ds ; теперь DS указывает на тот же сегмент, что и CS
Другое частое применение команд PUSH/POP — временное хранение переменных, например:
push eax ; сохраняет текущее значение EAX
. . . ; здесь располагаются какие-нибудь команды,
; которые используют EAX, например CMPXCHG
pop eax ; восстанавливает старое значение EAX
Начиная с 80286, команда PUSH ESP (или SP) помещает в стек значение ESP до того, как эта же команда его уменьшит, в то время как на 8086 SP помещался в стек уже уменьшенным на два.
• Команда: POP приемник
• Назначение: Считать данные из стека
• Процессор: 8086
Помещает в приемник слово или двойное слово, находящееся в вершине стека, увеличивая ESP на 2 или 4 соответственно. POP выполняет действие, полностью обратное PUSH. Приемником может быть регистр общего назначения, сегментный регистр, кроме CS (чтобы загрузить CS из стека, надо воспользоваться командой RET), или переменная. Если в роли приемника выступает операнд, использующий ESP для косвенной адресации, команда POP вычисляет адрес операнда уже после того, как она увеличивает ESP.
• Команда: PUSHA, PUSHAD
• Назначение: Поместить в стек все регистры общего назначения
• Процессор: 80186, 80386
PUSHA помещает в стек регистры в следующем порядке: AX, CX, DX, ВХ, SP, ВР, SI и DI. PUSHAD помещает в стек ЕАХ, ЕСХ, EDX, ЕВХ, ESP, EBP, ESI и EDI. (В случае SP и ESP используется значение, которое находилось в этом регистре до начала работы команды.) В паре с командами POPA/POPAD, считывающими эти же регистры из стека в обратном порядке, это позволяет писать подпрограммы (обычно обработчики прерываний), которые не должны изменять значения регистров по окончании своей работы. В начале такой подпрограммы вызывают команду PUSHA, а в конце — РОРА.
На самом деле PUSHA и PUSHAD — одна и та же команда с кодом 60h. Ее поведение определяется тем, выполняется ли она в 16- или в 32-битном режиме. Если программист использует команду PUSHAD в 16-битном сегменте или PUSHA в 32-битном, ассемблер просто записывает перед ней префикс изменения размерности операнда (66h).
Это же будет распространяться на некоторые другие пары команд: РОРА/POPAD, POPF/POPFD, PUSHF/PUSHFD, JCXZ/JECXZ, CMPSW/CMPSD, INSW/INSD, LODSW/LODSD, MOVSW/MOVSD, OUTSW/OUTSD, SCASW/SCASD и STOSW/ STOSD.
• Команда: РОРА, POPAD
• Назначение: Загрузить из стека все регистры общего назначения
• Процессор: 80186, 80386
Эти команды выполняют действия, полностью обратные действиям PUSHA и PUSHAD, за исключением того, что помещенное в стек значение SP или ESP игнорируется. РОРА загружает из стека DI, SI, ВР, увеличивает SP на два, загружает ВХ, DX, CX, AX, а POPAD загружает EDI, ESI, ЕВР, увеличивает ESP на 4 и загружает ЕВХ, EDX, ЕСХ, ЕАХ.
• Команда: IN приемник,источник
• Назначение: Считать данные из порта
• Процессор: 8086
Копирует число из порта ввода-вывода, номер которого указан в источнике, в приемник. Приемником может быть только AL, АХ или ЕАХ. Источник — или непосредственный операнд, или DX, причем можно указывать только номера портов не больше 255.
• Команда: OUT приемник,источник
• Назначение: Записать данные в порт
• Процессор: 8086
Копирует число из источника (AL, AX или ЕАХ) в порт ввода-вывода, номер которого указан в приемнике. Приемник может быть либо непосредственным номером порта, либо регистром DX. На командах IN и OUT строится все общение процессора с устройствами ввода-вывода — клавиатурой, жесткими дисками, различными контроллерами, и используются они, в первую очередь, в драйверах устройств. Например, чтобы включить динамик PC, достаточно выполнить команды:
in al,61h
or al,3
out 61h,al
• Команда: CWD
• Назначение: Конвертирование слова в двойное слово
• Процессор: 8086
• Команда: CDQ
• Назначение: Конвертирование двойного слова в учетверенное
• Процессор: 80386
Команда CWD превращает слово в AХ в двойное слово, младшая половина которого (биты 0 – 15) остается в АХ, а старшая (биты 16 – 31) располагается в DX. Команда CDQ выполняет аналогичное действие по отношению к двойному слову в ЕАХ, расширяя его до учетверенного слова в EDX:EAX. Эти команды всего лишь устанавливают все биты регистра DX или EDX в значение, равное значению старшего бита регистра АХ или ЕАХ, сохраняя таким образом его знак.
• Команда: CBW
• Назначение: Конвертирование байта в слово
• Процессор: 8086
• Команда: CWDE
• Назначение: Конвертирование слова в двойное слово
• Процессор: 80386
CBW расширяет байт, находящийся в регистре AL, до слова в АХ, CWDE расширяет слово в АХ до двойного слова в ЕАХ. CWDE и CWD отличаются тем, что CWDE располагает свои результат в ЕАХ, в то время как CWD, команда, выполняющая точно такое же действие, располагает результат в паре регистров DX:AX. Так же как и команды CWD/CDQ, расширение выполняется путем установки каждого бита старшей половины результата равным старшему биту исходного байта или слова, то есть:
mov al,0F5h ; AL = 0F5h = 245 = -11
cbw ; теперь АХ = 0FFF5h = 55 525 = -11
Так же как и в случае с командами PUSHA/PUSHAD, пара команд CWD/CDQ — это одна команда с кодом 99h, и пара команд CBW/CWDE — одна команда с кодом 98h. Интерпретация этих команд зависит от того, в каком (16-битном или в 32-битном) сегменте они исполняются, и точно так же, если указать CDQ или CWDE в 16-битном сегменте, ассемблер поставит префикс изменения разрядности операнда.
• Команда: MOVSX приемник,источник
• Назначение: Пересылка с расширением знака
• Процессор: 80386
Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет знак аналогично командам CBW/CWDE.
• Команда: MOVZX приемник,источник
• Назначение: Пересылка с расширением нулями
• Процессор: 80386
Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет нулями, то есть команда
movzx ах, bl
эквивалентна паре команд
mov al,bl
mov ah,0
• Команда: XLAT адрес
XLATB
• Назначение: Трансляция в соответствии с таблицей
• Процессор: 8086
Помещает в AL байт из таблицы в памяти по адресу DS:BX (или DS:EBX) со смещением относительно начала таблицы, равным AL. В качестве аргумента для XLAT в ассемблере можно указать имя таблицы, но эта информация никак не используется процессором, и в любом случае необходимо заполнять регистр BX (EBX) адресом таблицы. Однако при указании аргумента-имени можно использовать переопределение сегмента ES: (если таблица расположена в дополнительном сегменте данных). Применение формы записи команды XLATB не допускает переопределения сегмента.
Эта команда часто используется для преобразований значений цифр в ASCII-формат или различных перекодировок (например, из ASCII в EBCDIC или KOI-8). Впрочем, преобразование цифр в ASCII-форму более эффективно выполнить с помощью команды DAS.
• Команда: LEA приемник,источник
• Назначение: Вычисление эффективного адреса
• Процессор: 8086
Вычисляет эффективный адрес источника (переменная) и помещает его в приемник (регистр). С помощью LEA можно вычислить адрес переменной, которая описана сложным методом адресации, например по базе с индексированием. Если адрес 32‑битный, а регистр-приемник 16-битный, старшая половина вычисленного адреса теряется, если наоборот, приемник 32-битный, а адресация 16-битная, то вычисленное смещение дополняется нулями.
Команду LEA можно использовать также для быстрых арифметических вычислений, например умножения:
lea bx,[ebx+ebx4] ; ВХ=ЕВХ5
или сложения:
lea ebx,[eax+12] ; ЕВХ=ЕАХ+12
(эти команды занимают меньше, чем соответствующие MOV и ADD, и не изменяют флаги).