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