Ассемблер je: Инструкция JE

Содержание

Инструкция JE







ТВ онлайн: более 100 каналов в HD-качестве

1) Большинство каналов бесплатные. 2) Можно смотреть как на сайте, так и в приложениях для iOS, Android и Smart TV. 3) Множество телесериалов и фильмов бесплатно или за 1 рубль. 4) Несколько платных подписок, в том числе 18+ и телеканал «Дождь». 5) А также записи телепередач, новости, ТВ-шоу и др.
Подробнее…




Инструкция JE выполняет короткий переход, если первый операнд РАВЕН второму операнду при выполнении операции сравнения с помощью команды CMP.


Синтаксис команды JE:


JE МЕТКА


О метках я рассказывал в статье о команде JMP. Повторяться не буду.


Инструкция JE проверяет флаг ZF. Если этот флаг равен 1, то выполняется переход к МЕТКЕ.


Сама же инструкция JE при работе никакие флаги не изменяет.


Как вы понимаете, инструкция JE входит в список команд условного перехода, которых в ассемблере довольно много — на все случаи жизни.


Такое многообразие команд условного перехода требуется для того, чтобы у программиста было больше возможностей при разработке программ.


Пример использования команды JE приведён ниже:


	.model	tiny
	.code
	ORG 	100h
	
start:	

	MOV AL, 5      ; AL = 5
	MOV AH, 5      ; AH = 5
	CMP AL, AH     ; AL = AH, ZF = 1
	JE  lblJE
	;Так как AL = AH, то УСЛОВИЕ ПЕРЕХОДА ВЫПОЛНЯЕТСЯ,
	;а следующие инструкции НЕ выполняются, 
	;так как программа переходит к метке lblJE
	MOV AL, 5      ; AL = 5
	MOV AH, 6      ; AH = 6
	CMP AL, AH     ; AL JBE lblJE
	
lblJE:	
	MOV AH, 15
	
	END	start


В комментариях всё подробно расписано, поэтому что-то ещё добавлять нет смысла. Если нужно более подробно, то см. видео выше.


В конце, как всегда, расскажу, почему эта команда ассемблера называется JE.
Буква J — это первая буква слова слова JUMP (прыжок,
переход). А буква Е — это первая буква слова EQUAL (одинаковый, равный). Таким образом набор слов, от которых взяты первые буквы имени команды JE, можно перевести как “переход, если равно”.





Первые шаги в программирование

Главный вопрос начинающего программиста – с чего начать? Вроде бы есть желание, но иногда «не знаешь, как начать думать, чтобы до такого додуматься».
У человека, который никогда не имел дело с информационными технологиями, даже простые вопросы могут вызвать большие трудности и отнять много времени на решение.
Подробнее…


Ассемблер. Условия | Уроки Ассемблера

  Обновл. 20 Июл 2021  | 

К условиям в ассемблере относятся инструкции циклов и ветвления. Эти инструкции могут изменять поток выполнения кода в программе.

Типы прыжков

Есть 2 типа выполнения условий в ассемблере:

   Прыжок без условия (или «безусловный прыжок») — выполняется инструкцией JMP. Выполнение данной инструкции часто включает в себя передачу управления в адрес инструкции, которая не следует за выполняемой в настоящее время инструкцией. Результатом передачи управления может быть выполнение нового набора инструкций или повторное выполнение текущих инструкций.

   Прыжок с условием (или «условный прыжок») — выполняется с помощью инструкций типа J<условие> и зависит от самого условия. Условные инструкции, изменяя значение смещения в регистре IP, передают управление, прерывая последовательный поток выполнения кода.

Прежде чем разбираться с этими двумя типами инструкций детально, давайте рассмотрим инструкцию CMP.

Инструкция CMP

Инструкция CMP (от англ. «COMPARE») сравнивает два операнда. Фактически, она выполняет операцию вычитания между двумя операндами для проверки того, равны ли эти операнды или нет. Используется вместе с инструкцией условного прыжка.

Синтаксис инструкции CMP:

CMP назначение, источник

Инструкция CMP сравнивает два числовых поля. Операнд назначения может находиться либо в регистре, либо в памяти. Исходным операндом (источник) могут быть константы, регистры или память. Например:

CMP DX, 00 ; сравниваем значение регистра DX с нулем
JE L7 ; если true, то переходим к метке L7
.
.
L7: …

CMP DX, 00  ; сравниваем значение регистра DX с нулем

JE  L7      ; если true, то переходим к метке L7

.

.

L7: …

Инструкция CMP часто используется для проверки того, достигла ли переменная-счетчик максимального количества раз выполнения цикла или нет. Например:

INC EDX
CMP EDX, 10 ; сравниваем, достиг ли счетчик значения 10 или нет
JLE LP1 ; если его значение меньше или равно 10, то тогда переходим к LP1

INC EDX

CMP EDX, 10 ; сравниваем, достиг ли счетчик значения 10 или нет

JLE LP1     ; если его значение меньше или равно 10, то тогда переходим к LP1

Прыжок без условия

Как мы уже говорили, безусловный прыжок выполняется инструкцией JMP, которая включает в себя имя метки, куда следует перебросить точку выполнения программы:

JMP    label

В следующем примере мы рассмотрим использование инструкции JMP:

MOV AX, 00 ; инициализируем регистр AX значением 0
MOV BX, 00 ; инициализируем регистр BX значением 0
MOV CX, 01 ; инициализируем регистр CX значением 1
L20:
ADD AX, 01 ; выполняем инкремент регистра AX
ADD BX, AX ; добавляем AX к BX
SHL CX, 1 ; сдвиг влево регистра CX, что, в свою очередь, удваивает его значение
JMP L20 ; повторно выполняем стейтменты

MOV  AX, 00    ; инициализируем регистр AX значением 0

MOV  BX, 00    ; инициализируем регистр BX значением 0

MOV  CX, 01    ; инициализируем регистр CX значением 1

L20:

ADD  AX, 01    ; выполняем инкремент регистра AX

ADD  BX, AX    ; добавляем AX к BX

SHL  CX, 1     ; сдвиг влево регистра CX, что, в свою очередь, удваивает его значение

JMP  L20       ; повторно выполняем стейтменты

Прыжок с условием

Если при выполнении операции условного прыжка выполняется обозначенное условие, то точка выполнения программы переносится в указанную инструкцию. Существует множество инструкций условного прыжка, в зависимости от условия и данных.

Ниже приведены инструкции условного прыжка, используемые для данных со знаком в арифметических операциях:

ИнструкцияОписаниеТестируемые флаги
JE/JZJump Equal (равно) или Jump Zero (ноль)ZF
JNE/JNZJump Not Equal (не равно) или Jump Not Zero (не ноль)ZF
JG/JNLEJump Greater (больше) или Jump Not Less/Equal (не меньше/равно)OF, SF, ZF
JGE/JNLJump Greater/Equal (больше/равно) или Jump Not Less (не меньше)OF, SF
JL/JNGEJump Less (меньше) или Jump Not Greater/Equal (не больше/равно)OF, SF
JLE/JNGJump Less/Equal (меньше/равно) или Jump Not Greater (не больше)OF, SF, ZF

В следующей таблице приведены инструкции условного прыжка, используемые для данных без знака в логических операциях:

ИнструкцияОписаниеТестируемые флаги
JE/JZJump Equal (равно) или Jump Zero (ноль)ZF
JNE/JNZJump Not Equal (не равно) или Jump Not Zero (не ноль)ZF
JA/JNBEJump Above (больше) или Jump Not Below/Equal (не меньше/равно)CF, ZF
JAE/JNBJump Above/Equal (больше/равно) или Jump Not Below (не меньше)CF
JB/JNAEJump Below (меньше) или Jump Not Above/Equal (не больше/равно)CF
JBE/JNAJump Below/Equal (меньше/равно) или Jump Not Above (не больше)AF, CF

Следующие инструкции условного прыжка имеют специальное использование и проверяют значение флагов:

ИнструкцияОписаниеТестируемые флаги
JCXZJump если CX равно Zeronone
JCJump если Carry (перенос)CF
JNCJump если No Carry (нет переноса)CF
JOJump если Overflow (переполнение)OF
JNOJump если No Overflow (нет переполнения)OF
JP/JPEJump Parity или Jump Parity Even (если чётность)PF
JNP/JPOJump No Parity или Jump Parity Odd (если нечётность)PF
JSJump Sign (отрицательное значение)SF
JNSJump No Sign (положительное значение)SF

Пример синтаксиса набора инструкций типа J<условие>:

CMP AL, BL
JE EQUAL
CMP AL, BH
JE EQUAL
CMP AL, CL
JE EQUAL
NON_EQUAL: …
EQUAL: …

CMP AL, BL

JE EQUAL

CMP AL, BH

JE EQUAL

CMP AL, CL

JE EQUAL

NON_EQUAL: …

EQUAL: …

В качестве примера рассмотрим следующую программу, которая определяет и выводит на экран наибольшую из 3 целочисленных переменных:

section .text
global _start ; должно быть объявлено для использования gcc

_start: ; сообщаем линкеру входную точку
mov ecx, [num1]
cmp ecx, [num2]
jg check_third_num
mov ecx, [num2]

check_third_num:

cmp ecx, [num3]
jg _exit
mov ecx, [num3]

_exit:

mov [largest], ecx
mov ecx,msg
mov edx, len
mov ebx,1 ; файловый дескриптор (stdout)
mov eax,4 ; номер системного вызова (sys_write)
int 0x80 ; вызов ядра

mov ecx,largest
mov edx, 2
mov ebx,1 ; файловый дескриптор (stdout)
mov eax,4 ; номер системного вызова (sys_write)
int 0x80 ; вызов ядра

mov eax, 1
int 80h

section .data

msg db «The largest digit is: «, 0xA,0xD
len equ $- msg
num1 dd ’47’
num2 dd ’22’
num3 dd ’31’

segment .bss
largest resb 2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

section .text

   global _start         ; должно быть объявлено для использования gcc

 

_start:                  ; сообщаем линкеру входную точку

   mov   ecx, [num1]

   cmp   ecx, [num2]

   jg    check_third_num

   mov   ecx, [num2]

  

check_third_num:

 

   cmp   ecx, [num3]

   jg    _exit

   mov   ecx, [num3]

  

_exit:

  

   mov   [largest], ecx

   mov   ecx,msg

   mov   edx, len

   mov   ebx,1 ; файловый дескриптор (stdout)

   mov   eax,4 ; номер системного вызова (sys_write)

   int   0x80 ; вызов ядра

   mov   ecx,largest

   mov   edx, 2

   mov   ebx,1 ; файловый дескриптор (stdout)

   mov   eax,4 ; номер системного вызова (sys_write)

   int   0x80 ; вызов ядра

    

   mov   eax, 1

   int   80h

 

section .data

  

   msg db «The largest digit is: «, 0xA,0xD

   len equ $- msg

   num1 dd ’47’

   num2 dd ’22’

   num3 dd ’31’

 

segment .bss

   largest resb 2

Результат выполнения программы:

The largest digit is:
47

Оценить статью:

Загрузка…

Поделиться в социальных сетях:

Разница между JE/JNE и JZ/JNZ

В коде x86 assembly являются ли JE и JNE точно такими же, как JZ и JNZ ?

assembly

x86

Поделиться

Источник


Daniel Hanrahan    

10 января 2013 в 20:53

3 ответа


  • Assembly — JZ инструкция После CMP

    У меня есть следующая инструкция: cmp al, 1 jz mub Когда al равен 2 (10 в двоичной системе). Что будет делать эта инструкция? Как я знаю, я могу использовать JE,JNE, JA и т. д., Но что означает jz после инструкции cmp? Спасибо

  • x86 jnz после xor?

    После использования IDA Pro для разборки x86 dll я нашел этот код (комментарии, добавленные мной в коде pusedo-c. Надеюсь, они правы): test ebx, ebx ; if (ebx == false) jz short loc_6385A34B ; Jump to 0x6385a34b mov eax, [ebx+84h] ; eax = *(ebx+0x84) mov ecx, [esi+84h] ; ecx = *(esi+0x84) mov al,…



127

JE и JZ -это просто разные имена для одного и того же:
условный переход, когда ZF (флаг «zero») равен 1.

(Аналогично, JNE и JNZ -это просто разные имена для условного перехода
, когда ZF равно 0.)

Вы можете использовать их взаимозаменяемо, но вы должны использовать их в зависимости от
того, что вы делаете:

  • JZ / JNZ более подходят, когда вы явно тестируете
    что-то равное нулю:

    dec  ecx
    jz   counter_is_now_zero
    
  • JE и JNE более уместны после инструкции CMP :

    cmp  edx, 42
    je   the_answer_is_42
    

    (Инструкция CMP выполняет вычитание и отбрасывает значение результата, сохраняя при этом флаги; именно поэтому вы получаете ZF=1 , когда операнды равны
    , и ZF=0 , когда они не равны.)

Поделиться


Matthew Slattery    

10 января 2013 в 21:31



36

Из ссылки на набор инструкций Intel , JE и JZ имеют один и тот же код операции ( 74 для rel8 / 0F 84 для rel 16/32), а также JNE и JNZ ( 75 для rel8 / 0F 85 для rel 16/32)).

JE и JZ они оба проверяют наличие ZF (или нулевого флага), хотя руководство немного отличается в описаниях первого использования JE rel8 и JZ rel8 ZF , но в основном они одинаковы.

Вот выдержка из страниц руководства 464, 465 и 467.

 Op Code    | mnemonic  | Description
 -----------|-----------|-----------------------------------------------  
 74 cb      | JE rel8   | Jump short if equal (ZF=1).
 74 cb      | JZ rel8   | Jump short if zero (ZF ← 1).

 0F 84 cw   | JE rel16  | Jump near if equal (ZF=1). Not supported in 64-bit mode.
 0F 84 cw   | JZ rel16  | Jump near if 0 (ZF=1). Not supported in 64-bit mode.

 0F 84 cd   | JE rel32  | Jump near if equal (ZF=1).
 0F 84 cd   | JZ rel32  | Jump near if 0 (ZF=1).

 75 cb      | JNE rel8  | Jump short if not equal (ZF=0).
 75 cb      | JNZ rel8  | Jump short if not zero (ZF=0).

 0F 85 cd   | JNE rel32 | Jump near if not equal (ZF=0).
 0F 85 cd   | JNZ rel32 | Jump near if not zero (ZF=0).

Поделиться


higuaro    

10 января 2013 в 21:32



-5

  je : Jump if equal:

  399  3fb:   64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
  400  402:   00 00
  401  404:   74 05                   je     40b <sims_get_counter+0x51>

Поделиться


leesagacious    

05 июля 2019 в 06:41


  • О je в assembly

    77f4bccd 741a je SHLWAPI!UrlIsW+0x3d (77f4bce9) Согласно этой формуле: 7x xx RIP=RIP+8 bit displacement Конечный адрес должен быть 77f4bccd+1a=77F4BCE7 , 77f4bce9 , но это на самом деле . Почему? UPDATE 77f4bce2 0f85a20a0000 jne SHLWAPI!Ordinal152+0x101 (77f4c78a) Конечный адрес должен быть…

  • NASM меняет JNZ на JNE во время сборки? Почему?

    У меня есть фрагмент кода, который использует JNZ. Когда я собираю и связываю двоичный файл, я вижу, что мой JNZ заменяется на JNE. Я понимаю, что оба они принципиально одинаковы. Но тогда почему NASM меняет его? Кроме того, есть ли какая-либо опция конфигурации, чтобы остановить это изменение во…


Похожие вопросы:

Как работает тест и je/jne

Итак, я начал немного работать с assembly. Я начал со следующих инструкций: test al, al jne 0x1000bffcc Используя отладчик, я хотел, чтобы код не перескакивал на адрес 0x1000bffcc , поэтому я…

MSP430 JC, JNC , JEQ и JNZ

Я просматривал набор инструкций MSP430 и наткнулся на что-то, чего не могу понять. Я, кажется, не могу различить, в чем разница между JC и JNZ и JNC и JEQ . Я понимаю функции JEQ и JNZ , но я…

Как изменить 6 байтовый адрес памяти с je на jne

я играю с языком assembly и gdb, пытаясь изменить адрес памяти: +67 00058093 0f84e8000000 Йе 0x00058181 id хотел бы изменить второй байт, который читает 84 на 85, так что инструкция становится jne….

Assembly — JZ инструкция После CMP

У меня есть следующая инструкция: cmp al, 1 jz mub Когда al равен 2 (10 в двоичной системе). Что будет делать эта инструкция? Как я знаю, я могу использовать JE,JNE, JA и т. д., Но что означает jz…

x86 jnz после xor?

После использования IDA Pro для разборки x86 dll я нашел этот код (комментарии, добавленные мной в коде pusedo-c. Надеюсь, они правы): test ebx, ebx ; if (ebx == false) jz short loc_6385A34B ; Jump…

О je в assembly

77f4bccd 741a je SHLWAPI!UrlIsW+0x3d (77f4bce9) Согласно этой формуле: 7x xx RIP=RIP+8 bit displacement Конечный адрес должен быть 77f4bccd+1a=77F4BCE7 , 77f4bce9 , но это на самом деле . Почему?…

NASM меняет JNZ на JNE во время сборки? Почему?

У меня есть фрагмент кода, который использует JNZ. Когда я собираю и связываю двоичный файл, я вижу, что мой JNZ заменяется на JNE. Я понимаю, что оба они принципиально одинаковы. Но тогда почему…

jnz по указателю не возможен в 386 assembly?

Этот код работает: jz jnzover jmp [esi + 8] jnzover: Есть ли способ написать это в одном коде операции? Когда я наберу jnz [esi + 8] , он скажет expecting pointer type

Разница между assembly нулем и равным

Я полный новичок в огромном мире assembly, и во время обучения я столкнулся со странным явлением. Условные переходы выполняются на основе проверки флагов, чтобы увидеть, как сравниваются…

Почему моя программа работает только с PoS-или neg-номерами?

Мне нужно написать программу деления, которая принимает 2 десятичных числа (они могут быть положительными или отрицательными) и показывает ответ в двоичном коде. Программа работает, но только с…

Управление ходом программы

Короткие условные переходы

Подобно команде JMP, которая выполняет безусловный переход,
существуют команды, которые осуществляют условный переход (переход,
который осуществляется только в том случае, если выполняется определенное
условие). Эти команды разделяются на три группы. Первая группа только проверяет
отдельный флаг, вторая — сравнивает числа со знаком, третья —
сравнивает числа без знака.

Команды перехода, проверяющие одиночный флаг

КомандаОписаниеУсловиеОбратная команда
JZ , JEПереход, если «равно» («нуль»). Т.е. если сравниваемые значения равны, то ZF = 1 и переход выполняется ZF = 1JNZ, JNE
JC , JB, JNAEПереход, если есть перенос («ниже», «не выше или равно»). CF = 1JNC, JNB, JAE
JSПереход по знаку. SF = 1JNS
JOПереход по переполнению. OF = 1JNO
JPE, JPПереход, если есть паритет или паритет четный. PF = 1JPO
JNZ , JNEПереход по «не равно» или по «не нуль» ZF = 0JZ, JE
JNC , JNB, JAEПереход, если нет переноса («выше или равно» или «не ниже»). CF = 0JC, JB, JNAE
JNSПереход, если нет знака. SF = 0JS
JNOПереход, если нет переполнения. OF = 0JO
JPO, JNPПереход, если нет паритета или паритет нечетный. PF = 0JPE, JP

Как вы можете видеть, существуют команды, которые выполняют
одинаковые действия. Это нормально. Они даже ассемблируются в
одинаковый машинный код, поэтому будет неплохо, если вы запомните,
что при компиляции команды JE, после дизассемблирования
вы получите ее как: JZ.
Различные имена используются для того, чтобы делать программы
более легкими для понимания и кодирования.

Команды перехода для чисел со знаками

КомандаОписаниеУсловиеОбратная команда
JE , JZПереход, если «равно» (=).
переход, если «ноль».
ZF = 1JNE, JNZ
JNE , JNZПереход, если «не равно» ().
Переход, если «не ноль».
ZF = 0JE, JZ
JG , JNLEПереход, если «больше» (>).
Переход, если «не меньше или равно» (not <=).
ZF = 0
and
SF = OF
JNG, JLE
JL , JNGEПереход, если «меньше» ().
Переход, если «не больше или равно» (not >=).
SF <> OFJNL, JGE
JGE , JNLПереход, если «больше или равно» (>=).
Переход, если «не меньше» (not <).
SF = OFJNGE, JL
JLE , JNGПереход, если «меньше или равно» (<=).
Переход, если «не больше» (not >).
ZF = 1
or
SF OF
JNLE, JG

<> — этот знак означает «не равно».

Команды перехода для чисел без знаков

КомандаОписаниеУсловиеОбратная команда
JE , JZПереход, если «равно» (=).
Переход, если «ноль».
ZF = 1JNE, JNZ
JNE , JNZПереход, если «не равно» ().
Переход, если «не ноль».
ZF = 0JE, JZ
JA , JNBEПереход, если «выше» (>).
Переход, если «не ниже или равно» (not <=).
CF = 0
and
ZF = 0
JNA, JBE
JB , JNAE, JCПереход, если «ниже» (<).
Переход, если «не выше или равно» (not >=).
Переход по переносу.
CF = 1JNB, JAE, JNC
JAE , JNB, JNCПереход, если «выше или равно» (>=).
Переход, если «не ниже» (not <).
Переход, если «нет переноса».
CF = 0JNAE, JB
JBE , JNAПереход, если «ниже или равно» (<=).
Переход, если «не выше» (not >).
CF = 1
or
ZF = 1
JNBE, JA

Обычно, если требуется сравнить два числовых значения, то
используют команду CMP (она делает то же самое, что и команда
SUB (вычитание), но не сохраняет результат, а влияет
только на флаги.

Логика очень простая, например:

требуется сравнить числа
5 и 2,

5 — 2 = 3

результат — НЕ НОЛЬ (Флаг Нуля — Zero Flag (ZF) установлен в 0).

Другой пример:

требуется сравнить 7 и
7,

7 — 7 = 0

результат — НОЛЬ! (Флаг Нуля — Zero Flag (ZF) установлен в
1 и команды JZ или JE выполнят переход).

Ниже приведен пример команды CMP и условного перехода:

include emu8086.inc

ORG    100h

MOV    AL, 25     ; записать в AL число 25.
MOV    BL, 10     ; записать в BL число 10.

CMP    AL, BL     ; сравнить AL с BL.

JE     equal      ; если AL = BL (ZF = 1), то перейти к метке equal.

PUTC   'N'        ; иначе, если AL <> BL, то продолжить выполнение
JMP    stop       ; программы - напечатать 'N' и перейти к метке stop.

equal:            ; если программа на этой метке,
PUTC   'Y'        ; то AL = BL, поэтому выводим на экран 'Y'.

stop:

RET               ; сюда приходим в любом случае

END

Попробуйте вышеописанный пример с различными числами в AL и BL,
откройте флаги, щелкнув по кнопке [FLAGS]. Используйте
[Single Step — пошаговый режим] и наблюдайте за происходящим.
Не забывайте перекомпилировать и перезагружать вашу программу после
кажого сделанного вами в ней изменения (используйте клавишу F5).


Все условные переходы имеют одно серьезное ограничение — в отличие от
команды JMP, они могут выполнять переход только на 127
байтов вперед или на 128 байтов назад (учтите, что большие
команды ассемблируются в 3 и более байтов).

Мы можем легко преодолеть это ограничение, используя следующий метод:

  • Взять обратную команду из приведенной выше таблицы
    и выполнить переход к метке label_x.
  • Использовать команду JMP для перехода
    к нужному участку программы.
  • Определить метку label_x: только после
    команды JMP.

label_x: — может быть любым именем.

Пример:

include emu8086.inc

ORG    100h

MOV    AL, 25     ; записать в AL число 25.
MOV    BL, 10     ; записать в BL число 10.

CMP    AL, BL     ; сравнить AL с BL.



JNE    not_equal  ; переход, если AL <> BL (ZF = 0).
JMP    equal
not_equal:


; представим, что здесь у нас
; размещается код, который ассмеблируется
; более чем в 127 байтов...


PUTC   'N'        ; если мы оказались здесь, то AL <> BL,
JMP    stop       ; тогда печатаем 'N', и переходим к метке stop.

equal:            ; если мы оказались здесь,
PUTC   'Y'        ; то AL = BL, тогда печатаем 'Y'.

stop:

RET               ; сюда приходим в любом случае.

END

Дневники чайника

Дневники чайника

Циклы, ветвления и регистр флагов

В предыдущей программе 25 раз повторяется вывод строки текста на экран.
Можно было бы написать 25 раз один и тот же код. Но что если нужно повторить программу 10 000 раз?
А если нужно повторять её до тех пор, пока не получишь правильного решения?
Для этого мы заставляем нужный участок программы повторяться несколько раз.
В данном примере повторяется почти вся программа, но это совсем не обязательно.
Сейчас я приведу упрощённую схему этой программы, это будет практически алгоритм.

01 Очистить экран и задать размерность 80×25

02 Переменные X,Y принять за позицию вывода текста

03 Вывести текст в указанное место

04 Увеличить Y на 1, X на 3

05 Если Y ещё не стал 25d, то перейти к шагу 2. Если стал — программа выполнена

Очень важно, чтоб вы поняли, что 1 шаг алгоритма — это вовсе не одна строка в коде программы.

Обязательно соотнесите каждую строку реальной программы с представленным алгоритмом.
Когда вы поймёте все отличия такой записи от кода Ассемблера, вы поймёте, как писать программы.
Это самый простой способ начать думать на Асме.

Шаг 5 предполагает выбор между выполнением выхода из программы или возвратом к шагу 2.

Чтобы такой выбор предложить процессору, лучше всего использовать специальный регистр флагов.

И действительно, я разъяснил действие двух команд так, как будто они что-то друг другу говорят:


Адреса имена         операнды
011D:  CMP  byte ptr [176h],19h ;сверяется значение байта в памяти с числом 25d
0122:  JNE           0105h      ;и если эти значения не равны, 
                                ;то прыг на выполнение адреса 105

У команд нет волшебных свойств, и они никак не связаны друг с другом!

Всё, что они могут, это менять переменные в памяти и регистры процессора (есть, конечно, ещё специальные команды ввода/вывода).

Команда CMP сравнивает два числа путём вычитания и в зависимости от результата меняет биты в регистре флагов.

Команда JNZ, второе написание JNE (пишите как нравится). Эта команда осуществляет прыжок, если выключен флаг нуля.
Про эти две команды я подробно напишу после того, как расскажу об устройстве регистра флагов.

Регистр флагов

Если я ничего не забыл, то регистр флагов будет завершать мой рассказ о регистрах процессора.
При отладке обычных программ другие регистры процессора изучать не обязательно.
В отладчиках они даже не отображаются постоянно. Если интересно, посмотрите в учебнике.

Физическое устройство — такое же, как и у других регистров (32 бита).

А вот назначение — специальное. В этот регистр нельзя просто записать значение командой MOV. У него даже нет имени обращения.
Он изменяется по-другому. И он значительно отличается от первого специального регистра — EIP (указатель текущей инструкции).

EIP имеет имя обращения, и он цельный по содержанию регистр, в этом его сходство с регистрами общего назначения.

Я понимаю это так. Содержимое всех РОН программист может воспринимать как хранилище:

  • Числа (адреса или значения).
  • Или чисел (по тетрадам, байтам, словам и т.п.).
  • И, возможно, битовой информации.

Регистр EIP (или IP) имеет смысл воспринимать только как хранинилище числа, и оно всегда означает адрес.

А вот регистр флагов — хранилище битовой информации!

Что я имею в виду под «битовой информацией»?

Разумеется, что с точки зрения компьютера любая информация битовая.
Однако с точки зрения программиста, даже низкоуровневого, есть числа, которые имеет смысл делить,
умножать и производить с ними сложные вычисления.
В этих числах важнее целое, чем каждый разряд по отдельности (примечание).

И есть такие байты, каждый бит которых означает самостоятельное явление.
Состояние процессора, результат сравнения и многое другое.
Конечно, эти байты тоже могут выглядеть как hex число, но оно, наоборот, в целом виде не имеет явного смысла.

Регистр флагов — это как раз 32 бита, важных по отдельности.

Матрос, ты знаешь, каким флагом обозначают занятую посадочную полосу на орбитальной станции?

Стыдно такого не знать. Учи космический формат, иначе всю жизнь будешь драить потолки в столовой.

За исключением одного парного флага каждый бит обозвали флагом. Просто от балды. Могли бы обозвать светофором или знаком.

У всех флагов есть имена. Радует то, что для нас интересны далеко не все флаги.
Я буду рассказывать о них по одному.

Сам когда-то прочёл сразу обо всех и ничего не запомнил, а вот при написании программ всё быстро усвоилось.
5-6 дней смотрел в справочник, а потом как-то само запомнилось (то, что было нужно, а точнее 3 флага :)).

Сегодня я расскажу о самом главном для программиста бит-флаге.

ZF (Zero Flag — флаг нуля)

Причём рассказывать о нём, перечисляя все команды, которые его меняют или смотрят, — бесполезно, так как это будет половина команд Ассемблера.
Флаг нуля включается, когда в результате действия команды переменная (часто мнимая) обнуляется, и выключается, когда переменная становится не ноль.
Но так, естественно, делают не все команды (mov не меняет флаги, арифметические и некоторые другие команды меняют флаги…).

Самое интересное, что флаг нуля переключается при сравнении переменных.

Я расскажу, что с ZF делает команда CMP. Команду cmp мы не будем сразу же описывать полностью, ведь она может менять 6 флагов.
А нам на первых порах все они совершенно не нужны.

Сегодня мы столкнулись с тем случаем, когда для нас важен лишь флаг ZF.


Адрес имя            операнды
011D: CMP   byte ptr [176h],19h ; Сверяется значение байта в памяти с числом 25d

Как и у большинства команд, при cmp в квадратных скобках указывается адрес памяти, по которому будет производиться действие.

Команда CMP
ПроисхождениеОт англ. слова compare — сравнивать
ФорматCMP
ДействиеX=операнд1 — операнд2
в соответствии с X-результатом измененяет 6 флагов
ПримечаниеОперанды не меняются (x — мнимый)

На самом деле всё элементарно. Например, относительно флага нуля (ZF) действие команды выглядит так:

MOV AH, 10h &nbsp&nbsp ; для наглядности присвоим AH значение 10h

CMP AH, 8 &nbsp&nbsp&nbsp&nbsp ; ZF = 0

CMP AH, 10h &nbsp&nbsp ; ZF = 1

CMP AH, 0A0h &nbsp ; ZF = 0

И всё. Про флаг нуля практически вся теория. Теперь надо потренироваться на практике. Запустите в отладчике prax03a.com.

CodeView показывает флаги по состоянию.

NZ - означает, что ZF=0,
ZR - означает, что ZF=1,
подсветка означает изменение состояния.

prax03a.com:

00000000: FEC0         inc  al
00000002: 3D0400       cmp  ax,00004
00000005: 40           inc  ax
00000006: 3D0400       cmp  ax,00004
00000009: FEC0         inc  al
0000000B: 3D0400       cmp  ax,00004
0000000E: 40           inc  ax
0000000F: 3D0400       cmp  ax,00004
00000012: FEC4         inc  ah
00000014: 3D0400       cmp  ax,00004
00000017: 3D0401       cmp  ax,00104
0000001A: 3D0401       cmp  ax,00104
0000001D: 3D0100       cmp  ax,00001
00000020: FECC         dec  ah
00000022: 48           dec  ax
00000023: 48           dec  ax
00000024: 48           dec  ax
00000025: 48           dec  ax
00000026: 40           inc  ax
00000027: 66B805000000 mov  eax,000000005
0000002D: 66B800000000 mov  eax,000000000
00000033: 6683F800     cmp  eax,000
00000037: 6683C009     add  eax,009
0000003B: 6683E809     sub  eax,009
0000003F: 66B807000000 mov  eax,000000007
00000045: 6633C0       xor  eax,eax
00000048: 6683F801     cmp  eax,001
0000004C: C606610101   mov  b,[0161],001
00000051: 803E610101   cmp  b,[0161],001
00000056: 803E610100   cmp  b,[0161],000
0000005B: FE0E6101     dec  b,[0161]
0000005F: CD20         int  020

Обязательно смотрите в отладчике подобные примеры, не ленитесь.
Потому что если вы увидите своими глазами, что происходит в регистрах, знания будут сохранены в полезную область памяти.
Так уж устроен человеческий мозг. Пощупал — значит реально, значит запомню.
Вам надо понять, когда будет меняться флаг ZF и почему это будет происходить. Это очень важный урок!

Напишите на листке бумаги, при каких обстоятельствах флаг нуля выключается и при каких включается.
Я не случайно прошу вас это сделать. Если вы собираетесь заняться исследованием программ, самая важная вещь для вас — это флаг нуля.
Подобная выписка поможет быстро разобраться с принятием решений в коде программ.

Вернёмся к предыдущему примеру (prax03.com). Мы разобрались, что CMP меняет флаг нуля. Теперь следующая строка.

00000022: 75E1 jne 000000005 ;если значения не равны, прыг на выполнение 5-го байта

Команда условного перехода JNZ (она же JNE)
ПроисхождениеОт англ. слов Jmp if Not Zero — Прыгнуть если не ноль
ФорматJNZ метка
ДействиеЕсли ZF=0, то действие похоже на JMP (смена EIP на указанный адрес, то есть прыг куда сказали).

Если ZF=1, то действие как у NOP (смена EIP на адрес следующей команды, то есть ничего не происходит).
ПримечаниеКоманда противоположного действия JZ (JE).

Вообще, надо сказать, что у большинства прыжковых команд Ассемблера два написания,
кроме того, разные диалекты (MASM’а,FASM’а,Heiw’а и т.п.) вносят своё написание других команд,
но особой путаницы это пока не создаёт.

Теперь, когда вы знаете все команды prax03.com, можно его как следует разобрать.


00000000: B80300     mov  ax,00003
00000003: CD10       int  010

В строке 0 происходит присвоение регистру AX значения 0003, причём если EAX был бы не ноль, изменения выглядели бы так: ????0003.
В строке 3 вызывается BIOS-прерывание, которое при AL=3 переключает видеорежим в 80×25 текстовых ячеек и чистит экран.

После выполнения этих строк:

EAX=00000003
Появляется экран ДОС (текстовый режим 80×25)

00000005: B402       mov  ah,002
00000007: 8B167501   mov  dx,[0175]
0000000B: CD10       int  010

В строке 5 меняется значение регистра AH.

В строке 7 происходит загрузка регистра DX значениями из памяти. Значения будут размером в регистр (16 бит).
Два этих байта при старте программы равны 00 00. А дальше программа будет их менять.

В строке 0Bh происходит вызов прерывания, которое установит курсор в положение DH — колонка, DL — строка.
В первый раз DH=0, DL=0, значит строка будет печататься от левой верхней ячейки на экране.


После выполнения этих строк:

EAX=00000203

Курсор меняет положение


0000000D: FEC6       inc  dh
0000000F: 80C203     add  dl,003
00000012: 89167501   mov  [0175],dx

В строке 0Dh происходит увеличение на 1 значения регистра DH.

В строке 0Fh происходит увеличение на 3 значения регистра DL.

В строке 12h DH и DL сохраняются в память, причём как целое.
А это значит, что младший байт регистра DX будет в памяти раньше (по адресу 175h),
соответственно, старший байт будет следующим (по адресу 176h).

После выполнения этих строк:

EDX=0000????

&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp + +

&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp 1 3

Память:

0175h здесь будет число из DL (будущее значение колонки курсора)

0176h здесь будет число из DH (будущее значение строки курсора и по совместительству счетчик повторений цикла)


00000016: B409       mov  ah,009
00000018: BA5001     mov  dx,00150
0000001B: CD21       int  021

В строке 16h снова происходит присвоение регистру AH значения 09.

В строке 18h меняется регистр DX. Теперь там будет адрес строки текста.

В строке 1Bh вызывается прерывание ДОС-функций. При AH=9 это прерывание выполняет функцию вывода на экран строки текста (указатель на строку в DX).



0000001D: 803E760119 cmp  b,[0176],019
00000022: 75E1       jne  000000005

Вот самое интересное в этой программе.

В строке 1Dh происходит сравнение содержимого в памяти по адресу 0176h со значением 19h.
В памяти по адресу 175h находится word (2 байта), адрес 176h (старший байт слова) сейчас содержит число,
которое при следующем проходе цикла должно стать номером строки для вывода текста.
19h = 25d — ровно столько строк в нашем видеорежиме. Однако начали мы с нулевой строки, значит 25d — это уже двадцать шестая строка.
Она оказалась бы за пределами видимости, а этого мы не хотим. Именно поэтому мы проверяем на значение 25 — будущее значение строки.
Когда в памяти будет 19h, то есть 25d, на экране все строки уже будут заполнены.

Итак, если значение в памяти равно 19h, то флаг нуля (ZF) будет включен командой cmp, или, как иногда говорят — флаг поднят.

В строке 22h происходит выяснение, поднят ли флаг нуля.
Первые 24d раза флаг нуля опущен (ZF=0). И соответственно следующая команда,
которая будет выполняться, находится в памяти по адресу 0105 (а в файле 05).
Но когда ZF включится, вместо прыжка будет «пустое действие» — просто EIP станет указывать на следующую команду.
То есть в любом случае действие команд перехода заключается в изменении EIP.

Значит, в строке 22h происходит условный переход, а условие перехода ZF=0.

После выполнения этих строк:

Ничего не меняется, кроме

ZF=? и

EIP= 124h или 105.

00000024: B410       mov  ah,010
00000026: CD16       int  016

Это код, вызывающий паузу до нажатия клавиши.


00000028: CD20       int  020

Подпрограмма завершения.


Видите, организовать цикл на Ассемблере совсем не сложно.
Хотя этот цикл я сначала построил неправильно и только в ходе отладки обнаружил ошибку.
Что ещё раз доказывает: отладчик — главный инструмент программиста.

Насчёт ветвлений. Можно их делать точно так же, как и циклы, только если веток очень много, получится неэффективно.
Пример: вы ждёте конкретное сообщение от пользователя и вам нужно обработать по-разному 200 или 500 действий юзера.
Ну, не перебирать же всё подряд на «да/нет»… Хотя большинство программистов и языков программирования именно так и делают :).
Поэтому и вам начать можно вот с такого варианта.

prax04.com:

00000000: CD16    int  016
00000002: B409    mov  ah,009 
00000004: 3C20    cmp  al,020
00000006: 7411    je   000000019
00000008: 3C61    cmp  al,061
0000000A: 7415    je   000000021
0000000C: 3C0D    cmp  al,00D 
0000000E: 7419    je   000000029
00000010: 3C1B    cmp  al,01B
00000012: 741D    je   000000031
00000014: 33C0    xor  ax,ax
00000016: E9E7FF  jmp  000000000
00000019: BA3801  mov  dx,00138
0000001C: CD21    int  021
0000001E: E9DFFF  jmp  000000000
00000021: BA6701  mov  dx,00167
00000024: CD21    int  021
00000026: E9D7FF  jmp  000000000
00000029: BAB701  mov  dx,001B7
0000002C: CD21    int  021
0000002E: E9CFFF  jmp  000000000
00000031: BACF01  mov  dx,001CF
00000034: CD21    int  021
00000036: CD20    int  020

Код программы, как всегда, связан с данными. Их вполне можно набирать в текстовом редакторе Фара в кодировке DOS.

Закорючки — это код.

Заметьте, каждая строка текста отбита Enter’ами.

_?_?_?_?_?_?_?_?
_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?Если б я была игра - пулемёт бы застрелял...
$А-а-а в Африке горы вот такой вышины, а-а-а в Африке реки вот такой ширины...
$Enter кнопка о-го-го!
$Очень жалко мне прощаться :(.$

Вы можете набрать текст с каким-нибудь сдвигом, тогда программа будет отображать неправильные строки.
Точно убедиться в правильности набора можно, сверив ваш файл с моим.

Каждый раз, как вы печатаете команду, совершаете ошибку или находите её у меня, вы совершенствуетесь в геометрической прогрессии.
Поэтому и prax04 тоже лучше набрать самому. Практика — это главное в данный момент обучения.

Я уверен, что вам захочется переделать эту программу, например, в текстовый редактор.
Что ж, это очень похвальное желание. Но для того, чтоб этот маленький пример стал текстовым редактором, нужно о нём кое-что знать.

Во-первых, прерывание 16 при AH=0 ждёт нажатия клавиши и возвращает в AX сообщение:

AH — скан-код клавиши (в таком коде различаются все кнопки на клаве),

AL — ASCII-код символа.

Во-вторых, в этой программе есть одна новая для вас команда XOR. Команда логического действия.
Для компьютера такие команды всё равно что сложение и вычитание — арифметические действия. Есть даже задачи,
которые можно решать в столбик с использованием логических команд. Основных логических действий всего 5.
Сейчас я расскажу о XOR, и вы сами всё поймёте.

Команда XOR
Происхождениеот англ. слов eXclusive OR — исключающее ИЛИ
Форматxor приёмник , источник
Действиеxor — «побитовое исключающее или» (притивоположность включающему).

Если один из сравниваемых битов равен 0, а другой равен 1, то результат равен 1.

Если сравниваемые биты одинаковы (оба — 0 или оба — 1), то результат = 0.

приёмник = приёмник xor источник.
ПримечаниеКоманда XOR обратима. Это значит, что поXORив её результат с одним из операндов, мы получим второй операнд.

Знаю, в первый раз сбивает с толку, но если вы решите пару примеров, всё станет понятно.
Вот все 4 возможных варианта битов в операндах.


    0101
XOR 
    1001
    ----
    1100

Поняли? Если нет, потренируйтесь на листочке.

Вывод: если операнды одинаковые, то на выходе в любом случае будет 0. Самый быстрый для процессора способ обнулить регистр EAX — написать:

XOR  EAX,EAX

Так же можно обнулять и другие регистры общего назначения.
Через пару примеров мы будем использовать команду XOR не только для обнуления.

Ещё я не говорил отдельно о команде jmp. Но думаю, вы и сами поняли, что это команда перехода без всяких условий.

Теперь вы можете экспериментировать. Напишите сами подробный разбор действий в этом примере.

Только из Hiew’a, как мы это делаем сейчас, будет очень неудобно вносить поправки в код.
Вам придется переправлять все прыжки, кроме того, они в этом примере короткие.
То есть в коде операций пишется, сколько нужно прибавить к адресу следующей команды или сколько нужно от него отнять.
Несмотря на то, что дизассемблер показывает

команду и адрес в памяти, куда будет совершен прыжок. Допустим, вы написали:

jmp  00000000

Теперь вы вставили 3 байта выше и не тронули саму команду, строка станет такой:

jmp  00000003

И ещё адреса текстовых данных в памяти «уедут», придётся править команды с указателями.
Так писать серьёзную программу можно годами.
В следующей главе мы познакомимся с MASM’ом, и тогда дело пойдёт значительно быстрее.

Возможно, к этому времени у моих читателей накопились скептические мысли.
Если это всё, на что способен Ассемблер, то зачем тогда мучиться его изучать?
Признаюсь, когда я выполнял подобные примеры, тоже сомневался, что на Ассемблере можно программировать полезные программы.

Однако сейчас знаю, что, используя знания Асма и машинного языка,
можно «попросить» свой компьютер работать в нужном вам направлении, а ответ его почти наверняка будет положительным.
И в Windows Ассемблер даёт возможность легко и красиво писать инструменты,
получающие доступ к глубинам форточек, драйверы и системные программы. Но это далеко не единственное применение Ассемблера.
Вы думаете, что самые навороченные игрушки пишут на Delphi или C++? Нет, это вовсе не обязательно.
Качественный 3D-движок для игры, написанный на Ассемблере, куда как круче, чем любой подобный движок,
построенный с использованием только высокоуровневых языков. И поверьте, такие вещи писать на Ассемблере реально!

Вот замечательный пример (!!!97Kb!!!).

Скачайте его и посмотрите (если ваш комп потянет). Сайт программы:
www.theprodukkt.com

Причём совершенно не обязательно писать всю программу на Ассемблере.
Вы можете писать базу данных на Delphi или С++ и, не выходя из проекта, вставлять куски кода на Ассемблере в специальных скобках.
Есть много задач, которые требуют многократного повторения. Если вы их красиво напишете на Асме,
программа может обрести возможности, которых не будет у конкурентов. Я уже не говорю о программах перебора различных значений.
Такие программы не имеет смысл писать на других языках, когда есть Ассемблер.

На завтра у нас запланирован переход к программированию под Windows.


— Любое число, представленное в двоичной системе, можно воспринимать как битовую информацию,
и очень часто в этом есть математический смысл. Например, нулевой бит определяет нечётность числа.
Также старший бит числа может трактоваться как знаковый бит. Если он включен, число можно воспринимать как отрицательное.
Обо всём этом мы поговорим в витке1 и следующих Чтивах.

Bitfry


Как ввести и вывести число в assembler(AT&T, GAS синтаксис)? — Хабр Q&A

Приветствую всех.
Учусь разработке на Assembler
При обучении возникла задача — написание калькулятора.
Пишу под Linux(Ubuntu), следовательно приходится осваивать синтаксис GAS(AT&T)
Возникла проблема с выводом, а именно при компиляции этого кода:

.data
number:
  .long 0x00000595
str:
  .space 4, 0
i:
  .byte 0

str_len =.-str

.text
  .global _start
_start:

  0:
    addb $str, i
    movb $0x30, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  1:
    addb $str, i
    movb $0x31, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  2:
    addb $str, i
    movb $0x32, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  3:
    addb $str, i
    movb $0x33, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  4:
    addb $str, i
    movb $0x34, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  5:
    addb $str, i
    movb $0x35, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  6:
    addb $str, i
    movb $0x36, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  7:
    addb $str, i
    movb $0x37, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  8:
    addb $str, i
    movb $0x38, (%eax)
    incb %al
    movb %al, (i)
    jmp logic

  9:
    addb $str, i
    movb $0x39, (%eax)
    incb %al
    movb %al, (i)
    jmp logic


  logic:
    movl $number, %eax
    cdq
    movl $10, %ebx
    idivl %ebx

    cmpl $0, %edx
    je 0
    cmpl $1, %edx
    je 1
    cmpl $2, %edx
    je 2
    cmpl $3, %edx
    je 3
    cmpl $4, %edx
    je 4
    cmpl $5, %edx
    je 5
    cmpl $6, %edx
    je 6
    cmpl $7, %edx
    je 7
    cmpl $8, %edx
    je 8
    cmpl $9, %edx
    je 9

    movl $4, %eax
    movl $1, %ebx
    movl $str, %ecx
    movl $str_len, %edx
    int $0x80

    movl $1, %eax
    xor %ebx, %ebx
    int $0x80

При компиляции появляется такая ошибка:

$ as --64 test_print.s -o test_print.o
$ ld -melf_x86_64 -s test_print.o -o test_print
test_print.o: In function `_start':
(.text+0x7): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x21): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x3b): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x55): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x6f): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x86): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0x9d): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0xb4): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0xcb): relocation truncated to fit: R_X86_64_8 against `.data'
(.text+0xe2): relocation truncated to fit: R_X86_64_8 against `.data'

Помогите разобраться, сломал себе весь мозг.
P.S. Буду очень благодарен, если подскажите на основе скрипта вывода способ получения числа из символьного массива при вводе

Размер окна приложения на ассемблере

format          PE GUI 4.0                                                      ; Формат PE

entry           start                                                           ; Точка входа

include         ‘%include%\win32a.inc’                                          ; Включение описателей

 

_style          equ             WS_VISIBLE+WS_DLGFRAME+WS_SYSMENU+WS_THICKFRAME ; Стиль окна.

 

;=== сегмент кода ============================================================

section         ‘.text’ code readable executable

  start:

                invoke          GetModuleHandle,0                               ; Получим дескриптор приложения.

                mov             [wc.hInstance],eax                              ; Сохраним дескриптор в структуру wc

                mov             [wc.style], CS_HREDRAW or CS_VREDRAW            ; Стили перерисовки окна полностью

                invoke          LoadIcon,0,IDI_ASTERISK                         ; Загружаем иконку IDI_ASTERISK из ресурсов исполняемого файла

                mov             [wc.hIcon],eax                                  ; Сохраним в структуре окна

                invoke          LoadCursor,0,IDC_ARROW                          ; Загружаем курсор IDC_ARROW из ресурсов исполняемого файла

                mov             [wc.hCursor],eax                                ; Сохраним дескриптор курсора в структуре окна

                mov             [wc.lpfnWndProc],WindowProc                     ; Адрес нашей процедуры обработки окна

                mov             [wc.lpszClassName],_class                       ; Имя класса окна

                mov             [wc.hbrBackground],COLOR_WINDOW+1               ; Цвет кисти

                invoke          RegisterClass,wc                                ; Регистрируем наш класс окна

                test            eax,eax                                         ; Проверим выходной eax

                jz              error                                           ; Если 0 — то ошибка. Выводим и выходим.

 

                invoke          CreateWindowEx,0,_class,_title,_style,128,128,512,512,NULL,NULL,[wc.hInstance],NULL  ; Создаем экземпляр окна на основе зарегистрированного класса

                test            eax,eax                                         ; Проверим выходной eax.

                jz              error                                           ; Если 0 — то ошибка. Выводим и выходим.

                mov             [wHMain],eax                                    ; сохраним дескриптор окна

 

;— цикл обработки сообщений ————————————————

 

  msg_loop:

                invoke          GetMessage,msg,NULL,0,0                         ; Получаем сообщение из очереди сообщений приложения, помещает его в структуру msg

                or              eax,eax                                         ; Проверяем eax

                jz              end_loop                                        ; 0 = пришло сообщение WM_QUIT (выходим из цикла ожидания сообщений), не 0 = продолжаем обрабатывать очередь

  msg_loop_2:

                invoke          TranslateMessage,msg                            ; Дополнительная функция обработки сообщения. Конвертирует некоторые сообщения и отправляет их обратно в очередь.

                invoke          DispatchMessage,msg                             ; Пересылает сообщения соответствующим процедурам обработки сообщений (WindowProc …).

                jmp             short msg_loop                                  ; Цикл

  error:

                invoke          MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK  ; Вывод сообщения об ошибке

  end_loop:

                invoke          ExitProcess,[msg.wParam]                        ; Выход из программы. Код выхода сохраняется в msg.wParam. Его надо передать системе

 

;— процедура обработки окна (функция окна, оконная процедура) —————

 

proc            WindowProc      hWnd,wMsg,wParam,lParam

                push            ebx esi edi                                     ; сохраним все регистры

                cmp             [wMsg],WM_CREATE                                ; Проверим на WM_CREATE

                je              .wmcreate                                       ; на обработчик wmcreate

                cmp             [wMsg],WM_DESTROY                               ; Проверим на WM_DESTROY

                je              .wmdestroy                                      ; на обработчик wmdestroy

                cmp             [wMsg],WM_PAINT                                 ; Проверим на WM_PAINT

                je              .wmpaint                                        ; на обработчик wmpaint

 

  .defwndproc:

                invoke          DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]   ; Дефолтная функция. Обрабатывает сообщения, которые наша программа не использует.

                jmp             .finish

  .wmcreate:

                xor             eax,eax

                jmp             .finish

  .wmpaint:

                invoke          BeginPaint,[hWnd],pnt

                mov             [pHMain],eax ; Сохранить контекст (дескриптор) устройства

 

                invoke          GetClientRect,[hWnd],rect_client                ; Запрашиваем координаты клиентской области окна

                mov             ebx,[rect_client.bottom]

                sub             ebx,[rect_client.top]                           ; EBX = высота клиентской области

                mov             ecx,[rect_client.right]

                sub             ecx,[rect_client.left]                          ; ECX = ширина клиентской области

                invoke          wsprintf,szValue1,szFmt,ecx,ebx                 ; Записываем в буфер десятичные эквиваленты ширины и высоты

                invoke          lstrlen,_message1                               ; Вычислим длину выводимой строки

                invoke          TextOut,[pHMain],2,10,_message1,eax             ; Выведем текст в клиентскую область окна

 

                invoke          GetWindowRect,[hWnd],rect_nonclient             ; Запрашиваем координаты неклиентской области окна

                mov             ebx,[rect_nonclient.bottom]

                sub             ebx,[rect_nonclient.top]                        ; EBX = высота неклиентской области

                mov             ecx,[rect_nonclient.right]

                sub             ecx,[rect_nonclient.left]                       ; ECX = ширина неклиентской области

                invoke          wsprintf,szValue2,szFmt,ecx,ebx                 ; Записываем в буфер десятичные эквиваленты ширины и высоты

                invoke          lstrlen,_message2                               ; Вычислим длину выводимой строки

                invoke          TextOut,[pHMain],2,30,_message2,eax             ; Выведем текст в клиентскую область окна

 

                invoke          EndPaint,[hWnd],pnt                             ; Освободить контекст устройства

                xor             eax,eax

                jmp             .finish

  .wmdestroy:                                                                   ; Обработчик сообщения WM_DESTROY.

                invoke          PostQuitMessage,0                               ; Посылает сообщение WM_QUIT в очередь сообщений, что вынуждает GetMessage вернуть 0. Посылается для выхода из программы. Посылается только основным окном.

                xor             eax,eax                                         ; Если наша процедура окна обрабатывает какое-либо сообщение, то она должна вернуть 0 в eax.

  .finish:

                pop             edi esi ebx                                     ; восстановим все регистры

                ret

endp

 

;=== сегмент данных ==========================================================

 

section         ‘.data’ data readable writeable

 

_class          db              ‘FASMWIN32’,0                                   ; Название собственного класса. Мы будем регистрировать свой.

_title          db              ‘Win32 get window size’,0                       ; Текст в заголовке окна.

_error          db              ‘Startup failed.’,0                             ; Текст ошибки

 

szFmt           db              ‘%04u,%04u’,0                                   ; шаблон формата для wsprintf

_message1       db              ‘ClientSizeXY: ‘

szValue1        db              ‘XXXXXXXXX’,0

 

_message2       db              ‘NonClientSizeXY: ‘

szValue2        db              ‘XXXXXXXXX’,0

 

wHMain          dd              ?                                               ; дескриптор окна

pHMain          dd              ?                                               ; дескриптор контекста устройства

wc              WNDCLASS                                                        ; Структура окна. Для функции RegisterClass

msg             MSG                                                             ; Структура системного сообщения. Структура содержится в INCLUDE\EQUATES\USER32.INC

pnt             PAINTSTRUCT                                                     ; Структура отрисовки клиентской области окна.

rect_client     RECT                                                            ; Структура RECT клиентской части окна

rect_nonclient  RECT                                                            ; Структура RECT неклиентской части окна

 

;=== таблица импорта =========================================================

section         ‘.idata’ import data readable writeable

 

library         kernel32,’KERNEL32.DLL’,user32,’USER32.DLL’,gdi32,’GDI32.DLL’

include         ‘api\kernel32.inc’

include         ‘api\user32.inc’

include         ‘api\gdi32.inc’

Перейти, если условие выполнено

JE — Перейти, если условие выполнено


Код Мнемоника Описание
77 куб JA rel8 Короткий переход, если выше (CF = 0 и ZF = 0)
73 куб JAE rel8 Короткий прыжок, если он больше или равен (CF = 0)
72 куб JB rel8 Короткий переход, если ниже (CF = 1)
76 куб JBE rel8 Короткий переход, если ниже или равно (CF = 1 или ZF = 1)
72 куб JC rel8 Короткий прыжок при переноске (CF = 1)
E3 CB JCXZ rel8 Короткий переход, если регистр CX равен 0
E3 CB JECXZ rel8 Короткий переход, если регистр ECX равен 0
74 куб JE rel8 Короткий переход при равенстве (ZF = 1)
7F CB JG rel8 Короткий переход, если больше (ZF = 0 и SF = OF)
7D CB JGE rel8 Короткий переход, если больше или равно (SF = OF)
7C CB JL rel8 Короткий переход, если меньше (SF <> OF)
7E CB JLE rel8 Короткий переход, если меньше или равно (ZF = 1 или SF <> OF)
76 куб JNA rel8 Короткий переход, если не выше (CF = 1 или ZF = 1)
72 куб JNAE rel8 Короткий прыжок, если он не выше или равен (CF = 1)
73 куб JNB rel8 Короткий переход, если не ниже (CF = 0)
77 куб JNBE rel8 Короткий переход, если он не ниже или равен (CF = 0 и ZF = 0)
73 куб JNC rel8 Короткий переход, если не переносится (CF = 0)
75 куб JNE rel8 Короткий переход, если не равен (ZF = 0)
7E CB JNG rel8 Короткий переход, если не больше (ZF = 1 или SF <> OF)
7C CB JNGE rel8 Короткий переход, если не больше или равно (SF <> OF)
7D CB JNL rel8 Короткий прыжок, если не меньше (SF = OF)
7F CB JNLE rel8 Короткий переход, если не меньше или равно (ZF = 0 и SF = OF)
71 куб JNO rel8 Короткое замыкание, если не переполнение (OF = 0)
7Б кб JNP rel8 Короткий переход, если нет четности (PF = 0)
79 куб JNS rel8 Короткий переход, если нет знака (SF = 0)
75 куб JNZ rel8 Короткий переход, если не ноль (ZF = 0)
70 куб JO rel8 Короткое замыкание при переполнении (OF = 1)
7A CB JP rel8 Короткий переход при четности (PF = 1)
7A CB JPE rel8 Короткий переход при четности (PF = 1)
7Б кб JPO rel8 Короткий переход, если четность нечетная (PF = 0)
78 куб JS rel8 Короткий переход, если знак (SF = 1)
74 куб JZ rel8 Короткий переход, если ноль (ZF = 1)
0F 87 ч / кд JA rel16 / 32 Перейти рядом, если выше (CF = 0 и ZF = 0)
0F 83 кд / кд JAE rel16 / 32 Перейти рядом, если больше или равно (CF = 0)
0F 82 cw / cd JB rel16 / 32 Перейти рядом, если ниже (CF = 1)
0F 86 ч / кд JBE rel16 / 32 Перейти рядом, если ниже или равно (CF = 1 или ZF = 1)
0F 82 cw / cd JC отн16 / 32 Прыгать, если переносится (CF = 1)
0F 84 ч / кд JE rel16 / 32 Перейти почти, если равно (ZF = 1)
0F 84 ч / кд JZ отн16 / 32 Перейти рядом, если 0 (ZF = 1)
0F 8F cw / cd JG отн16 / 32 Перейти рядом, если больше (ZF = 0 и SF = OF)
0F 8D cw / cd JGE отн16 / 32 Перейти рядом, если больше или равно (SF = OF)
0F 8C по часовой / кд JL отн16 / 32 Перейти рядом, если меньше (SF <> OF)
0F 8E cw / cd JLE rel16 / 32 Перейти рядом, если меньше или равно (ZF = 1 или SF <> OF)
0F 86 ч / кд JNA rel16 / 32 Перейти рядом, если не выше (CF = 1 или ZF = 1)
0F 82 cw / cd JNAE rel16 / 32 Перейти рядом, если не выше или равно (CF = 1)
0F 83 кд / кд JNB отн.16 / 32 Перейти рядом, если не ниже (CF = 0)
0F 87 ч / кд JNBE rel16 / 32 Перейти рядом, если не ниже или равно (CF = 0 и ZF = 0)
0F 83 кд / кд JNC rel16 / 32 Прыгнуть, если не унести (CF = 0)
0F 85 cw / cd JNE rel16 / 32 Перейти рядом, если не равно (ZF = 0)
0F 8E cw / cd JNG отн16 / 32 Перейти рядом, если не больше (ZF = 1 или SF <> OF)
0F 8C по часовой / кд JNGE отн16 / 32 Перейти рядом, если не больше или равно (SF <> OF)
0F 8D cw / cd JNL отн16 / 32 Перейти рядом, если не меньше (SF = OF)
0F 8F cw / cd JNLE rel16 / 32 Перейти рядом, если не меньше или равно (ZF = 0 и SF = OF)
0F 81 cw / cd JNO rel16 / 32 Перейти рядом, если не переполнение (OF = 0)
0F 8B по часовой / кд JNP отн.16 / 32 Перейти близко, если нет четности (PF = 0)
0F 89 cw / cd JNS отн.16 / 32 Перейти рядом, если нет знака (SF = 0)
0F 85 cw / cd JNZ отн.16 / 32 Перейти к нулю, если не к нулю (ZF = 0)
0F 80 ч / кд JO rel16 / 32 Перейти при переполнении (OF = 1)
0F 8A cw / cd JP отн.16 / 32 Перейти к ближайшему при проверке четности (PF = 1)
0F 8A cw / cd JPE отн16 / 32 Перейти, если четность четна (PF = 1)
0F 8B по часовой / кд JPO rel16 / 32 Перейти рядом, если четность нечетная (PF = 0)
0F 88 ч / кд JS отн16 / 32 Перейти рядом со знаком, если (SF = 1)
0F 84 ч / кд JZ отн16 / 32 Перейти рядом, если 0 (ZF = 1)

Описание
Проверяет состояние одного или нескольких флагов состояния в регистре EFLAGS (CF, OF, PF, SF и ZF) и, если флаги находятся в указанном состоянии (условии), выполняет переход к целевой инструкции, указанной в операнд назначения.Код условия (cc) связан с каждой инструкцией, чтобы указать условие, которое проверяется. Если условие не выполняется, переход не выполняется, и выполнение продолжается с инструкции, следующей за инструкцией Jcc.

Целевая инструкция указывается с относительным смещением (смещение со знаком относительно текущего значения указателя инструкции в регистре EIP). Относительное смещение (rel8, rel16 или rel32) обычно указывается как метка в ассемблерном коде, но на уровне машинного кода оно кодируется как подписанное, 8-битное или 32-битное непосредственное значение, которое добавляется к указатель инструкции.Кодирование инструкций наиболее эффективно для смещений от 128 до +127. Если атрибут размера операнда равен 16, два старших байта регистра EIP очищаются до 0, в результате чего максимальный размер указателя инструкции составляет 16 бит.

Условия для каждой мнемоники Jcc приведены в столбце «Описание» таблицы на предыдущей странице. Термины «меньше» и «больше» используются для сравнения целых чисел со знаком, а термины «выше» и «ниже» используются для целых чисел без знака.

Поскольку конкретное состояние флагов состояния иногда можно интерпретировать двояко, для некоторых кодов операций определены две мнемоники.Например, инструкция JA (переход, если выше) и инструкция JNBE (переход, если не ниже или равно) являются альтернативными мнемониками для кода операции 77H.

Инструкция Jcc не поддерживает далекие переходы (переходы к другим сегментам кода). Когда цель для условного перехода находится в другом сегменте, используйте условие, противоположное проверяемому условию для инструкции Jcc, а затем получите доступ к цели с безусловным дальним переходом (инструкция JMP) к другому сегменту. Например, следующий условный дальний прыжок недопустим:
JZ FARLABEL;

Чтобы совершить этот дальний прыжок, используйте следующие две инструкции:

JNZ BEYOND;
JMP FARLABEL;
ВНЕ:


Инструкции JECXZ и JCXZ отличаются от других инструкций Jcc, поскольку они не проверяют флаги состояния.Вместо этого они проверяют содержимое регистров ECX и CX, соответственно, на 0. В соответствии с атрибутом размера адреса выбирается регистр CX или ECX. Эти инструкции полезны в начале условного цикла, который завершается инструкцией условного цикла (например, LOOPNE). Они предотвращают вход в цикл, когда регистр ECX или CX равен 0, что приведет к тому, что цикл будет выполняться 2 32 или 64 К раз, соответственно, вместо нуля раз.

Все условные переходы преобразуются в выборки кода одной или двух строк кэша, независимо от адреса перехода или кэшируемости.

Операнды байтов Частоты
около 8 2 1 PV
около 16 3 1 PV

циклы применяются к прыжку и без прыжка

Флаги

ID незатронутый DF незатронутый
VIP незатронутый IF незатронутый
VIF незатронутый TF незатронутый
ВС незатронутый SF незатронутый
ВМ незатронутый ZF незатронутый
РФ незатронутый AF незатронутый
NT незатронутый ПФ незатронутый
IOPL незатронутый CF незатронутый
ИЗ незатронутый

Лекция по языку ассемблера и компьютерной архитектуре (CS 301)

Лекция по языку ассемблера и компьютерной архитектуре (CS
301)

CS 301:
Лекция по программированию на языке ассемблера, д-р.Лоулор

Прыжок
инструкция, такая как «jmp», просто переключает ЦП на выполнение
другой фрагмент кода. Это ассемблерный эквивалент
«goto», но, в отличие от goto, прыжки не считаются постыдными в
сборка. (Дейкстра написал в 1968 году статью под названием «Goto
Считается вредным «. С тех пор goto обычно
считались вредными, за исключением
Linux.)

Вы говорите, куда перейти, используя «ярлык перехода», который может быть любым
имя строки с двоеточием после него.(Такой же точный синтаксис
используется в C / C ++)

В обоих случаях мы возвращаем 3, потому что мы перепрыгиваем через 999
назначение. Прыжки несколько полезны для перепрыгивания через плохие
код, но он действительно становится полезным, когда вы добавляете условный
прыгает. Они используются после инструкции «cmp» для сравнения
два значения.

Инструкция Полезно для …
jmp Всегда прыгать
je Перейти, если cmp равно
jne Перейти, если cmp не равно
jg Подписано>
(больше)
jge Подписано> =
и Подписано < (менее)
ил Подпись <=
и Без знака> (вверху)
иэ Без знака> =
jb Без знака <(внизу)
jbe Без знака <=
jecxz Перейти, если ecx 0
(Серьезно !?)
JC Перейти при переносе: используется для неподписанных
переполнение,
или multiprecision добавить
ио Перейти если был подписан
перелив

Есть также
«n» версий НЕ для каждого прыжка; например «jno» прыгает, если есть
НЕ переполнение.

Условный
Прыжки: разветвление в сборке

В сборе,
все ветвления выполняются с использованием двух типов инструкций:

  • Команда сравнения, такая как «cmp», сравнивает два значения.
    Внутри он делает это путем их вычитания.
  • Инструкция условного перехода, например «je» (переход, если равен),
    делает переход где-нибудь, если два значения удовлетворяют
    правильное состояние. Например, если значения равны,
    их вычитание дает ноль, поэтому «je» то же самое, что «jz».

Вот как
используйте сравнение и прыжок, если равно («je»):

 mov eax, 3 
cmp eax, 3; как eax по сравнению с 3?
je lemme_outta_here; если равно, то перейти на
mov eax, 999; <- не выполняется * если * перепрыгиваем через него
lemme_outta_here:
ret

(Попробуйте
это сейчас в NetRun!)

Вот сравнить
и jump-if-less-than («jl»):

 mov eax, 1 
cmp eax, 3; как eax по сравнению с 3?
jl lemme_outta_here; если меньше, то перейти на
mov eax, 999; <- не выполняется * если * перепрыгиваем через него
lemme_outta_here:
ret

(Попробуйте
это сейчас в NetRun!)

C ++
эквивалентно сравнить-и-прыгать-если-что бы то ни было «если (что-то) перейти
где-то;».

Петли

Чтобы зациклиться, вы
просто вернитесь к началу кода. Где-то ты делаешь
нужно условное выражение, или вы создали бесконечный цикл!

В этом
Например, мы отсчитываем edi, пока он не достигнет нуля.

; edi - наш первый аргумент функции 
mov eax, 0; сумма добавлена ​​здесь Начните: ; цикл начинается здесь добавить eax, 10; добавлять каждый раз по петле sub edi, 1; приращение цикла cmp edi, 0; петлевой тест jg start; продолжить цикл, если edi> 0 ret

(Попробуйте
это сейчас в NetRun!)

Это
построчно эквивалентно этому коду C ++:

 int foo (int bar) {
int sum = 0;

Начните:
сумма + = 10;
бар--;
если (bar> 0) перейти к началу;

сумма возврата;
} 
 (Попробуйте сейчас в NetRun!) 

Оф
конечно, это очень уродливый код на C ++! Это более идиоматично
напишите здесь цикл «for»:

 int foo (int bar) {
int sum = 0;
for (int count = bar; count> 0; count--)
сумма + = 10;
сумма возврата;
} 

(Попробуйте
это сейчас в NetRun!)

Подробнее
Сложный поток управления: C-

Можно
на самом деле написать очень своеобразный вариант C ++, где «если»
операторы содержат только операторы goto.Мое шутливое название для
этот C ++ в стиле сборки — «C—»: вы используете только «+ =» и «* =»
арифметика и «если (простой тест) перейти куда-нибудь»; управление потоком.

Например, это совершенно допустимый C ++ в стиле «C—«:

 int main () {
int я = 0;
if (i> = 10) goto byebye;
std :: cout << "Не слишком большой! \ N";
до свидания: возврат 0;
}

Этот способ
написание C ++ очень похоже на сборку — фактически, есть
взаимно однозначное соответствие между строками кода C, написанными таким образом
и инструкции на машинном языке.Более сложный C ++,
подобно конструкции «for», расширяется до многих линий сборки.

 int i, n = 10; 
for (i = 0; i std :: cout << "В цикле: i ==" << i << "\ n";
}

Вот один
расширенная версия этого C / C ++ цикла «for»:

 int i = 0, n = 10; 
start: std :: cout << "В цикле: i ==" << i << "\ n";
i ++;
, если (i

(исполняемый
Ссылка NetRun)

Вы должны убедить себя, что это действительно эквивалентно
цикл «for» во всех случаях.Осторожно - если n - параметр,
это не! (Что, если n> = i?)

Все конструкции управления потоком C могут быть написаны с использованием только «if» и
"goto", которые обычно сопоставляют "один к одному" для сравнения и перехода.
последовательность в сборке.

Нормальный C Расширенный C
если (A) {
...
}
if (! A) goto END;
{
...
}
КОНЕЦ:
если (! A) {
...
}
if (A) goto END;
{
...
}
КОНЕЦ:
если (A && B) {
...
}
if (! A) goto END;
if (! B) goto END;
{
...
}
КОНЕЦ:
если (A || B) {
...
}
if (A) goto STUFF;
if (B) goto STUFF;
goto END;
ВЕЩЕСТВО:
{
...
}
КОНЕЦ:
, а (A) {
...
}
goto TEST;
НАЧАЛО:
{
...
}
ТЕСТ: если (A) перейти к НАЧАЛУ;
делать {
...
}, а (A)
START:
{
...
}
if (A) перейти к START;
для (i = 0; i {
...
}
я = 0;
/ * Версия A * /
goto TEST;
НАЧАЛО:
{
...
}
i ++;
ТЕСТ: if (i
для (i = 0; i {
...
}
я = 0;
/ * Версия B * /
START: if (i> = n) goto END;
{
...
}
i ++;
goto START;
КОНЕЦ:

Обратите внимание, что два последних перевода понятия «для» (помечены
Версия A и версия B) вычисляют одно и то же. Который
один быстрее? Если цикл повторяется много раз, я требую версию
(A) обычно быстрее, так как есть только один (условный) goto
каждый раз по циклу вместо двух goto в версии
(Б) - одно условное и одно безусловное.Но версия (B)
вероятно, быстрее, если n часто равно 0, потому что в этом случае
быстро переходит в END (за один условный переход).

ВНИМАНИЕ:
Философское содержание

Ядерная
оружие настолько мощное, что обычно считается плохой идеей
используйте их для решения большинства задач.

Аналогично,
Операторы goto настолько мощны, что обычно считаются плохими
идея использовать их для большинства задач. Проблема в том, когда ты
увидеть goto, вы не можете сказать, используется ли он для имитации for,
while, вызов функции или какой-то более странный их гибрид.Таким образом
goto может быть достаточно легко написать, но его труднее читать,
и поскольку код в больших проектах пишется только один раз, но получает
прочитал много раз много разных людей за эти годы, это больше
важно сделать ваш код легко читаемым.

Я больше всего замечаю
программисты-самоучки (включая меня), как правило, предпочитают goto или
его немного более классный кузен, цикл while, потому что они более
Общее. Но после того, как вы испортили цикл "while" достаточно раз,
например, отказавшись от приращения цикла и непреднамеренно сделав
бесконечный цикл, вы в конечном итоге по умолчанию используете циклы "for",
потому что синтаксис, предписанный компилятором ("for (int i = 0; i

Петли и ответвления в сборке

CS 301 Лекция

CS 301 Лекция, доктор Лоулор

Инструкция перехода, такая как "jmp", просто переключает CPU на выполнение
другой фрагмент кода. Это ассемблерный эквивалент goto,
но, в отличие от goto, при сборке прыжки не считаются зазорными. (Дейкстра написал в 1968 году статью под названием «Goto, которая считается вредной». С тех пор goto в целом считалась вредной, за исключением Linux.)

Вы указываете, куда перейти, используя «метку перехода», которая представляет собой любую строку
с двоеточием после него.(Такой же точный синтаксис используется в C / C ++)

В обоих случаях мы возвращаем 3, потому что мы перепрыгиваем через 999
назначение. Прыжки несколько полезны для перепрыгивания через плохие
код, но он действительно становится полезным, когда вы добавляете условные переходы.
Они используются после инструкции «cmp» для сравнения двух значений.

Инструкция Полезно для ...
jmp Всегда прыгать
и Без знака>
иэ Без знака> =
сп Без знака <
jbe Без знака <=
jc Беззнаковое переполнение,
или сложение multiprecision
jecxz Сравните ecx с 0
(Серьезно !?)
je Равенство
jg Подписано>
jge Подпись> =
jl Подписано <
ил Подпись <=
иен Неравенство
jo Переполнение со знаком

Также существует "n" версий НЕ для каждого прыжка; например "jno" перескакивает, если НЕ происходит переполнения.

Условные переходы: разветвление в сборке

В сборке все ветвления выполняются с использованием двух типов инструкций:

  • Команда сравнения, такая как «cmp», сравнивает два значения.
  • Команда условного перехода, такая как «je» (переход, если равно), выполняет переход где-нибудь, если два значения удовлетворяют правильному условию.

Вот как использовать сравнение и переход при равном ("je"):

 mov eax, 3 
cmp eax, 3; как eax по сравнению с 3?
je lemme_outta_here; если равно, то перейти на
mov eax, 999; <- не выполняется * если * перепрыгиваем через него
lemme_outta_here:
ret

(Попробуйте сейчас в NetRun!)

Вот сравнение и прыжок, если меньше ("jl"):

 mov eax, 1 
cmp eax, 3; как eax по сравнению с 3?
jl lemme_outta_here; если меньше, то перейти на
mov eax, 999; <- не выполняется * если * перепрыгиваем через него
lemme_outta_here:
ret

(Попробуйте сейчас в NetRun!)

Эквивалент C ++ для compare-and-jump-if-something - «if (something) gotowhere;».

Также проверьте машинный код, сгенерированный для условного
jump - назначение перехода кодируется как количество байтов машины
код, который нужно пропустить. Например, "jl" выше кодируется в
машинный код вроде этого:

 0: b8 01 00 00 00 mov eax, 0x1 
5:83 f8 03 cmp eax, 0x3
8: 7c 05 jl f
a: b8 e7 03 00 00 mov eax, 0x3e7
f: c3 ret

Расстояние для прыжка, показанное красным выше, составляет пять байтов, потому что
код, который мы пропускаем, имеет длину пять байтов.Обратите внимание, что прыжок
метка вообще не отображается в машинном коде - она ​​просто используется
ассемблер, чтобы выяснить, как далеко прыгнуть.

Более сложный поток управления: C--

На самом деле вы можете написать очень своеобразный вариант C ++, где
Операторы if содержат только операторы goto. Мое шутливое название для
этот C ++ в стиле сборки, в котором используются только арифметические операции "+ =" и "* =",
и "если (простой тест) перейти куда-нибудь;" управление потоком - "C--"!

Например, это совершенно допустимый C ++ в стиле "C--":

 int main () {
int i = 0;
if (i> = 10) goto byebye;
std :: cout << "Не слишком большой! \ N";
до свидания: возврат 0;
}

Этот способ написания C ++ очень похож на сборку - фактически, есть
взаимно однозначное соответствие между строками кода C, написанного здесь
путь и инструкции на машинном языке.Более сложный C ++, например
конструкция «for» расширяется до многих линий сборки.

 int i, n = 10; 
for (i = 0; i std :: cout << "В цикле: i ==" << i << "\ n";
}

Вот одна расширенная версия этого цикла C / C ++ "for":

 int i = 0, n = 10; 
start: std :: cout << "В цикле: i ==" << i << "\ n";
i ++;
, если (i

(исполняемая ссылка NetRun)

Вы должны убедить себя, что это действительно эквивалентно
цикл "for" во всех случаях.Осторожно - если n - параметр, это не так! (Что, если n> = i?)

Все конструкции управления потоком C могут быть написаны с использованием только «if» и
"goto", который обычно однозначно отображает последовательность сравнения и перехода в сборке.

Нормальный C Расширенный C
если (A) {
...
}
if (! A) goto END;
{
...
}
КОНЕЦ:
если (! A) {
...
}
if (A) goto END;
{
...
}
КОНЕЦ:
если (A && B) {
...
}
if (! A) goto END;
if (! B) goto END;
{
...
}
КОНЕЦ:
если (A || B) {
...
}
if (A) goto STUFF;
if (B) goto STUFF;
goto END;
STUFF:
{
...
}
КОНЕЦ:
, а (A) {
...
}
goto TEST;
НАЧАЛО:
{
...
}
ТЕСТ: если (A) перейти к НАЧАЛУ;
делать {
...
}, а (A)
START:
{
...
}
if (A) перейти к START;
для (i = 0; i {
...
}
я = 0; / * Версия A * /
goto TEST;
НАЧАЛО:
{
...
}
i ++;
ТЕСТ: if (i
для (i = 0; i {
...
}
я = 0; / * Версия B * /
START: if (i> = n) goto END;
{
...
}
i ++;
goto START;
КОНЕЦ:

Обратите внимание, что два последних перевода понятия «для» (помечены
Версия A и версия B) вычисляют одно и то же. Который из
быстрее? Если цикл повторяется много раз, я утверждаю, что версия (A) равна
быстрее, так как есть только один
(условно) goto каждый раз по циклу вместо двух goto в
версия (B) - одна
условный и один безусловный.Но версия (B), вероятно,
быстрее, если n часто равно 0, потому что в этом случае он быстро переходит в END
(в одном условном прыжке).

Инструкция по сравнению

Итак, вы хотите знать, как какое-то число A соотносится с другим числом B. Итак, вы вычитаете их.

Если A-B = 0, то A = B.
Если A-B> 0, то A> B.
Если A-B <0, то A

Ага, поэтому "cmp eax, 10" фактически внутренне вычитает 10 из значения в
eax. Если разница равна нулю, CPU устанавливает флаг ZF (Zero
Флаг).Если разница положительная или отрицательная, ЦП устанавливает
некоторые другие отвратительные флаги, чтобы указать на это (ЦП устанавливает различные флаги
как для знаковых, так и для беззнаковых сравнений).

Оказывается, «sub eax, 10» на самом деле устанавливает все одинаковые флаги. Таким образом, вы можете сравнить два числа с «cmp A, B» или «sub A, B», и вы получите тот же результат (но они не полностью взаимозаменяемы: «cmp» не изменит A!).

Итак, вы хотите прыгнуть, если предыдущее сравнение оказалось равным. Вы используете инструкцию «je» (прыгать, если равно).
Или вы хотите прыгнуть, если предыдущее вычитание оказалось нулевым. Вы используете инструкцию «jz» (переход, если ноль).

Оказывается, «je» и «jz» - это одна и та же инструкция машинного языка, потому что они оба делают одно и то же.

Суть в том, чтобы выполнить сравнение в сборке, сначала вы выполняете либо команду cmp, либо вспомогательную инструкцию, а затем:

Английский язык Менее Меньше или равно равно Больше или равно Больше Не равно
К / С ++ < <= ==> =>! =
Сборка
(с подписью)
мкл ил je или jz jge jg jne или jnz
Сборка
(без знака)
jb jbe je или jz дже и jne или jnz

Буква «b» в инструкциях сравнения без знака означает «внизу», а «a» - «вверху».

В C / C ++ компилятор может определить, нужны ли вам подписанные и неподписанные
сравнение по типам переменных. Нет никаких типов
в сборке, так что подбирать инструкцию вам решать!

Сравнить против вычесть: примеры

Subtract устанавливает все те же флаги сравнения, что и "cmp". Таким образом, этот код возвращает 1, потому что 5 <7.

 mov ecx, 5 

sub ecx, 7
jl yes_it_jumped
; ... иначе нет, не прыгнул: return 0
mov eax, 0
ret

yes_it_jumped:; ... так что верните 1
mov eax, 1
ret

(исполняемая ссылка NetRun)

Subtract также устанавливает нулевой флаг, поэтому вот очень маленький цикл с обратным отсчетом:

 mov edx, 5 
mov eax, 0

loop_start:
add eax, 7
sub edx, 1
jnz loop_start; Выполняется переход, если edx все еще не равен нулю

ret

(исполняемая ссылка NetRun)

Бесполезная информация: флаги и подробности о кровопролитии

Условные переходы фактически используют регистр EFLAGS:

Инструкция Полезно для… Флаги (см. Ниже)
jmp Всегда прыгать Нет
и Без знака> CF = 0 и ZF = 0
иэ Без знака> = CF = 0
сп Без знака < CF = 1
jbe Без знака <= CF = 1 или ZF = 1
jc Беззнаковое переполнение,
или сложение multiprecision
CF = 1
jecxz Сравнить ecx с 0 эккс = 0
je или jz Равенство ZF = 1
jg Подписано> ZF = 0 и SF = OF
jge Подпись> = SF = OF
jl Подписано < SF! = OF
ил Подпись <= ZF = 1 или SF! = OF
jne или jnz Неравенство ZF = 0
jo Переполнение со знаком OF = 1
jp или jpe Проверка четности (четность) ПФ = 1
ипо Проверка четности (нечетная) ПФ = 0
js Перейти при отрицательном значении SF = 1

Регистр «EFLAGS» на x86 хранит набор флагов, как показано на странице 73 тома руководства Intel Arch.
1.К важным флагам относятся:

  • ZF — «нулевой флаг». Устанавливается всякий раз, когда предыдущий арифметический результат
    был нулевым. Может использоваться с помощью «jz» (переход, если последний результат был нулевым) или
    «jnz» инструкция. «je» (прыгать, если равны) и «jne» (прыгать, если не равны)
    являются просто псевдонимами jz и jnz, потому что, если разница равна нулю,
    тогда два значения равны. Например, этот код проверяет, равен ли ввод 4:
    extern read_input
    вызов read_input
    cmp eax, 4
    je равно
    add eax, 20; Если не равно, прибавить
    равно: ret; Если равно, просто верните
  • CF — The
    «нести флаг».Установлен в
    указать бит, который выполняет сложение или вычитание.
    Для чисел со знаком это на самом деле не указывает на проблему, но для
    беззнаковые числа, это указывает на переполнение.
    Может использоваться командой «jc» (переход, если установлен флаг переноса).
    Устанавливается по всем арифметическим инструкциям.
    Можно добавить в другую арифметическую операцию с помощью «adc» (добавить с помощью
    нести). Например, вы можете сохранить бит, выходящий за пределы
    большое добавление вроде этого:
    mov ecx, 0x8000ff00
    add ecx, ecx
    mov eax, 0
    adc eax, eax; Добавляет eax, eax и флаг переноса вместе

    «adc» используется в реализации компилятора 64-битного «long».
    long «тип данных, и вообще в» арифметике с множественной точностью »
    программное обеспечение, такое как арифметическая библиотека множественной точности GNU.
    Его также можно было бы использовать для реализации проверки переполнения путем тщательного
    компилятор. Флаги переноса и нуля также используются беззнаковыми
    сравнение
    инструкции: «jb» (перейти, если ниже без знака), «jbe» (перейти, если без знака
    ниже или равно), «ja» (переходить, если выше без знака), и «jae» (переходить, если
    без знака выше или равно) обычным способом.
  • SF — «флаг знака», который указывает отрицательный знак
    результат. Это просто самый высокий бит результата, и он игнорирует любые
    переполнение. Поскольку переполнение может испортить ваш ответ, «jl» смотрит на
    как SF, так и OF для реализации сравнения со знаком.
  • OF—
    Подписанный «флаг переполнения». Устанавливается путем вычитания, сложения и сравнения в
    на удивление умный способ: например, если вы сложите два положительных числа,
    и получили отрицательное число, то произошло переполнение. Если вы добавите
    положительное и отрицательное число, переполнения быть не может. Если добавить два
    отрицательные числа и получить положительное число, то произошло переполнение. ИЗ
    является
    используется в подписанных инструкциях сравнения «jl» (перейти, если меньше чем),
    «jle» (переходить, если меньше или равно), «jg» (переходить, если больше) и
    «jge» (переход, если больше или равно) инструкции.См. Ниже, как именно используются OF и SF. Например:
    • jae: unsigned> =. Прыгает, если CF == 0. Хорошо, мы только что
      вычисляет a-b и хочет прыгнуть, если a> = b. Если a-b положительный (или
      ноль), тогда CF == 0 и a> = b, поэтому мы должны прыгнуть и сделать. Если а-б
      отрицательно, мы получим перенос, CF == 1, и мы не прыгаем.
    • jge: подписано> =. Прыгает, если SF == OF. Обычно мы
      не переполнялся, поэтому OF равно нулю, и это в точности то же самое, что и jae
      случай выше. Напомним, что это сравнение со знаком, поэтому мы можем получить
      переносить, если мы сравниваем отрицательные числа, поэтому не стоит смотреть на
      CF.Любопытно, что если мы переполнились, знаковый бит теперь неверен, поэтому, если
      OF — один, мы сравниваем SF с одним, что переворачивает сравнение
      правильный путь снова.
  • PF и AF — действительно причудливые древние флаги, пережитки 8
    битые дни. Оба они работают только с младшими 8 битами
    результат. PF возвращает нечетную четность, как для серийного
    коммуникация. AF указывает перенос из младших 4 бит в
    старшие 4 бита, которые использовались для древней техники «двоичного
    кодированная десятичная дробь «(BCD), где» 0x23 «означает двадцать три десятичной дроби, а не
    тридцать пять, как нормальный гекс.Это противно, теперь прошло и хорошо
    избавление!

Если вы пытаетесь представить себе условный jmp, вы должны знать, какие инструкции устанавливают какие
флаги. Например, «cmp», «and» (побитовое И), «sub» и
инструкция «добавить» устанавливает все флаги; «inc» (увеличение на 1) и «dec»
(уменьшение на 1) установить все, кроме CF; пока «мов» и все прыгают
инструкции не связываются с флагами. Легко случайно
перезаписать флаги, которые вам нужны, если вы оставите слишком много вещей между
время установки флага и время его чтения!

Фактически вы можете посмотреть на флаги с помощью инструкции «lahf», которая
копирует важные биты EFLAGS в регистр ah, то есть биты
8-16 eax
получить EFLAGS (SF: ZF: 0: AF: 0: PF: 1: CF).Вот код, который распечатывает
странная шестнадцатеричная константа в зависимости от флагов, хотя она выполняет
многократные сравнения:

 mov ecx, 4; значения для сравнения 
mov edx, 7

mov eax, 0

cmp ecx, edx
jne skipe; Флаг равно нулю
добавить eax, 0xE
skipe:

cmp ecx, edx
jno skipo; Флаг переполнения (при условии подписи)
add eax, 0x80
skipo:

cmp ecx, edx
jns пропускает; Флаг подписи (отрицательный результат)
add eax, 0x500
skips:

cmp ecx, edx
jnc skipc; Флаг переноса (33-й бит результата)
add eax, 0xC000
skipc:

ret

(Попробуйте сейчас в NetRun!)

Различные команды фанк-перехода, такие как «jc» (переход, если установлен CF), или
«jo» (переход, если установлен OF), также читайте флаги.Обратите внимание, что нет возможности
добраться до флагов или напрямую вызвать
инструкции по использованию флагов на C ++! Никто! Компиляторы C / C ++ игнорируют
целочисленное переполнение, и это невозможно исправить в C / C ++, но в
сборка так же проста как «жо»!

Бонусный ужас! Кровавые подробности сравнений

Достаточно внимательно посмотреть, чтобы убедиться, что различные
прыжки на самом деле делают правильное сравнение.

Легкий футляр для
беззнаковые числа, где ja и jb обозначают флаг переноса, CF:

v минус -> 4000000000 3000000000 2000000000 1000000000 0
4000000000 0
ZF
1000000000 2000000000 3000000000 4000000000
3000000000 3294967296
(32-разрядная версия -1000000000)
CF
0
ZF
1000000000 2000000000 3000000000
2000000000 2294967296
(32-разрядная версия -2000000000)
CF
3294967296
(32-разрядная версия -1000000000)
CF
0
ZF
1000000000 2000000000
1000000000 1294967296
(32-разрядная версия -3000000000)
CF
2294967296
(32-разрядная версия -2000000000)
CF
3294967296
(32-разрядная версия -1000000000)
CF
0
ZF
1000000000
0 294967296
(32-битная версия -4000000000)
CF
1294967296
(32-разрядная версия -3000000000)
CF
2294967296
(32-разрядная версия -2000000000)
CF
3294967296
(32-разрядная версия -1000000000)
CF
0
ZF

Таблица беззнакового вычитания.Обратите внимание, что CF устанавливается, когда ответ должен
быть отрицательным; это означает, что первое число меньше (беззнаковый
ниже), поэтому «jb» проверяет CF == 1. «jbe» проверяет CF == 1 или ZF == 1. Вот уменьшенная версия приведенной выше таблицы:

v минус -> 4млн 3млн 2млн 1млрд 0
4млн ZF (=) нет флагов (>) нет флагов (>)

без флагов (>)

без флагов (>)

3млн CF (<) ZF (=)

без флагов (>)

без флагов (>)

без флагов (>)

2млрд CF (<) CF (<) ZF (=)

без флагов (>)

без флагов (>)

1млрд CF (<) CF (<) CF (<) ZF (=)

без флагов (>)

0 CF (<) CF (<) CF (<) CF (<) ZF (=)

Не так уж и плохо, правда? Для беззнакового CF означает меньше.Обратите внимание, что SF
и OF сходят с ума во время этой таблицы, но для неподписанных мы можем
игнорируй их.

Вот та же таблица для чисел со знаком:

v минус -> 2000000000 1000000000 0 -1000000000 -2000000000
2000000000 0
ZF
1000000000 2000000000 -1294967296
(32-битная версия 3000000000)
SF OF
-294967296
(32-битная версия 4000000000)
SF OF
1000000000 -1000000000
SF
0
ZF
1000000000 2000000000 -1294967296
(32-битная версия 3000000000)
SF OF
0 -2000000000
SF
-1000000000
SF
0
ZF
1000000000 2000000000
-1000000000 1294967296
(32-битная версия -3000000000)
OF
-2000000000
SF
-1000000000
SF
0
ZF
1000000000
-2000000000 294967296
(32-битная версия -4000000000)
OF
1294967296
(32-битная версия -3000000000)
OF
-2000000000
SF
-1000000000
SF
0
ZF

Таблица вычитания со знаком.Если первое число меньше, чем
второе число, вы получите либо SF (отрицательный ответ), либо просто OF
(переполнение). Если он больше, вы либо не получите флагов (положительный ответ,
нет переполнения), или как SF, так и OF (отрицательный ответ из-за переполнения). Вот уменьшенная версия приведенной выше таблицы:

v минус -> + 2млн + 1млн 0 -1млрд -2млрд
+ 2млн ZF (=) нет флагов (>)

без флагов (>)

СФ ОФ (>) SF OF (>)
+ 1млн SF (<) ZF (=)

без флагов (>)

без флагов (>)

SF OF (>)
0 SF (<) SF (<) ZF (=)

без флагов (>)

без флагов (>)

-1млрд OF (<) SF (<) SF (<) ZF (=)

без флагов (>)

-2млрд OF (<) OF (<) SF (<) SF (<) ZF (=)

Опять же, для чисел со знаком * либо * SF, либо OF означает <(поэтому jl указан как «SF! = OF»).Нет flags, или * оба * SF и OF означают> (поэтому jg указан как «SF == OF»). Итак, «jl» и «jg» поступают правильно.

Вы, наверное, проживете всю свою жизнь и непременно сдадите этот класс,
не помня точно, почему «jl» проверяет SF! = OF. Ты сделаешь,
однако необходимо знать, что SF и OF существуют, потому что они действительно
полезны сами по себе.

Intel x86 JUMP краткий справочник

Восприятие прыжков и флажков долгое время было проблемой.
область для меня, тем более что книга ассемблера Intel показывает 32 из
все с похожими именами.Присмотревшись, я
обнаружил, что многие инструкции являются синонимами друг друга,
и на практике вся гамма не нужна, а в процессе
обнаружил, что моя копия Справочника программиста Intel 80386
Руководство
дало неверное описание одной из инструкций.

Итак, я сгруппировал их функционально, со всеми инструкциями
синонимы в одном ряду.

Инструкция Описание подпись Флаги короткий
переход
коды операций
около
скачок
коды операций
JO Перейти при переполнении OF = 1 70 0F 80
JNO Перейти, если не переполнение OF = 0 71 0F 81
JS Перейти, если знак SF = 1 78 0F 88
JNS Перейти, если не подписать SF = 0 79 0F 89
JE
JZ
Перейти, если равно

Перейти, если ноль
ZF = 1 74 0F 84
JNE

JNZ
Перейти, если не равно

Перейти, если не ноль
ZF = 0 75 0F 85
JB

JNAE

JC
Перейти, если ниже

Перейти, если не больше или равно

Прыгать, если переносить
без знака CF = 1 72 0F 82
JNB

JAE

JNC
Перейти если не ниже

Перейти, если больше или равно

Прыгай, если не несешь
без знака CF = 0 73 0F 83
JBE

JNA
Перейти, если меньше или равно

Перейти, если не выше
без знака CF = 1 или ZF = 1 76 0F 86
JA

JNBE
Перейти, если выше

Перейти, если не меньше или равно
без знака CF = 0 и ZF = 0 77 0F 87
JL

JNGE
Перейти если меньше

Перейти, если не больше или равно
подписано SF <> OF 7C 0F 8C
JGE

JNL
Перейти, если больше или равно

Перейти, если не менее
подписано SF = OF 7D 0F 8D
JLE

JNG
Перейти, если меньше или равно

Перейти, если не больше
подписано ZF = 1 или SF <> OF 7E 0F 8E
JG

JNLE
Перейти, если больше

Прыжок, если не меньше или равно
подписано ZF = 0 и SF = OF 7F 0F 8F
JP

JPE
Перейти, если четность

Перейти, если четность даже
ПФ = 1 7A 0F 8A
JNP

JPO
Перейти, если не четность

Перейти, если четность нечетная
ПФ = 0 0F 8B
JCXZ

JECXZ
Перейти, если регистр% CX равен 0

Перейти, если регистр% ECX равен 0
% CX = 0

% ECX = 0
E3

Процессоры x86 имеют большой набор флагов, которые представляют состояние
процессора, а инструкции условного перехода могут
от них в сочетании.

CF — флаг переноски
Установить на перенос или заимствование старшего бита; растаможен в противном случае
PF — флаг четности
Устанавливается, если младшие восемь бит результата содержат четное число
бит «1»; растаможен в противном случае
ZF — нулевые флаги
Устанавливается, если результат равен нулю; растаможен в противном случае
SF — знак флага
Установить равным старшему биту результата (0, если положительный, 1, если отрицательный)
OF — флаг переполнения
Устанавливается, если результат слишком большое положительное число или слишком мало
отрицательное число (исключая знаковый бит), чтобы соответствовать операнду назначения;
очищено иначе

8086 Руководство по ассемблеру для начинающих (часть 7)

Короткие условные переходы

В отличие от инструкции JMP , которая выполняет безусловный переход, есть инструкции
которые делают условные прыжки (прыжки только при выполнении некоторых условий).Эти инструкции разделены на три группы: первая группа просто проверяет один флаг, вторая
сравнивает числа как знаковые, а третий сравнивает числа как беззнаковые.

Инструкции перехода, которые проверяют одиночный флаг

Инструкция Описание Состояние Противоположная инструкция
JZ, JE Перейти, если ноль (равно). ZF = 1 JNZ, JNE
JC, JB, JNAE Прыгать при переносе (ниже, не выше равно). CF = 1 JNC, JNB, JAE
JS Перейти, если подпись. SF = 1 JNS
JO Перейти при переполнении. OF = 1 JNO
JPE, JP Перейти, если четность. PF = 1 JPO
JNZ, JNE Перейти, если не ноль (не равно). ZF = 0 JZ, JE
JNC, JNB, JAE Перейти, если не переносится (не ниже, выше). CF = 0 JC, JB, JNAE
JNS Перейти, если не подписать. SF = 0 JS
JNO Перейти, если нет переполнения. OF = 0 JO
JPO, JNP Перейти, если четность нечетная (без четности). PF = 0 JPE, JP

как вы уже могли заметить, есть некоторые инструкции, которые делают то же самое, это правильно,
они даже собраны в один и тот же машинный код, поэтому хорошо помнить, что
когда вы скомпилируете инструкцию JE — вы получите ее в разобранном виде: JZ , JC собирается так же, как JB и т. д…
различных названия используются, чтобы упростить понимание программ, их кодирование и, самое главное, запоминание. очень смещенный диссемблер понятия не имеет, как выглядела исходная инструкция, поэтому он использует наиболее распространенное имя.

, если вы имитируете этот код, вы увидите, что все инструкции собраны в JNB , рабочий код (код операции) для этой инструкции — 73h эта инструкция имеет фиксированную длину в два байта, второй байт — это количество байтов для добавить в регистр IP , если условие верно.поскольку инструкция имеет только 1 байт для сохранения смещения, она ограничена передачей управления до -128 байтов назад или 127 байтов вперед, это значение всегда подписано.

   jnc a
   jnb a
   чжэ

   мов топор, 4
a: mov ax, 5
   Ret

 

Инструкции перехода для чисел со знаком

Инструкция Описание Состояние Противоположная инструкция
JE, JZ Перейти, если равно (=).
Перейти, если ноль.
ZF = 1 JNE, JNZ
JNE, JNZ Перейти, если не равно (<>).
Перейти, если не ноль.
ZF = 0 JE, JZ
JG, JNLE Перейти, если больше (>).
Перейти, если не меньше или равно (не <=).
ZF = 0
и
SF = OF
JNG, JLE
JL, JNGE Перейти, если меньше ().
Перейти, если не больше или равно (не> =).
SF <> OF JNL, JGE
JGE, JNL Перейти, если больше или равно (> =).
Перейти, если не меньше (не <).
SF = OF JNGE, JL
JLE, JNG Перейти, если меньше или равно (<=).
Перейти, если не больше (не>).
ZF = 1
или
SF <> OF
JNLE, JG

<> — знак означает не равно.

Инструкции перехода для чисел без знака

Инструкция Описание Состояние Противоположная инструкция
JE, JZ Перейти, если равно (=).
Перейти, если ноль.
ZF = 1 JNE, JNZ
JNE, JNZ Перейти, если не равно ().
Перейти, если не ноль.
ZF = 0 JE, JZ
JA, JNBE Перейти, если вверху (>).
Перейти, если не ниже или равно (не <=).
CF = 0
и
ZF = 0
JNA, JBE
JB, JNAE, JC Перейти, если ниже (<).
Перейти, если не выше или равно (not> =).
Прыгай, если несешь.
CF = 1 JNB, JAE, JNC
JAE, JNB, JNC Перейти, если выше или равно (> =).
Перейти, если не ниже (не <).
Прыгай, если не несешь.
CF = 0 JNAE, JB
JBE, JNA Перейти, если ниже или равно (<=).
Перейти, если не выше (not>).
CF = 1
или
ZF = 1
JNBE, JA

Обычно, когда требуется сравнить числовые значения Инструкция CMP
используется (выполняет то же самое, что и инструкция SUB (вычитание), но не сохраняет
результат, просто влияет на флаги).

Логика очень простая, например:
нужно сравнить
5 и 2,
5-2 = 3
результат не равен нулю (нулевой флаг
установлен на 0).

Другой пример:
требуется сравнить 7 и
7,
7-7 = 0
результат равен нулю! (Нулевой флаг
установлен на 1 и JZ или JE выполнит прыжок).

вот пример инструкции CMP и условного перехода:

включить "emu8086.inc"

org 100h

mov al, 25; установите al на 25.mov bl, 10; установите bl на 10.

cmp al, bl; сравнить al - bl.

Je равный; перейти, если al = bl (zf = 1).

putc 'n'; если он попадает сюда, то al <> bl,
jmp stop; так что напечатайте «n» и прыгайте, чтобы остановиться.

равный:            ; если попадет сюда,
putc 'y'; тогда al = bl, поэтому выведите 'y'.

останавливаться:

ret; попадает сюда несмотря ни на что.

 

попробуйте приведенный выше пример с разными номерами для AL и BL ,
открыть флаги, нажав на кнопку флажков, использовать пошаговый
и посмотрим, что получится.вы можете использовать горячую клавишу F5 для перекомпиляции и перезагрузки программы в эмулятор.


петель

инструкция срабатывание и условие перехода напротив инструкции
LOOP уменьшить cx, перейти к метке, если cx не равно нулю. DEC CX и JCXZ
LOOPE уменьшить cx, перейти к метке, если cx не равно нулю и равно (zf = 1). ПЕТЛЯ
LOOPNE уменьшить cx, перейти к метке, если cx не ноль и не равно (zf = 0). ПЕТЛЯ
LOOPNZ уменьшить cx, перейти к метке, если cx не равно нулю и zf = 0. LOOPZ
LOOPZ уменьшить cx, перейти к метке, если cx не равно нулю и zf = 1. LOOPNZ
JCXZ перейти к метке, если cx равно нулю. ИЛИ CX, CX и JNZ

Циклы

— это в основном те же переходы, циклы можно кодировать без использования инструкции цикла, просто
используя условные переходы и сравнение, и это именно то, что делает цикл.все инструкции цикла используют регистр CX для подсчета шагов, поскольку вы знаете, что регистр CX имеет 16 бит и максимальное значение, которое он может удерживать, составляет 65535 или FFFF, однако с некоторой гибкостью можно поместить один цикл в другой, а другой — в другой. два, три и т.д … и получить хорошее значение 65535 * 65535 * 65535 …. до бесконечности …. или до конца оперативной памяти или стека. можно сохранить исходное значение регистра cx с помощью инструкции push cx и вернуть его к исходному, когда внутренний цикл закончится на pop cx , например:

org 100h

mov bx, 0; общий счетчик шагов.mov cx, 5
k1: добавить bx, 1
    mov al, '1'
    mov ах, 0эх
    int 10h
    нажать cx
    mov cx, 5
      k2: добавить bx, 1
      mov al, '2'
      mov ах, 0эх
      int 10h
      нажать cx
         mov cx, 5
         k3: добавить bx, 1
         mov al, '3'
         mov ах, 0эх
         int 10h
         петля k3; внутренний во внутреннем контуре.
      поп cx
      петля k2; внутренний цикл.
    поп cx
петля k1; внешний контур.

Ret

 

bx подсчитывает общее количество шагов, по умолчанию эмулятор показывает значения в шестнадцатеричном формате, вы можете дважды щелкнуть регистр, чтобы увидеть значение во всех доступных базах.

, как и все другие циклы условных переходов, имеют противоположного компаньона, который может помочь в создании обходных путей, когда адрес желаемого места слишком далек от ассемблера, автоматически собирает инструкцию обратного и длинного перехода, что составляет всего 5 байтов вместо 2, это может быть видел и в дизассемблере.

для более подробного описания и примеров см. полный набор инструкций 8086


Все условные переходы имеют одно большое ограничение, в отличие от инструкции JMP
они могут перемещаться только на 127 байтов вперед и на 128 байтов назад (обратите внимание, что большинство
инструкции собираются в 3 и более байта).

Мы можем легко обойти это ограничение, используя забавный трюк:

  • Получите противоположную инструкцию условного перехода из таблицы выше,
    заставьте его перейти на label_x .
  • Используйте команду JMP для перехода в желаемое место.
  • Определите label_x: сразу после инструкции JMP .

label_x: — может быть любое допустимое имя метки, но не должно быть двух или более меток с одинаковым именем.

вот пример:

включить "emu8086.inc"

org 100h

mov al, 5
mov bl, 5

cmp al, bl; сравнить al - bl.


jne not_equal; перейти, если al <> bl (zf = 0).
jmp равно
не равный:

добавить bl, al
sub al, 10
xor al, bl

jmp skip_data
db 256 dup (0); 256 байт
skip_data:

putc 'n'; если он попадает сюда, то al <> bl,
jmp stop; так что напечатайте «n» и прыгайте, чтобы остановиться.

равный:            ; если попадет сюда,
putc 'y'; тогда al = bl, поэтому выведите 'y'.останавливаться:

Ret
 

Примечание: последняя версия встроенного ассемблера 8086 автоматически создает обходной путь, заменяя условный переход на противоположный и добавляя большой безусловный переход. Чтобы проверить, установлена ​​ли у вас последняя версия emu8086, нажмите help-> проверьте наличие обновлений в меню.


Другой, но редко используемый метод — получение вместо этого немедленного значения.
этикетки. Когда немедленное значение начинается с $ относительного скачка
выполняется, иначе компилятор вычисляет инструкцию, которая перескакивает напрямую
к заданному смещению.Например:

org 100h

; безусловный переход вперед:
; пропустить следующие 3 байта + себя
; машинный код короткой инструкции jmp занимает 2 байта.
jmp $ 3 + 2
db 3; 1 байт.
b db 4; 1 байт.
c db 4; 1 байт.

; условный переход назад на 5 байтов:
mov bl, 9
dec bl; 2 байта.
cmp bl, 0; 3 байта.
jne $ -5; перейти на 5 байтов назад

Ret
 

<<< предыдущая часть <<<

>>> Следующая часть >>>


% PDF-1.4
%
7146 0 объект
>
эндобдж
xref
7146 528
0000000015 00000 н.
0000010892 00000 п.
0000016546 00000 п.
0000016683 00000 п.
0000016744 00000 п.
0000016851 00000 п.
0000016972 00000 п.
0000017092 00000 п.
0000017264 00000 п.
0000017391 00000 п.
0000017539 00000 п.
0000017681 00000 п.
0000017868 00000 п.
0000017981 00000 п.
0000018097 00000 п.
0000018232 00000 п.
0000018348 00000 п.
0000018533 00000 п.
0000018651 00000 п.
0000018784 00000 п.
0000018960 00000 п.
0000019083 00000 п.
0000019254 00000 п.
0000019374 00000 п.
0000019511 00000 п.
0000019630 00000 п.
0000019799 00000 п.
0000019915 00000 п.
0000020045 00000 п.
0000020184 00000 п.
0000020304 00000 п.
0000020464 00000 п.
0000020635 00000 п.
0000020779 00000 п.
0000020924 00000 п.
0000021069 00000 п.
0000021198 00000 п.
0000021333 00000 п.
0000021478 00000 п.
0000021609 00000 п.
0000021733 00000 п.
0000021868 00000 п.
0000022052 00000 п.
0000022204 00000 п.
0000022327 00000 п.
0000022467 00000 п.
0000022632 00000 п.
0000022750 00000 п.
0000022940 00000 п.
0000023095 00000 п.
0000023211 00000 п.
0000023354 00000 п.
0000023484 00000 п.
0000023655 00000 п.
0000023784 00000 п.
0000023917 00000 п.
0000024043 00000 п.
0000024182 00000 п.
0000024349 00000 п.
0000024458 00000 п.
0000024639 00000 п.
0000024780 00000 п.
0000024915 00000 п.
0000025052 00000 п.
0000025200 00000 н.
0000025333 00000 п.
0000025447 00000 п.
0000025618 00000 п.
0000025762 00000 п.
0000025883 00000 п.
0000026005 00000 п.
0000026139 00000 п.
0000026270 00000 п.
0000026417 00000 п.
0000026544 00000 п.
0000026705 00000 п.
0000026850 00000 п.
0000026997 00000 п.
0000027110 00000 п.
0000027260 00000 н.
0000027389 00000 н.
0000027529 00000 п.
0000027666 00000 п.
0000027863 00000 п.
0000027989 00000 п.
0000028166 00000 п.
0000028318 00000 п.
0000028447 00000 п.
0000028570 00000 п.
0000028714 00000 п.
0000028874 00000 п.
0000028996 00000 н.
0000029111 00000 п.
0000029290 00000 н.
0000029413 00000 п.
0000029551 00000 п.
0000029675 00000 п.
0000029852 00000 п.
0000029989 00000 н.
0000030115 00000 п.
0000030252 00000 п.
0000030415 00000 п.
0000030550 00000 п.
0000030684 00000 п.
0000030834 00000 п.
0000031034 00000 п.
0000031152 00000 п.
0000031293 00000 п.
0000031428 00000 п.
0000031563 00000 п.
0000031697 00000 п.
0000031824 00000 п.
0000031981 00000 п.
0000032151 00000 п.
0000032281 00000 п.
0000032422 00000 п.
0000032569 00000 п.
0000032707 00000 п.
0000032856 00000 п.
0000033004 00000 п.
0000033199 00000 п.
0000033320 00000 п.
0000033455 00000 п.
0000033597 00000 п.
0000033732 00000 п.
0000033869 00000 п.
0000034060 00000 п.
0000034206 00000 п.
0000034338 00000 п.
0000034477 00000 п.
0000034645 00000 п.
0000034793 00000 п.
0000034931 00000 п.
0000035056 00000 п.
0000035250 00000 п.
0000035367 00000 п.
0000035498 00000 п.
0000035629 00000 п.
0000035771 00000 п.
0000035901 00000 п.
0000036023 00000 п.
0000036137 00000 п.
0000036314 00000 п.
0000036424 00000 п.
0000036548 00000 н.
0000036672 00000 н.
0000036797 00000 п.
0000036921 00000 п.
0000037045 00000 п.
0000037155 00000 п.
0000037352 00000 п.
0000037475 00000 п.
0000037608 00000 п.
0000037743 00000 п.
0000037875 00000 п.
0000038009 00000 п.
0000038141 00000 п.
0000038273 00000 п.
0000038422 00000 п.
0000038562 00000 п.
0000038693 00000 п.
0000038826 00000 п.
0000038957 00000 п.
0000039087 00000 п.
0000039218 00000 п.
0000039350 00000 п.
0000039480 00000 п.
0000039647 00000 п.
0000039769 00000 п.
0000039957 00000 н.
0000040108 00000 п.
0000040230 00000 п.
0000040415 00000 п.
0000040523 00000 п.
0000040634 00000 п.
0000040779 00000 п.
0000040910 00000 п.
0000041050 00000 п.
0000041189 00000 п.
0000041362 00000 п.
0000041479 00000 п.
0000041608 00000 п.
0000041726 00000 п.
0000041891 00000 п.
0000042008 00000 п.
0000042143 00000 п.
0000042276 00000 п.
0000042413 00000 п.
0000042557 00000 п.
0000042697 00000 п.
0000042829 00000 п.
0000042971 00000 п.
0000043146 00000 п.
0000043303 00000 п.
0000043461 00000 п.
0000043643 00000 п.
0000043787 00000 п.
0000043905 00000 п.
0000044074 00000 п.
0000044188 00000 п.
0000044304 00000 п.
0000044471 00000 п.
0000044599 00000 п.
0000044767 00000 п.
0000044886 00000 п.
0000045019 00000 п.
0000045148 00000 п.
0000045280 00000 п.
0000045410 00000 п.
0000045542 00000 п.
0000045672 00000 п.
0000045804 00000 п.
0000045972 00000 п.
0000046092 00000 п.
0000046226 00000 п.
0000046358 00000 п.
0000046489 00000 п.
0000046620 00000 н.
0000046752 00000 п.
0000046922 00000 п.
0000047035 00000 п.
0000047175 00000 п.
0000047294 00000 п.
0000047426 00000 п.
0000047596 00000 п.
0000047727 00000 п.
0000047857 00000 п.
0000047987 00000 п.
0000048119 00000 н.
0000048251 00000 п.
0000048383 00000 п.
0000048514 00000 п.
0000048646 00000 н.
0000048778 00000 п.
0000048910 00000 п.
0000049042 00000 н.
0000049174 00000 п.
0000049344 00000 п.
0000049470 00000 п.
0000049604 00000 п.
0000049747 00000 п.
0000049904 00000 н.
0000050029 00000 п.
0000050146 00000 п.
0000050316 00000 п.
0000050429 00000 п.
0000050576 00000 п.
0000050728 00000 п.
0000050907 00000 п.
0000051030 00000 п.
0000051172 00000 п.
0000051325 00000 п.
0000051465 00000 п.
0000051643 00000 п.
0000051780 00000 п.
0000051918 00000 п.
0000052084 00000 п.
0000052223 00000 п.
0000052363 00000 п.
0000052495 00000 п.
0000052668 00000 п.
0000052806 00000 п.
0000053000 00000 п.
0000053113 00000 п.
0000053282 00000 п.
0000053399 00000 п.
0000053535 00000 п.
0000053654 00000 п.
0000053786 00000 п.
0000053975 00000 п.
0000054089 00000 п.
0000054215 00000 п.
0000054345 00000 п.
0000054542 00000 п.
0000054685 00000 п.
0000054828 00000 н.
0000055016 00000 п.
0000055132 00000 п.
0000055247 00000 п.
0000055427 00000 п.
0000055561 00000 п.
0000055702 00000 п.
0000055871 00000 п.
0000056014 00000 п.
0000056194 00000 п.
0000056335 00000 п.
0000056510 00000 п.
0000056648 00000 н.
0000056819 00000 п.
0000056930 00000 п.
0000057060 00000 п.
0000057188 ​​00000 п.
0000057316 00000 п.
0000057429 00000 п.
0000057601 00000 п.
0000057723 00000 п.
0000057856 00000 п.
0000058014 00000 п.
0000058146 00000 п.
0000058320 00000 п.
0000058454 00000 п.
0000058593 00000 п.
0000058747 00000 п.
0000058879 00000 п.
0000059013 00000 п.
0000059183 00000 п.
0000059313 00000 п.
0000059438 00000 п.
0000059571 00000 п.
0000059702 00000 п.
0000059819 00000 п.
0000059989 00000 н.
0000060117 00000 п.
0000060251 00000 п.
0000060414 00000 п.
0000060563 00000 п.
0000060715 00000 п.
0000060867 00000 п.
0000061018 00000 п.
0000061172 00000 п.
0000061326 00000 п.
0000061470 00000 п.
0000061617 00000 п.
0000061763 00000 п.
0000061909 00000 п.
0000062054 00000 п.
0000062202 00000 п.
0000062353 00000 п.
0000062504 00000 п.
0000062648 00000 п.
0000062797 00000 н.
0000062949 00000 п.
0000063101 00000 п.
0000063250 00000 п.
0000063402 00000 п.
0000063554 00000 п.
0000063700 00000 п.
0000063845 00000 п.
0000063990 00000 п.
0000064138 00000 п.
0000064285 00000 п.
0000064430 00000 н.
0000064576 00000 п.
0000064734 00000 п.
0000064883 00000 п.
0000065036 00000 п.
0000065186 00000 п.
0000065339 00000 п.
0000065485 00000 п.
0000065636 00000 п.
0000065790 00000 п.
0000065944 00000 п.
0000066095 00000 п.
0000066249 00000 п.
0000066403 00000 п.
0000066549 00000 п.
0000066695 00000 п.
0000066841 00000 п.
0000066987 00000 п.
0000067135 00000 п.
0000067285 00000 п.
0000067438 00000 п.
0000067590 00000 н.
0000067736 00000 п.
0000067867 00000 п.
0000068062 00000 п.
0000068225 00000 п.
0000068367 00000 п.
0000068492 00000 п.
0000068622 00000 п.
0000068773 00000 п.
0000068893 00000 п.
0000069024 00000 н.
0000069166 00000 п.
0000069339 00000 п.
0000069459 00000 п.
0000069604 00000 п.
0000069775 00000 п.
0000069892 00000 п.
0000070024 00000 п.
0000070182 00000 п.
0000070293 00000 п.
0000070419 00000 п.
0000070570 00000 п.
0000070721 00000 п.
0000070864 00000 п.
0000070990 00000 н.
0000071111 00000 п.
0000071232 00000 п.
0000071358 00000 п.
0000071466 00000 п.
0000071618 00000 п.
0000071767 00000 п.
0000071934 00000 п.
0000072044 00000 п.
0000072189 00000 п.
0000072341 00000 п.
0000072482 00000 п.
0000072693 00000 п.
0000072816 00000 п.
0000072980 00000 п.
0000073104 00000 п.
0000073239 00000 п.
0000073382 00000 п.
0000073522 00000 п.
0000073673 00000 п.
0000073793 00000 п.
0000073961 00000 п.
0000074099 00000 п.
0000074235 00000 п.
0000074373 00000 п.
0000074509 00000 п.
0000074647 00000 п.
0000074784 00000 п.
0000074922 00000 п.
0000075061 00000 п.
0000075193 00000 п.
0000075324 00000 п.
0000075485 00000 п.
0000075611 00000 п.
0000075748 00000 п.
0000075865 00000 п.
0000075988 00000 п.
0000076136 00000 п.
0000076310 00000 п.
0000076482 00000 п.
0000076613 00000 п.
0000076752 00000 п.
0000076924 00000 п.
0000077054 00000 п.
0000077193 00000 п.
0000077377 00000 п.
0000077508 00000 п.
0000077677 00000 п.
0000077802 00000 п.
0000077948 00000 п.
0000078067 00000 п.
0000078236 00000 п.
0000078360 00000 п.
0000078489 00000 п.
0000078658 00000 п.
0000078783 00000 п.
0000078928 00000 п.
0000079047 00000 п.
0000079223 00000 п.
0000079338 00000 п.
0000079470 00000 п.
0000079588 00000 п.
0000079736 00000 п.
0000079890 00000 н.
0000080015 00000 п.
0000080168 00000 п.
0000080304 00000 п.
0000080472 00000 п.
0000080597 00000 п.
0000080750 00000 п.
0000080887 00000 п.
0000081056 00000 п.
0000081167 00000 п.
0000081282 00000 п.
0000081464 00000 п.
0000081578 00000 п.
0000081714 00000 п.
0000081832 00000 п.
0000081981 00000 п.
0000082128 00000 п.
0000082302 00000 п.
0000082462 00000 п.
0000082559 00000 п.
0000082686 00000 п.
0000082831 00000 п.
0000082950 00000 п.
0000083073 00000 п.
0000083189 00000 п.
0000083305 00000 п.
0000083421 00000 п.
0000083536 00000 п.
0000083652 00000 п.
0000083768 00000 п.
0000083884 00000 п.
0000084000 00000 п.
0000084116 00000 п.
0000084232 00000 п.
0000084348 00000 п.
0000084464 00000 н.
0000084580 00000 п.
0000084696 00000 н.
0000084812 00000 п.
0000084928 00000 п.
0000085044 00000 п.
0000085160 00000 п.
0000085276 00000 п.
0000085392 00000 п.
0000085508 00000 п.
0000085624 00000 п.
0000085740 00000 п.
0000085856 00000 п.
0000085971 00000 п.
0000086073 00000 п.
0000086182 00000 п.
0000086233 00000 п.
0000087300 00000 п.
0000087516 00000 п.
0000119143 00000 н.
0000120296 00000 н.
0000121079 00000 н.
0000122148 00000 н.
0000122366 00000 н.
0000154282 00000 н.
0000155435 00000 н.
0000156218 00000 н.
0000157293 00000 н.
0000157516 00000 н.
00001

00000 н.
00001
00000 н.
00001

00000 н.
00001

00000 н.
00001

00000 н.
0000221681 00000 н.
0000222834 00000 н.
0000223617 00000 н.
0000223790 00000 н.
0000223999 00000 н.
0000227969 00000 н.
0000228067 00000 н.
0000228372 00000 н.
0000016523 00000 п.
0000011056 00000 п.
трейлер
] / Информация 7143 0 R / Назад 3554921 >>
startxref
0
%% EOF
7147 0 объект
> / PageMode / UseOutlines / Outlines 7149 0 R / OpenAction [7148 0 R / FitH 1000] / Метаданные 7144 0 R >>
эндобдж
7673 0 объект
>
транслировать
x = {| U ߜ Idi

A Руководство по сборке x86

Ассемблер (или ассемблер ) — это любой язык программирования низкого уровня, на котором существует очень строгое соответствие между операторами программы и инструкциями машинного кода архитектуры.«Некоторые из вас могут знать это из своих курсов по информатике, где вы должны были прочитать много единиц и нулей.

Что такое язык программирования низкого уровня?

Язык программирования низкого уровня — это язык программирования, который мало что дает к отсутствию абстракции от архитектуры набора команд компьютера.Языки низкого уровня обычно работают на инструкциях , а команды или функции на этом языке низкого уровня тесно сопоставляются с набором команд процессора.Слово «Низкий» означает, что между языком и процессором практически нет абстракции.

Языки программирования второго поколения обычно кодируют программы на Ассемблере, который, в свою очередь, генерирует машинный код для выполнения. Машинный код — единственный язык, который компьютеры могут понимать без какой-либо обработки. Ниже приведен пример машинного кода в шестнадцатеричной форме для генерации членов ряда Фибоначчи.

Машинный код X86

Точно так же, как мы не можем понять машинный код, компьютерный процессор не может понять наш язык, вот где в игру вступает язык ассемблера.

Ассемблер + компоновщик

Ассемблер + компоновщик вместе называются транслятором, который принимает предоставленную нами мнемонику ассемблера и преобразует их в машинный код, который мы затем можем выполнить.

Что такое мнемоника?

В программировании мнемоника — это имя, присвоенное машинной функции, или сокращение для операции. Каждая мнемоника представляет собой машинную команду низкого уровня или код операции в сборке. add , mul , lea , cmp и je являются примерами мнемоники.

Что такое регистры?

Регистры в программировании на ассемблере можно рассматривать как глобальные переменные, которые мы используем в языках программирования более высокого уровня для общих операций.

Некоторые различные типы регистров:

  • Общего назначения — Eax, Ebx, Esp, Ebp
  • Сегмент — CS, CD
  • Управление — EIP

Регистры общего назначения

Регистры общего назначения

Это некоторые из общих целевые регистры в архитектуре x86, каждый из вышеуказанных регистров может хранить 32 бита данных.Подумайте о регистре EAX с 32 битами, нижняя часть EAX называется AX, которая содержит 16 бит данных, AX также делится на две части AH и AL, каждая из которых имеет размер 8 бит, то же самое касается EBX, ECX и EDX.

EAX — регистр накопителя — используется для хранения операндов и данных результатов

EBX — базовый регистр — указывает на данные

ECX — регистр счетчика — операции цикла

В отличие от регистров, которые мы видели ранее, вышеуказанные регистры (ESP, EBP, ESI , EDI не может быть разделен на малые 8 бит, однако они делятся на старшие и младшие 16 бит регистра.Регистры в процессоре ограничены, вы не можете использовать их для хранения больших фрагментов данных, и здесь в игру вступает память. Данные могут храниться в памяти в структуре данных стека, регистр ESP служит операндом косвенной памяти , указывающим на вершину стека в любое время. Рассмотрим стек, содержащий данные, ESP указывает на вершину этого стека. Учтите, что в настоящее время стек содержит только целочисленное значение 2. так что 2 будет наверху стека. Регистр ESP будет указывать на целочисленное значение 2, и точно так же EBP указывает на основание стека.

То, что не помещается в регистры, живет в памяти

Доступ к памяти осуществляется либо путем загрузки и сохранения по адресам, как если бы это был большой массив, либо посредством операций PUSH и POP в стеке.

Это общая иерархия памяти компьютера, регистры находятся наверху, они быстрее остальных, но также меньше по размеру, при движении вниз по иерархии размер хранилища увеличивается, а скорость уменьшается

Как хранятся типы данных в памяти?

Существует несколько способов хранения многобайтовых типов данных. Двумя наиболее распространенными способами хранения типов данных в памяти являются Little Endian и Big Endian.

Тип хранилища данных Little Endian обычно используется в процессорах Intel, где основное внимание уделяется скорости обработки, а не количеству потребляемой мощности. Однако Arm производит процессоры для мобильных устройств, в которых аккумулятор и энергопотребление играют важную роль, поэтому Big Endian используется с процессорами Arm.

Сохранение 0x01234567

На изображении выше показано, как 0x01234567 будет храниться в памяти. В Big Endian данные хранятся как дано, но в Little Endian байты записываются в другом порядке, начиная с 0x01234567, сначала записывается 67, затем 45, затем 23 и, наконец, 01.

Более простое объяснение хранения CAFEBABE

Давайте поговорим о сегментах памяти!

  • Текст

    • Содержит инструкции для программы
  • Данные

    • Содержит данные для программы, например строки сообщений
  • BSS

    • Содержит все неинициализированные глобальные переменные

Hello World

На изображении выше представлена ​​структура программы Hello World на ассемблере.

Точкой входа в программу является глобальная переменная с именем _start:, и выполнение программы начинается оттуда. Раздел «Текст» содержит инструкции по печати и выходу из программы, раздел «Данные» содержит строку сообщения «Hello World!». который используется в инструкции печати в текстовом разделе.

Один из наиболее важных регистров: EIP

Как мы обсуждали ранее, сборка выполняется в соответствии с инструкциями, а инструкции записываются упорядоченным образом.

_start:

  1. mov $ 5, ecx
  2. mov $ 5, edx
  3. cmp ecx, edx

В приведенной выше ассемблерной программе выполнение начинается с символа _start:

EIP указывает на следующую инструкцию для выполнения

Перед выполнением 1-й инструкции «mov $ 5, ecx» EIP указывает на адрес первой инструкция.После выполнения EIP увеличивается на 1, так что теперь он будет указывать на вторую инструкцию. Выполнение программы будет происходить таким образом, как злоумышленник, если мы хотим получить контроль над программой, мы должны манипулировать значением EIP. Так же, как операторы if else в языках программирования более высокого уровня, ассемблер также предоставляет мнемонику для управления потоком программы, но давайте сначала разберемся с некоторыми основными мнемониками ассемблера.

Это некоторые из многих, оснащенных процессором, и они в значительной степени говорят сами за себя.Обсудим инструкцию Jmp .

jmp — это как функция goto в C, она безоговорочно переместится в указанное место. Рассмотрим этот код, который я привожу ниже.

  1. mov $ 5, ecx
  2. mov $ 5, edx
  3. jmp 5
  4. mov $ 6, ecx
  5. cmp ecx, edx
  6. je функция
  7. функция:

В приведенном выше фрагменте кода 1-я инструкция и 2-я инструкция будут выполняться одна за другой, в результате получается 5 в ecx и edx.Встречается инструкция jmp 5, поэтому поток напрямую передается инструкции номер 5. Таким образом, инструкция номер 4 никогда не будет выполнена. Теперь посмотрим на инструкцию cmp, после выполнения 3-й инструкции выполнение переходит к 5-й инструкции.

cmp ecx, edx

Которая будет сравнивать ecx и edx, вычитая одно из другого, если вычитание равно нулю, это означает, что оба значения, хранящиеся в регистрах ecx и edx, одинаковы.

Итак, нулевой флаг установлен в единицу, что указывает на то, что результат равен нулю.

Теперь обнаружена инструкция JE.

Инструкция JE проверит наличие нулевого флага в выполненной выше инструкции. JE просто означает переход, если он равен приведенной выше инструкции, если ecx и eds равны, je перенаправляет поток в функцию:

Компьютеры содержат многоуровневую структуру

  • Уровень 3

    • Библиотеки прикладного уровня
  • Уровень 2

  • Уровень 1

  • Уровень 0

ОС содержит библиотеки и драйверы

Чтобы взаимодействовать с ОС, мы должны использовать системный вызов (syscall), операционная система предлагает некоторые службы для приложения, работающего на ней.Эти службы доступны с помощью этих системных вызовов для открытия файлов, отображения памяти, чтения содержимого каталога и т. Д. Все эти действия требуют взаимодействия с оборудованием (жесткий диск, блок управления памятью) и управляются ОС.

Все возможные системные вызовы linux перечислены, поэтому на них можно ссылаться по номерам при выполнении вызовов в сборке.

т.е. EXIT — 1

WRITE — 4

Как работают системные вызовы?

На изображении ниже представлена ​​краткая информация о том, как работают системные вызовы.

Управление системными вызовами

Программа пользовательского пространства вызывает системный вызов, вызывая прерывание. Затем это прерывание передается в таблицу обработчиков прерываний, которая вызывает обработчик системного вызова, который, в свою очередь, вызывает определенный системный вызов. В основном существует два режима вызова SystemCall

Int 0x8; и

SYSENTER

Каждый системный вызов принимает некоторые аргументы, поэтому перед выполнением системного вызова нам нужно, чтобы наши параметры были готовы в регистрах.

EAX содержит номер системного вызова, а остальные регистры содержат другие аргументы, мы можем получить подробную информацию о конкретном системном вызове, посетив его справочную страницу в Linux с помощью «man (имя системного вызова)».

Т.е. man write

Итак, для системного вызова write нам нужно сохранить номер нашего системного вызова в EAX, который равен 4, затем сохранить EBX, дескриптор файла, и нам понадобится ECX, чтобы указать на нашу строку, которую нам нужно напечатать. и, наконец, edx, чтобы содержать длину, которую нам нужно напечатать. После сохранения всего этого мы просто вызываем прерывание с int 0x80.

То же самое, выход

Теперь давайте попробуем написать нашу первую программу печати Hello world! в сборке

global _start

раздел .text

_start:
mov eax, 0x4
mov ebx, 0x1
mov ecx, сообщение
mov edx, 12
int 0x80

  mov eax, 0x1
mov ebx, 0x5
int 0x80
  

раздел.данные

  сообщение: db "Hello, World!"; определить байт  

Сначала мы объявили _start как нашу глобальную переменную,

, затем начали текстовый раздел с .text

для выполнения записи, мы поместили номер системного вызова записи, равный «4», в eax

, затем дескриптор файла «1» в ebx .

затем из нашего раздела .data, указатель сообщения на ecx.

нам нужно напечатать 12 байтов, поэтому вставляем 12 в edx

, затем вызываем прерывание, в результате чего печатаем Hello world!

Теперь мы вставили 1 в eax, который является номером системного вызова для выхода.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *