Предыдущая страница (Описание простейших команд Ассемблера)

Основные команды процессора

Таблица, расположенная ниже, поможет понять, как можно записывать основные команды процессора (например, из неё видно, что если команда двухоперандная, то оба операнда не могут быть ячейками памяти). Если команда унарная (число параметров=1), то мы должны указать, чем может быть её единственный операнд (регистром или переменной). Если в графе "r" стоит "+", значит может быть, если "–", значит, не может быть. Если пусто, то не имеет смысла (ведь не имеет смысла говорить, чем может являться единственный операнд двухоперандной команды!). Если команда бинарная, то таблица показывает, какими могут быть операнды. Во всех бинарных командах таблицы операнды должны иметь одинаковую разрядность (8|16|32). ZF, SF, CF, OF, AF, PF – меняет ли операция данные флаги "+" – меняет; ничего – не меняет, "?" – делает неопределёнными; "=0" – делает нулями

команда
пар-в
r
m
rr
rm
mr
rC
mC
ZF
SF
CF
OF
AF
PF
mov x, y
2
 
 
+
+
+
+
+
xchg x, y
2
 
 
+
+
+
add x, y
2
 
 
+
+
+
+
+
+
+
+
+
+
+
adc x, y
2
 
 
+
+
+
+
+
+
+
+
+
+
+
sub x, y
2
 
 
+
+
+
+
+
+
+
+
+
+
+
sbb x, y
2
 
 
+
+
+
+
+
+
+
+
+
+
+
inc x
1
+
+
 
 
 
 
 
+
+
+
+
+
dec x
1
+
+
 
 
 
 
 
+
+
+
+
+
cmp x, y
2
 
 
+
+
+
+
+
+
+
+
+
+
+
and|or|xor|test x, y
2
 
 
+
+
+
+
+
+
+
=0
=0
?
+
not x
1
+
+
 
 
 
 
 

1) push r16|r32|m16|m32|C16|C32 – поместить в стек. Флаги не меняет. "push sp|esp" – помещает в стек значение esp до того как его уменьшит. Чтобы занести константу в стек, надо записать перед ней "word ptr" или "dword ptr", чтобы компилятор понял, сколько байт начиная с данного адреса заносить в стек. В стек можно занести слово (2 байта), двойное слово (4 байта), но не байт!
1) pop r16|r32|m16|m32 – считать данные из стека. Флаги не меняет. Если операнд – ячейка памяти, использующая esp для адресации, то команда pop вычисляет адрес операнда уже после того, как она увеличит esp

2) lea r32, [адрес] – записывает в 32-разрядный регистр значение адреса. Флаги не меняются. Адрес записывается в квадратных скобках. Таким образом можно определить адреса некоторых переменных, имеющих сложную адресацию. Например, пусть у нас есть глобальный массив y: array [0..1000] of longint. Тогда после выполнения команды lea eax, [y+4*200] в регистре eax будет храниться адрес ячейки y[200] (то есть 200-й элемент массива, считая с нуля). На 4 умножали, потому что массив из 4-байтных данных. Также команду lea можно использовать для быстрых вычислений выражений определённого типа

3) cbw – расширяет al до ax
3) cwd – расширяет ax до dx:ax
3) cwde – расширяет ax до eax
3) cdq – расширяет eax до edx:eax
Расширение единицами, если старший бит исходного регистры был единицей, иначе расширение нулями. По сути расширяет число со знаком. Например, пусть у нас есть число 00111101b (то есть 61) типа shortint. После выполнения над ним команды cbw оно превратится в число 0000000000111101 (то есть тоже 61). Однако, если у нас было отрицательное число, например, 10001000b (то есть -120), то после расширения оно будет 1111111110001000b (то есть тоже 120). Таким образом, эти команды расширяют числа со знаком (чтобы расширить число без знака, надо дополняемую часть заполнить нулями, существуют команды, которые это делают)

4) movsx приёмник, источник – копирует содержимое источника (r8_или_m8 | r16_или_m16) в приёмник (r16|r32) и расширяет знак аналогично cbw|cwde команда movzx копирует содержимое источника (r8_или_m8 | r16_или_m16) в приёмник (r16|r32) и расширяет нулями. По сути расширяет число без знака

5) bswap r32 – меняет местами 1-й с 4-м байты в 32-разрядном регистре и 2-й с 3-м. Например, если у нас регистр eax=12345678h, то после выполнения команды bswap eax регистр eax=78563412

6) xlatb – трансляция в соответствии с таблицей. Помещает в al байт из таблицы в памяти по адресу ebx со смещением относительно начала таблицы, равным al. Например:
    jmp @begin;
@htable: db '0123456789ABCDEF';
@begin:
    mov al, 09h;
    mov ebx, offset @htable;
    xlatb;

После этого al=ord(9) (ASCII-числу 9), то есть al=39h


7) in приёмник, источник – копирует в приёмник (al|ax|eax) число из порта ввода-вывода, номер которого указан в источнике. Источник – регистр dx, или 8-битная константа
7) out источник, приёмник – копирует число из порта с номером, указанным в источнике (регистр dx или 8-битная константа), в приёмник (al|ax|eax)
Команды in и out обычно используются драйверами различных устройств (рули, джойстики, принтеры, сканеры,...). Также они используются
обработчиками прерываний. Когда мы пишем int 21h, то происходит на самом деле множество действий (в том числе, множество выполнений in и out)

8) imul источник – знаковое умножение. Источник – r8|r16|r32|m8|m16|m32. Источник умножается на al|ax|eax в зависимости от разрядности операнда и результат располагается в ax|dx:ax|edx:eax соответственно. Если результат поместился в младшую половину, то CF=OF=0, иначе CF=OF=1. Флаги ZF, SF, AF, PF – не определены. То есть команда умножения mul|imul предполагает, что результат в два раза длиннее множителей. Это довольно непростая команда, поэтому приведём пример

9) mul источник – беззнаковое умножениие (аналогично предыдущему)

10) idiv источник – целочисленное деление со знаком. Источник – r8|r16|r32|m8|m16|m32. Выполняет целочисленное деление со знаком следующим образом:
8 бит: ax/x8; al – частное; ah – остаток
16 бит: dx:ax/x16; ax – частное; dx – остаток
32 бит: edx:eax/x32; eax – частное; edx – остаток
Флаги ZF, SF, CF, OF, AF, PF после этой команды не определены. Опасность: при переполнении или делении на ноль программа вылетает! Например, если ax=256, cl=1, то после команды div cl в al должно будет записаться число 256, а это невозможно (регистр 8-битный). Поэтому программа вылетит. Так как команда div|idiv довольно сложная, приведём пример применения

11) div источник – беззнаковое деление (аналогично предыдущему)

12) imul приёмник, источник – источник (C|r|m) умножается на приёмник (регистр), результат заносится в приёмник. Разрядность операндов – 16 или 32
12) imul приёмник, источник1, источник2 – источник1 (r|m) умножается на источник2 (число), результат заносится в приёмник (регистр). Разрядность операндов – 16 или 32. Если произошло переполнение, и потеря старших битов результата, то OF=CF=1, иначе OF=CF=0. Значения SF, ZF, AF, PF не определены

13) neg приёмник – выполняет над числом, содержащемся в приёмнике (r8|r16|32|m8|m16|m32) операцию дополнения до двух. Эта операция эквивалентна обращению знака, если рассматривать приёмник как число со знаком. Если приёмник равен нулю, то CF=0, иначе CF=1. Остальные флаги (OF, SF, ZF, AF, PF) назначаются в соответствии с результатом операции. Если забить на флаги, то команда neg – по сути равносильна последовательному выполнению команды not , затем inc (вспоминаем, как хранятся отрицательные числа в дополнительном обратном коде). Красивый пример применения команды neg – получение абсолютного значения числа: @label: neg eax; js @label;

14) pushad – поместить в стек регистры общего назначения. Располагает в стеке регистры в следующем порядке: eax, ecx, edx, ebx, esp, ebp, esi, edi (edi будет на вершине стека). В случае с esp используется значение, которое было до начала работы команды
15) popad – загрузить из стека регистры общего назначения. Выполняет действие, обратное pushad , но помещённое в стек значение esp игнорируется

15) xadd приёмник, источник – обменять между собой и сложить. Выполняет сложение, помещает приёмник в источник, сумму – в приёмник. Источник – регистр, приёмник – регистр|переменная. Разрядность операндов – 8|16|32
xadd
до
после
приёмник
x
x+y
источник
y
x

16) cmpxchg приёмник, источник – сравнить и обменять между собой. Сравнивает значения в al|ax|eax с приёмником (регистром). Если они равны, приёмник:=источник и ZF:=1, иначе al|ax|eax:=приёмник и ZF:=0. Остальные флаги устанавливаются по результату операции сравнения, как после cmp. Источник – регистр, приёмник – r|m

17) cmpxchg8b приёмник – сравнить и обменять 8 байт. Сравнивает edx:eax с приёмником (m64). Если они равны, то приёмник:=ecx:ebx. Иначе edx:eax:=приёмник

Следующая страница (Условные и безусловные переходы)