Участник:SkudarnovYaroslav/Лекция по ассемблеру за 3 марта — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Новая страница: «Регистры -- основная память в ассемблере. Регистры общего назначения: eax (accumulator) ebx (base) ecx (...»)
 
(Арифметические команды: так читабельнее)
 
(не показано 16 промежуточных версий 4 участников)
Строка 1: Строка 1:
Регистры -- основная память в ассемблере.
 
  
Регистры общего назначения:
+
= Регистры =
eax (accumulator)
 
ebx (base)
 
ecx (counter)
 
edx (data)
 
ebp ...
 
esp (stack pointer) трогать нежелательно, стек ПЫЩЬ
 
esi ...
 
edi ...
 
  
То, что вторые буквы -- первые четыре буквы лат. алфавита -- случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...
+
Регистры — память процессора.
 +
 
 +
Регистры '''общего назначения''' (в скобках — изначальное предназначение)
 +
  eax (accumulator)
 +
  ebx (base)
 +
  ecx (counter)
 +
  edx (data)
 +
  ebp (stack base pointer)
 +
  esp (stack pointer) трогать <s>нежелательно</s> аккуратно, стек ПЫЩЬ
 +
  esi (source)
 +
  edi (destination)
 +
 
 +
То, что вторые буквы &mdash; первые четыре буквы латинского алфавита &mdash; случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...
  
 
Ещё регистры:
 
Ещё регистры:
eip (instruction pointer)
+
  eip (instruction pointer)
eflags: куча битов, которые означают флаги. пример:
+
  eflags: куча битов, которые означают флаги. пример:
zf -- 1, если последняя операция вернула 0, 1 otherwise
+
  zf &mdash; 1, если последняя операция вернула 0, 1 otherwise
cf -- знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
+
  cf &mdash; знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
sf -- знаковый бит. 0 -- полож., 1 -- отриц.
+
  sf &mdash; знаковый бит. 0 &mdash; полож., 1 &mdash; отриц.
 +
 
 +
На вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций, в том числе comp и test.
 +
 
 +
  df &mdash; направление выполнения строковых операций.
  
(на вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций!)
+
Регистры e** 32-битны. На 64-битных системах действует следующие наименования:
  
df -- направление выполнения строковых операций.
+
<tex>
Регистры 32-битны. На 64-битных системах действует следующие наименования:
+
\underbrace {\fbox{32 \quad\quad\quad\quad}
        e*x
+
  \overbrace{\fbox{16 \quad\quad}
    ____^____ 
+
      \overbrace{
    /        \
+
        \underbrace{\fbox{8 \quad}}_{*h}
32 | 16 | 8 | 8
+
         \underbrace{\fbox{8 \quad}}_{*l}
         *h  *l
+
      }^{*x}
\______ ______/
+
  }^{e*x}
      v
+
}_{r*x}
      r*x
+
 +
</tex>
  
 +
Собственно, этимология названий: буква '''e''' (extended) появилась в названиях регистров с 386 процессором и 32-битным режимом работы. '''h''' и '''l''' пошли от high и low соответственно (старший и младший байты)
  
 +
= Обзор команд =
  
 +
== Команды загрузки ==
 +
  mov register|memory, register|memory|immediate ; записать значение второго операнда в первый, размер данных должен совпадать
 +
  mov al, 5 ; загрузить в al 5
 +
  mov cx, di ; скопировать значение di в cx
  
Команды
+
  movzx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются нулями
 +
  movsx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются знаковым битом
  
Команды загрузки
+
  cmov(cc) register, register|memory ; cc &mdash; условие, команда выполняется только если условие верно
mov -- команда загрузки.
 
mov al, 5 -- загрузить в al 5
 
Обычно первый аргумент команды -- то, что меняется.
 
mov cx, di -- скопировать значения cx в di. Размер копируемых данных должен совпадать.
 
movzx -- размер игнорируется; то, что осталось, заполняется нулями
 
movcx -- размер игнорируется; то, что осталось, заполняется знаковым (старшем) битом
 
mov(cc): cc -- условие. если cc, то mov, ничего otherwise
 
xchg -- обмен значений регистров
 
  
bswap -- меняет порядок байт в регистре на обратный (little в big, big в little)
+
  xchg register|memory, register|memory ; обмен значений
  
//little-endian:
+
  bswap register ; меняет порядок байт в регистре на обратный
//байты загружаются в память в порядке:
+
 
//0...7...15...31
+
Уточнение про порядок байт:
//
+
  //little-endian:
//big-endian:
+
  //байты загружаются в память в порядке:
//31...15...7...0
+
  //0...7...15...31
//
+
  //
//процессоры: x86 -- little-endian, при обмене данными по сети -- big-endian
+
  //big-endian:
 +
  //31...15...7...0
 +
  //
 +
  //процессоры: x86 &mdash; little-endian, при обмене данными по сети &mdash; big-endian
  
 
Обращаться можно и к памяти:
 
Обращаться можно и к памяти:
mov eax,[ebx] -- загрузка в eax того, что находится по адресу ebx. (в C выглядело бы как eax = *ebx. [] - косвенная адресация. не более одних скобок за раз, ** недопустимы. может использоваться как для левого, так и правого аргумента mov. по правилам в команде <= 1 обращения к памяти. сказать, что "mov [eax],[ebx]" разрешено == всё равно, что выгнать себя с экзамена)
+
  mov eax, [ebx] ; загрузка в eax того, что находится по адресу ebx (в C выглядело бы как eax = *ebx)
 +
 
 +
[] - косвенная адресация. не более одних скобок за раз, ** недопустимы. Может использоваться как для левого, так и правого аргумента mov. В команде может быть не больше одного обращения к памяти.
 +
  <s>mov [eax], [ebx]</s> ; так делать нельзя!
 +
 
 
Оперативную память попортить нельзя, она разделена между процессами.
 
Оперативную память попортить нельзя, она разделена между процессами.
//Си -- высокоуровневый ассемблер, многие вещи в Си пошли от ассемблера
+
 
 
В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:
 
В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:
bx (bp) + si (di) +(-) offset (16-битное число)
+
  bx   si
 +
      +     +/- offset (16-битное число)
 +
  bp  di
 
В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:
 
В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:
любой из регистров общего назначения + (любой из регистров общего назначения, за исключением esp)*(1|2|4|8) +(-) offset (32-битное число)
+
  eax
 +
  ebx  eax
 +
  ecx  ebx  1
 +
  edx  ecx  2
 +
      + edx *   +/- offset (32-битное число)
 +
  ebp  ebp  4
 +
  esp  edi  8
 +
  edi  esi
 +
  esi
  
push eax -- положить eax на стек.
+
== Работа со стеком ==
 +
 
 +
  push register|memory|immediate ; положить значение на стек
  
 
Сишный псевдокод:
 
Сишный псевдокод:
   | esp -= sizeof(eax) (минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот))
+
   esp -= sizeof(eax) // минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот)
   | *esp = eax
+
   *esp = eax
Вроде, эквивалентный код на ассемблере:
+
Эквивалентный код на ассемблере:
   | sub esp, 4
+
   sub esp, 4
   | mov [esp], eax
+
   mov [esp], eax
  
pop eax -- запихать вершину стека в eax
+
  pop register|memory ; записать значение верхнего элемента стека в регистр/память
  
 
Как это выглядит на ассемблере:
 
Как это выглядит на ассемблере:
   | mov eax, [esp]
+
   mov eax, [esp]
   | add esp, 4
+
   add esp, 4
  
Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном -- кратным четырём: всё будет работать существенно быстрее.
+
Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном &mdash; кратным четырём: всё будет работать существенно быстрее.
Стек -- хорошее быстрое временное хранилище. не слишком большое (1-2 мб)
+
Стек &mdash; хорошее быстрое временное хранилище. Не слишком большое (1-2 мб).
//malloc и free -- системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)
+
//malloc и free &mdash; системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)
  
pusha(d) -- сохранить в стек все 8 регистров общего назначения
+
  pusha(d) ; сохранить в стек все 8 регистров общего назначения
popa(d) -- вытащить их из стека
+
  popa(d) ; вытащить их из стека
  
(push|pop)ad работают с 32-битными регистрами, (push|pop)a - c 16-битными
+
(push|pop)ad работают с 32-битными регистрами, (push|pop)a &mdash; c 16-битными.
при восстановлении не меняется esp (логично, дабы с esp не произошло трешака)
+
При восстановлении не меняется esp (логично, дабы с esp не произошло трешака).
 
//"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"
 
//"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"
  
 
*Начало второй лекции. Начало пропущено:*
 
*Начало второй лекции. Начало пропущено:*
  
Арифм. команды:
+
== Арифметические команды ==
  
add eax, ebx (eax += ebx)
+
  add eax, ebx ; eax += ebx
adc (???)
+
  adc eax, ebx ; eax += ebx + CF // CF - флаг переноса
sub / sbb
+
  sub / sbb
mul / div
+
  mul / div
imul / idiv
+
  imul / idiv
  
mul OP -- после этой команды в edx:eax 64 бита -- результат умножения eax на аргумент
+
  mul OP &mdash; после этой команды в edx:eax 64 бита &mdash; результат умножения eax на OP
div OP -- после этой команды в edx edx:eax / ebx
+
  div OP &mdash; после этой команды в edx edx:eax / ebx
                          в eax же eds:eax % ebx
+
                            в eax же eds:eax % ebx
  
 
как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а.
 
как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а.
 
//в документации указана обработка всего этого в 8- и 16-битных случаях
 
//в документации указана обработка всего этого в 8- и 16-битных случаях
  
imul и idiv -- аналогично mul и div, НО: работают со знаковыми числами
+
  imul и idiv &mdash; аналогично mul и div, НО: работают со знаковыми числами
  
 
кроме того, у imul есть такие формы записи:
 
кроме того, у imul есть такие формы записи:
imul ecx, ebx == ecx *= ebx (верхние биты обнуляются)
+
  imul ecx, ebx == ecx *= ebx (верхние биты обнуляются)
imul ecx, ebx, 5 == ecx = ebx * 5
+
  imul ecx, ebx, 5 == ecx = ebx * 5
  
inc и dec. принимают один операнд, увеличивают (уменьшают) его на один; не трогают знак переноса
+
  inc и dec. принимают один операнд, увеличивают (уменьшают) его на один; не трогают флаг переноса
  
 
Команда, которая неизвестно для чего изначально нужна.
 
Команда, которая неизвестно для чего изначально нужна.
  
lea (load effective adress):
+
  lea (load effective adress):
  
lea eax, [ebx+ecx] -- фактически, трёхоперандное сложение. eax = abx + ecx
+
  lea eax, [ebx+ecx] ; фактически, трёхоперандное сложение. eax = abx + ecx
  
lea eax, [ebx+ebx*4] -- eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя)
+
  lea eax, [ebx+ebx*4] ; eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя)
  
 
Как быстрее всего умножить число на 10?
 
Как быстрее всего умножить число на 10?
  
lea eax, [ebx*5]
+
  lea eax, [ebx*5]
add eax, eax
+
  add eax, eax
  
Команды логики:
+
== Команды логики ==
and, or, xor, not. Все, кроме not -- унарные.
+
  and, or, xor, not. Все, кроме not имеют два аргумента.
 +
 
 +
  xor eax, eax &mdash; быстрое обнуление регистра
  
xor eax, eax -- быстрое обнуление регистра
 
 
команда mov anything, 0 имеет смысл, но редко. когда:
 
команда mov anything, 0 имеет смысл, но редко. когда:
 
1) когда нужно сохранить флаги. логика их меняет
 
1) когда нужно сохранить флаги. логика их меняет
Строка 142: Строка 169:
 
Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.
 
Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.
  
test eax,eax -- самый короткий способ проверить, 0 ли регистр
+
  test eax,eax &mdash; самый короткий способ проверить, 0 ли регистр
  
Команды сдвига:
+
== Команды сдвига ==
 
Во всех сдвигах последний сдвигаемый бит идёт в cf
 
Во всех сдвигах последний сдвигаемый бит идёт в cf
  
shr/shl -- сишные сдвиги (>>, <<). второй аргумент команды -- либо константа, либо cl. деление на степени двойки -- самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
+
  shr/shl &mdash; сишные сдвиги (>>, <<). второй аргумент команды &mdash; либо константа, либо cl. деление на степени двойки &mdash; самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
sar -- сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
+
  sar &mdash; сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
  
shrd/shld принимают три аргумента.
+
  shrd/shld принимают три аргумента.
shrd eax,ebx,3:
+
  shrd eax,ebx,3:
ebx,eax двигаются на 3 вправо (О_О). первые k (где k -- третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.
+
  ebx,eax двигаются на 3 вправо (О_О). первые k (где k &mdash; третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.
  
Команды вращения (о_О):
+
=== Команды вращения (о_О) ===
rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается.
+
  rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается.
rcl, rcr:
+
  rcl, rcr:
  
rcl:
+
  rcl:
  
cf <- 31...<первый аргумент>...0-^
+
  cf <- 31...<первый аргумент>...0-^
|                                |
+
  |                                |
v-------------------------------->
+
  v-------------------------------->
  
Аналогично rol/ror'у, но флаг cf -- часть того, что сдвигается (т.о., сдвигаются 33 байта)
+
Аналогично rol/ror'у, но флаг cf &mdash; часть того, что сдвигается (т.о., сдвигаются 33 бита)
  
Команды передачи управления:
+
== Команды передачи управления ==
  
jmp метка (jmp eax)
+
  jmp метка (jmp eax)
фактически -- mov eip, smth
+
фактически &mdash; mov eip, smth
  
 
Так же есть условия, зависящие от флагов:
 
Так же есть условия, зависящие от флагов:
j(cc)
+
  j(cc)
ex.: jz -- условный переход, если флаг нуля установлен.
+
  ex.: jz &mdash; условный переход, если флаг нуля установлен.
jnz -- переход, если флаг нуля не установлен.
+
  jnz &mdash; переход, если флаг нуля не установлен.
jc, jnc -- аналогично для флага переноса, и так далее... тысячи их!
+
  jc, jnc &mdash; аналогично для флага переноса, и так далее... тысячи их!
  
ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
+
  ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
ja -- above (если больше, без знака)
+
  ja &mdash; above (если больше, без знака)
jb -- below (если меньше, без знака)
+
  jb &mdash; below (если меньше, без знака)
jae -- above/equal (больше или равно без знака)
+
  jae &mdash; above/equal (больше или равно без знака)
jbe -- below/equal (меньше или равно без знака)
+
  jbe &mdash; below/equal (меньше или равно без знака)
jg  \
+
  jg  \
jl  |} -- аналогично вышеуказанным,
+
  jl  |} &mdash; аналогично вышеуказанным,
jge |} -- но с учётом знаков.
+
  jge |} &mdash; но с учётом знаков.
jle /
+
  jle /
  
 
к каждым командам существуют отрицания. j(n)cc
 
к каждым командам существуют отрицания. j(n)cc
Строка 192: Строка 219:
 
те же самые условия используеются в командах условной загрузки (mov(cc))
 
те же самые условия используеются в командах условной загрузки (mov(cc))
  
call -- аналогично jmp, НО.
+
  call &mdash; аналогично jmp, НО.
call x ~=  
+
  call x ~=  
    | push eip
+
      | push eip
    | mov eip, x
+
      | mov eip, x
ret ~=  
+
  ret ~=  
    | pop eip
+
      | pop eip
 
+
 
У ret есть опциональный параметр -- число.
+
У ret есть опциональный параметр &mdash; число.
ret x ~=
+
  ret x ~=
    pop eip
+
      pop eip
    add esp, x (выкинуть со стека x байт)
+
      add esp, x ; выкинуть со стека x байт
  
 
//пара команд, которые все любят
 
//пара команд, которые все любят
nop -- Команда, Которая Ничего Не Делает
+
  nop &mdash; Команда, Которая Ничего Не Делает
ud2 -- Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ
+
  ud2 &mdash; Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ

Текущая версия на 17:11, 3 мая 2012

Регистры

Регистры — память процессора.

Регистры общего назначения (в скобках — изначальное предназначение)

  eax (accumulator)
  ebx (base)
  ecx (counter)
  edx (data)
  ebp (stack base pointer)
  esp (stack pointer) трогать нежелательно аккуратно, стек ПЫЩЬ
  esi (source)
  edi (destination)

То, что вторые буквы — первые четыре буквы латинского алфавита — случайность. Это видно в том числе и по тому, что их естественный порядок виден при переносе на стек: eax, edx, ecx, ebx...

Ещё регистры:

  eip (instruction pointer)
  eflags: куча битов, которые означают флаги. пример:
  zf — 1, если последняя операция вернула 0, 1 otherwise
  cf — знак переноса (если результат последней операции не влезает в нужное кол-во битов и один переносится)
  sf — знаковый бит. 0 — полож., 1 — отриц.

На вышеперечисленные флаги влияет результат выполнения ТОЛЬКО арифметических операций, в том числе comp и test.

  df — направление выполнения строковых операций.

Регистры e** 32-битны. На 64-битных системах действует следующие наименования:

[math] \underbrace {\fbox{32 \quad\quad\quad\quad} \overbrace{\fbox{16 \quad\quad} \overbrace{ \underbrace{\fbox{8 \quad}}_{*h} \underbrace{\fbox{8 \quad}}_{*l} }^{*x} }^{e*x} }_{r*x} [/math]

Собственно, этимология названий: буква e (extended) появилась в названиях регистров с 386 процессором и 32-битным режимом работы. h и l пошли от high и low соответственно (старший и младший байты)

Обзор команд

Команды загрузки

  mov register|memory, register|memory|immediate ; записать значение второго операнда в первый, размер данных должен совпадать
  mov al, 5 ; загрузить в al 5
  mov cx, di ; скопировать значение di в cx
  movzx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются нулями
  movsx register, register|memory ; копирование значений меньшей разрядности в регистр большей разрядности, недостающие биты заполняются знаковым битом
  cmov(cc) register, register|memory ; cc — условие, команда выполняется только если условие верно
  xchg register|memory, register|memory ; обмен значений
  bswap register ; меняет порядок байт в регистре на обратный

Уточнение про порядок байт:

  //little-endian:
  //байты загружаются в память в порядке:
  //0...7...15...31
  //
  //big-endian:
  //31...15...7...0
  //
  //процессоры: x86 — little-endian, при обмене данными по сети — big-endian

Обращаться можно и к памяти:

  mov eax, [ebx] ; загрузка в eax того, что находится по адресу ebx (в C выглядело бы как eax = *ebx)

[] - косвенная адресация. не более одних скобок за раз, ** недопустимы. Может использоваться как для левого, так и правого аргумента mov. В команде может быть не больше одного обращения к памяти.

  mov [eax], [ebx] ; так делать нельзя!

Оперативную память попортить нельзя, она разделена между процессами.

В квадратных скобках при использовании 16-битных регистров можно писать три опциональные части:

  bx   si
     +     +/- offset (16-битное число)
  bp   di

В квадратных скобках при использовании 32-битных регистров позволено писать довольно много разных вещей:

  eax
  ebx   eax
  ecx   ebx   1
  edx   ecx   2 
      + edx *   +/- offset (32-битное число)
  ebp   ebp   4
  esp   edi   8
  edi   esi
  esi

Работа со стеком

  push register|memory|immediate ; положить значение на стек

Сишный псевдокод:

  esp -= sizeof(eax) // минус потому, что стек "растёт вниз", чем он больше, тем на меньшее число указывает esp (и наоборот)
  *esp = eax

Эквивалентный код на ассемблере:

  sub esp, 4
  mov [esp], eax
  pop register|memory ; записать значение верхнего элемента стека в регистр/память

Как это выглядит на ассемблере:

  mov eax, [esp]
  add esp, 4

Стек-пойнтеру (sp) в 16-битном режиме довольно желательно бы быть чётным, в 32-битном — кратным четырём: всё будет работать существенно быстрее. Стек — хорошее быстрое временное хранилище. Не слишком большое (1-2 мб). //malloc и free — системные функции, их можно свободно использовать в любом языке (и в Си, и здесь, и в дельфи)

  pusha(d) ; сохранить в стек все 8 регистров общего назначения
  popa(d) ; вытащить их из стека

(push|pop)ad работают с 32-битными регистрами, (push|pop)a — c 16-битными. При восстановлении не меняется esp (логично, дабы с esp не произошло трешака). //"Вас никто не заставляет делать разумные вещи. Если вы хотите делать безумные вещи, вы можете это делать совершенно свободно!"

  • Начало второй лекции. Начало пропущено:*

Арифметические команды

  add eax, ebx ; eax += ebx
  adc eax, ebx ; eax += ebx + CF // CF - флаг переноса
  sub / sbb
  mul / div
  imul / idiv
  mul OP — после этой команды в edx:eax 64 бита — результат умножения eax на OP
  div OP — после этой команды в edx edx:eax / ebx
                            в eax же eds:eax % ebx

как при делении на ноль, так и переполнении при делении вылетает исключение и программу убивает система. последнее может лечиться обнулением edx'а. //в документации указана обработка всего этого в 8- и 16-битных случаях

  imul и idiv — аналогично mul и div, НО: работают со знаковыми числами

кроме того, у imul есть такие формы записи:

  imul ecx, ebx == ecx *= ebx (верхние биты обнуляются)
  imul ecx, ebx, 5 == ecx = ebx * 5
  inc и dec. принимают один операнд, увеличивают (уменьшают) его на один; не трогают флаг переноса

Команда, которая неизвестно для чего изначально нужна.

  lea (load effective adress):
  lea eax, [ebx+ecx] ; фактически, трёхоперандное сложение. eax = abx + ecx
  lea eax, [ebx+ebx*4] ; eax = ebx * 5 (yasm поддерживает запись вида lea eax, [ebx*5], но по стандарту так нельзя)

Как быстрее всего умножить число на 10?

  lea eax, [ebx*5]
  add eax, eax

Команды логики

  and, or, xor, not. Все, кроме not имеют два аргумента.
  xor eax, eax — быстрое обнуление регистра

команда mov anything, 0 имеет смысл, но редко. когда: 1) когда нужно сохранить флаги. логика их меняет 2) когда нужно обнулять память. ксорить память нельзя (её можно указать лишь один раз)

Команды cmp/test аналогичны командам (sub/and). результаты не пишут никуда (!!!), но ставят флаги.

  test eax,eax — самый короткий способ проверить, 0 ли регистр

Команды сдвига

Во всех сдвигах последний сдвигаемый бит идёт в cf

  shr/shl — сишные сдвиги (>>, <<). второй аргумент команды — либо константа, либо cl. деление на степени двойки — самое быстрые, ибо сдвиги, которые гораздо более быстрее, нежели "реальное" деление.
  sar — сдвиг справо со знаком. Сишное >> компилируется в зависимости от "знаковости" переменной в shr/sar. shl, очевидно, == sal.
  shrd/shld принимают три аргумента.
  shrd eax,ebx,3:
  ebx,eax двигаются на 3 вправо (О_О). первые k (где k — третий аргумент) битов того, что указано в первом аргументе == последние k того, что указано во втором.

Команды вращения (о_О)

  rol, ror: тупое вращение по кругу, ничего ниоткуда не берётся, не теряется и не придумывается.
  rcl, rcr:
  rcl:
  cf <- 31...<первый аргумент>...0-^
  |                                |
  v-------------------------------->

Аналогично rol/ror'у, но флаг cf — часть того, что сдвигается (т.о., сдвигаются 33 бита)

Команды передачи управления

  jmp метка (jmp eax)

фактически — mov eip, smth

Так же есть условия, зависящие от флагов:

  j(cc)
  ex.: jz — условный переход, если флаг нуля установлен.
  jnz — переход, если флаг нуля не установлен.
  jc, jnc — аналогично для флага переноса, и так далее... тысячи их!
  ja, jb, jae, jbe, jl, jg, jle, jge (к каждой можно устроить отрицание(
  ja — above (если больше, без знака)
  jb — below (если меньше, без знака)
  jae — above/equal (больше или равно без знака)
  jbe — below/equal (меньше или равно без знака)
  jg  \
  jl  |} — аналогично вышеуказанным,
  jge |} — но с учётом знаков.
  jle /

к каждым командам существуют отрицания. j(n)cc логично, что, например, jb == jnae

те же самые условия используеются в командах условной загрузки (mov(cc))

  call — аналогично jmp, НО.
  call x ~= 
      | push eip
      | mov eip, x
  ret ~= 
      | pop eip
  

У ret есть опциональный параметр — число.

  ret x ~=
      pop eip
      add esp, x ; выкинуть со стека x байт

//пара команд, которые все любят

  nop — Команда, Которая Ничего Не Делает
  ud2 — Команда, Которой Не Существует. Если она выполняется, программа падает с ошибкой. И БУДЕТ ПАДАТЬ. ВСЕГДА. И НЫНЕ. И ПРИСНО. ВО ВЕКИ. ВЕКОВ