Как сложить 2 двоичных числа в ассемблере
Перейти к содержимому

Как сложить 2 двоичных числа в ассемблере

  • автор:

Базовые конструкции

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

Имитация конструкции if..else

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

IF условие THEN выполняемые действия, если условие верно ELSE выполняемые действия, если условие НЕ верно END IF

Или конструкция if..else в большинстве сиподобных языков

if(условие) < выполняемые действия, если условие верно >else

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

if(a == b)

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

.data a dword 22 b dword 22 .code main proc mov eax, a begin_if: cmp eax, b ; сравниваем a и b jne end_if ; если a и b не равны - к метке end_if mov eax, 1 ; если a и b равны end_if: ret main endp end

Для большей наглядности здесь определена необязательная метка begin_if, которая условно определяет начало конструкции. С помощью инструкции cmp сравниваем два значения и далее, если два числа не равны, переходим к метке end_if, которая знаменует конец конструкции.

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

cmp X, Y ; проверяем условие jcc ElseCode ; если условие не верно действия, если условие верно jmp EndOfIf ElseCode: действия, если условие не верно EndOfIf:

Например, если условие верно, в EAX поместим 1, если не верно — 0:

.data a dword 24 b dword 22 .code main proc mov eax, a begin_if: cmp eax, b ; сравниваем a и b jne else_if ; если условие не верно - к метке else_if mov eax, 1 ; если a и b равны jmp end_if ; в конец конструкции id else_if: mov eax, 0 ; если a и b не равны end_if: ret main endp end

Здесь, если условие не верно (a и b не равны), то переходим к метке else_if, после которой определено альтернативное действие.

Сложные условия

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

Операция AND

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

if((a == b) && (c != d))

то есть, условие истинно, если одновременно a равно b и c не равно d. Фактически мы можем представить это выражение как:

if(a == b) if(c != d)

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

.data a dword 22 b dword 22 c dword 24 d dword 25 .code main proc begin_if: mov eax, a cmp eax, b ; (a==b) jne end_if ; если первое условие не верно mov eax, c cmp eax, d ; (c!=d) je end_if ; если второе условие не верно mov eax, 1 ; если оба условия верны end_if: ret main endp end

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

Операция OR

Другой распространенный способ объединения условий представляет операция OR или логическое ИЛИ:

if((a == b) || (c != d))

то есть, условие истинно, если или a равно b, или c не равно d. Фактически мы можем представить это выражение как:

if(a == b) else if(c != d)

Если верно одно из условий, то выполняются те же самые действия.

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

.data a dword 22 b dword 22 c dword 24 d dword 25 .code main proc begin_if: mov eax, a cmp eax, b ; (a==b) je do_if ; если первое условие верно переходим к do_if mov eax, c cmp eax, d ; (c != d) je end_if ; если второе условие не верно (то есть если с == d), выходим do_if: mov eax, 1 ; если одно из условий верно end_if: ret main endp end

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

AND и OR

На основе выше указанной информации мы можем переложить на ассемблер более сложные условия, например, которые сочетают операции AND и OR:

if(((a == b) && (c != d)) || (e == f))

В данном случае общее условие истинно, если либо верно подусловие (a == b) && (c != d) , либо (e == f) . При составлении программы на языке ассемблера целесообразнее вначале проверить третье условие:

.data a dword 22 b dword 22 c dword 23 d dword 24 e dword 25 f dword 26 .code main proc begin_if: mov eax, e cmp eax, f ; (e==f) je do_if ; если третье условие верно переходим к do_if mov eax, a cmp eax, b ; (a==b) jne end_if ; если первое условие не верно mov eax, c cmp eax, d ; (c!=d) je end_if ; если второе условие не верно do_if: mov eax, 1 ; если одно из условий верно end_if: ret main endp end

То есть вначале проверяем третье условие. Если оно верно, то проверка первых двух условие не имеет смысла, и поэтому переходим к выполнению действий в метку do_if. Если третье условие не верно, то последовательно проверяем первое и второе условие. Если одно из них неверно, то переходим к концу конструкции в метке end_if.

Если нам важен порядок выполнения, то мы могли бы написать так:

.data a dword 21 b dword 22 c dword 23 d dword 24 e dword 25 f dword 26 .code main proc begin_if: mov eax, a cmp eax, b ; (a==b) jne or_if ; если первое условие не верно mov eax, c cmp eax, d ; (c!=d) jne do_if ; если второе условие верно or_if: mov eax, e cmp eax, f ; (e==f) jne end_if ; если третье условие верно переходим к do_if do_if: mov eax, 1 ; если одно из условий верно end_if: ret main endp end

Проверяем первое условие. Если оно неверно, переходим к метке or_if к проверке третьего условия. Если первое условие верно, переходим к проверке второго условия. Если второе условие верно, то проверять третье условие смысла нет, и переходим к метке do_if и выполнению указанных инструкций.

Сложение чисел на ассемблере

Есть такая программа, считывающая два числа, складывающая их и выводящая результат:

 SYS_EXIT equ 1 SYS_READ equ 3 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 section .text global _start _start: mov edx, len1 mov ecx, msg1 mov ebx, STDOUT mov eax, SYS_WRITE int 0x80 mov ecx, numX mov edx, 2 mov ebx, STDIN mov eax, SYS_READ int 0x80 mov edx, len2 mov ecx, msg2 mov ebx, STDOUT mov eax, SYS_WRITE int 0x80 mov ecx, numY mov edx, 2 mov ebx, STDIN mov eax, SYS_READ int 0x80 mov eax, [numX] sub eax, '0' mov ebx, [numY] sub ebx, '0' add eax, ebx add eax, '0' mov [res], eax mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, res mov edx, 2 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg4 mov edx, len4 int 0x80 mov eax,SYS_EXIT int 0x80 section .data msg1 db 'X=', len1 equ $-msg1 msg2 db 'Y=', len2 equ $-msg2 msg3 db 'X+Y=' len3 equ $-msg3 msg4 db 0xA, 0xB len4 equ 2 segment .bss numX resb 2 numY resb 2 res resb 1 

Вот что показывает objdump -D

app: file format elf32-i386 Disassembly of section .text: 08049000 : 8049000: ba 02 00 00 00 mov $0x2,%edx 8049005: b9 00 a0 04 08 mov $0x804a000,%ecx 804900a: bb 01 00 00 00 mov $0x1,%ebx 804900f: b8 04 00 00 00 mov $0x4,%eax 8049014: cd 80 int $0x80 8049016: b9 0c a0 04 08 mov $0x804a00c,%ecx 804901b: ba 02 00 00 00 mov $0x2,%edx 8049020: bb 00 00 00 00 mov $0x0,%ebx 8049025: b8 03 00 00 00 mov $0x3,%eax 804902a: cd 80 int $0x80 804902c: ba 02 00 00 00 mov $0x2,%edx 8049031: b9 02 a0 04 08 mov $0x804a002,%ecx 8049036: bb 01 00 00 00 mov $0x1,%ebx 804903b: b8 04 00 00 00 mov $0x4,%eax 8049040: cd 80 int $0x80 8049042: b9 0e a0 04 08 mov $0x804a00e,%ecx 8049047: ba 02 00 00 00 mov $0x2,%edx 804904c: bb 00 00 00 00 mov $0x0,%ebx 8049051: b8 03 00 00 00 mov $0x3,%eax 8049056: cd 80 int $0x80 8049058: a1 0c a0 04 08 mov 0x804a00c,%eax 804905d: 83 e8 30 sub $0x30,%eax 8049060: 8b 1d 0e a0 04 08 mov 0x804a00e,%ebx 8049066: 83 eb 30 sub $0x30,%ebx 8049069: 01 d8 add %ebx,%eax 804906b: 83 c0 30 add $0x30,%eax 804906e: a3 10 a0 04 08 mov %eax,0x804a010 8049073: b8 04 00 00 00 mov $0x4,%eax 8049078: bb 01 00 00 00 mov $0x1,%ebx 804907d: b9 10 a0 04 08 mov $0x804a010,%ecx 8049082: ba 01 00 00 00 mov $0x1,%edx 8049087: cd 80 int $0x80 8049089: b8 04 00 00 00 mov $0x4,%eax 804908e: bb 01 00 00 00 mov $0x1,%ebx 8049093: b9 08 a0 04 08 mov $0x804a008,%ecx 8049098: ba 02 00 00 00 mov $0x2,%edx 804909d: cd 80 int $0x80 804909f: b8 01 00 00 00 mov $0x1,%eax 80490a4: cd 80 int $0x80 Disassembly of section .data: 0804a000 : 804a000: 58 pop %eax 804a001: 3d 59 3d 58 2b cmp $0x2b583d59,%eax 804a006: 59 pop %ecx 804a007: 3d .byte 0x3d 804a008: 0a 0b or (%ebx),%cl Disassembly of section .bss: 0804a00c : . 

Вот что показывает отладчик:

(gdb) run Starting program: /home/rikitikitavi/projects/asm/app X=5 Y=5 Breakpoint 1, 0x08049058 in ?? () (gdb) c Continuing. Breakpoint 2, 0x08049069 in ?? () (gdb) info registers eax 0xa350a05 171248133 ecx 0x804a00e 134520846 edx 0x2 2 ebx 0xa05 2565 esp 0xffffd140 0xffffd140 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0x8049069 0x8049069 eflags 0x206 [ PF IF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 (gdb) si 0x0804906b in ?? () (gdb) info registers eax 0xa35140a 171250698 ecx 0x804a00e 134520846 edx 0x2 2 ebx 0xa05 2565 esp 0xffffd140 0xffffd140 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0x804906b 0x804906b eflags 0x206 [ PF IF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x0 0 

Как видно из objdump по адресу 0x08049069 находится команда сложения — add %ebx,%eax . Перед выполнением этой команды в регистрах eax и ebx должны быть необходимые числа 5 и 5, вместо этого я вижу 0xa350a05 и 0xa05 соответственно. 05 на конце как бы намекает что регистры содержат то что мне нужно, но что за остальная часть? — я правильно понимаю что 05 — это содержимое регистров al и bl а остальное мусор? Тогда откуда он взялся? Дальше я прибавляю код 0 к результату записываю в память и вывожу. Если при вводе вводить 4 и 5 то все работает:

rikitikitavi@rikitikitavi:~/projects/asm$ ./app X=4 Y=5 9 

а вот если ввести 5 и 5 то все ломатеся:

rikitikitavi@rikitikitavi:~/projects/asm$ ./app X=5 Y=5 : 
  1. Почему в отладчике я вижу какие-то непонянтые значения регистров?
  2. Как переделать вывод результата?

Как сложить 2 двоичных числа в ассемблере

mov ax, 10
mov bx, 20
and ax, bx

или я чего-то не понимаю?

Просто причем здесь сложение двух чисел и операция логического умножения?

Последний раз редактировалось rpy3uH; 13.02.2009 в 21:20 .
Участник клуба
Регистрация: 08.10.2007
Сообщений: 1,185

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

Высокая репутация
СуперМодератор
Регистрация: 27.07.2008
Сообщений: 15,551

Я думаю, сдвиги разрешены, они, вроде как, тоже к логическим операциям относятся. А так же операция mov, без которой тоже ни как.
Вот, что получилось (длинная портянка, скорее всего, можно упростить, это решение в лоб):

 pusha mov AL, A mov AH, B xor DL, DL mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH or DL, CL mov CH, BL and CH, BH ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL mov CL, BL and CL, CH and CH, BH or CH, CL and BL, BH or CH, BL ror DL, 1 shr AX, 1 mov BL, AL and BL, 1 mov BH, AH and BH, 1 mov CL, BL xor CL, BH xor CL, CH or DL, CL ror DL, 1 mov C, DL popa

A, B — исходные числа, C — результат.

Как сложить два числа в Ассемблере

В Контакте Ютуб Почта

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

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

Второе — мы можем складывать и отрицательные числа. Следовательно, и результат может быть отрицательным.

И да — сложение положительного и отрицательного чисел — это, по сути, вычитание одного числа из другого. Хотя для вычитания в ассемблере есть команда SUB.

Команда ADD существует с незапамятных времён — ещё с процессора 8086. Поэтому она поддерживается и всеми последующими процессорами Интел и совместимыми.

Простейший пример использования:

MOV AL, 3 ADD AL, 2 ; AL = 3 + 2 = 5

Здесь мы сначала записали число 3 в регистр AL, а затем применили команду ADD для сложения числа в AL с числом 2. Результат записывается в нашем случае в AL. И там после выполнения команды будет число 5.

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

MOV BX, 7 MOV AX, 2 ADD AX, BX ; AX = 7 + 2 = 9

Также вместо регистров можно использовать память:

MOV [110h], 3 ADD [110h], 2 ; По адресу [110h] теперь хранится 5 MOV AL, 1 ADD AL, [110h] ; AL = 6

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

Складывать можно не только константы, регистры и память, но и переменные:

ADD BL, MyVar ADD MyVar, 3

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

Есть ещё такая тема, как сложение с переносом. Но в этой статье рассматривать её не будет. Больше сведений об этом можно найти здесь.

И есть ещё такая вещь, как BCD-значения. Инструкции для работы с такими значениями: AAA, AAD, AAS, AAM.

На этом пока всё. Подключайтесь к группе Основы программирования в Телеграм, или к другим каналам (ссылки ниже), чтобы ничего не пропустить.

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

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