архитектура вычислительных систем / Лабораторные работы / Лабораторная работа_2_Ассемблер
Для записи отрицательного числа в программе на ассемблере используется символ ‘-’, например:
k db -101b
Со знаковыми и беззнаковыми числами нужно быть внимательным, потому что только вы знаете, какие числа используются в вашей программе! Процессору абсолютно все равно, какие данные он обрабатывает, поэтому невнимательность может привести к ошибке. Один и тот же байт может интерпретироваться по-разному, в зависимости от того со знаком число или без. Например, числу со знаком -5 соответствует число без знака 251:

Диапазоны значений чисел со знаком и без знака
При программировании на ассемблере (как, впрочем, и на многих других языках) необходимо учитывать ещё один важный момент. А именно – ограничение диапазона представления чисел. Например, если размер беззнаковой переменной равен 1 байт, то она может принимать всего 256 различных значений. Это означает, что мы не сможем представить с её помощью число, больше 255 (111111112). Для такой же переменной со знаком максимальным значением будет 127 (011111112), а минимальным -128 (100000002). Аналогично определяется диапазон для 2- и 4-байтных переменных.
2. Вычитание беззнаковых величин
Вычитание выполняется с помощью команды SUB (subtract — вычесть). В остальном все этапы выполнения вычисления повторяют действия, которые были описаны для операции сложения.
В регистр AX заносится уменьшаемое, а в регистр BX — вычитаемое. Результат выполнения инструкции появится в регистре AX.
Команда SUB имеет следующий формат: SUB приемник, источник
sub al,7 —> al = 3; al — приемник, 7 — источник
sub ax,10000 —> ax = 15000; ax — приемник, 10000 — источник
sub cx,bx —> cx = 85, bx = 15 (bx не меняется); cx — приемник, bx – источник
Проверьте, как микропроцессор использует форму двоичного дополнения для представления отрицательных результатов. Выполните вычитание из нуля единицы (т.е., 0 – 1). Какой результат получен и почему?
Команда INC
Команда INC увеличивает на единицу регистр. Она эквивалентна команде
ADD источник, 1
только выполняется быстрее.
inc al —> теперь AL = 16 (эквивалентна add al,1)
inc dh —> DH = 3Ah (эквивалентна add dh,1)
inc cl —> CL = 50h (эквивалентна add cl,1)
Команда DEC
Оператор dec уменьшает значение приемника на 1
3. Операции с байтами
В микропроцессорах Intel используются двухбайтовые машинные слова. Каждый регистр общего назначения (AX, BX, CX и DX) может хранить одно машинное слово. Однако имеется возможность оперировать с отдельными байтами этих регистров. В этом случае каждый регистр рассматривается состоящим из старшего (High) и младшего (Low) байтов. Обозначения отдельных байтов из регистров состоят из двух букв. Первая задает имя регистра (A, B, C или D), а вторая указывает, какой это байт регистра. Для обозначения старшего байта используется буква H, а младшего — L. Таким образом, регистр AX можно рассматривать, состоящим из двух однобайтовых регистров AH и AL.
Микропроцессор может выполнять арифметические операции над отдельными байтами.
Введите в регистр AX число 0102h (два байта) и выполните инструкцию
Сделать выводы каков результат выполнения операции, который будет помещен в регистр AH?
4. Умножение беззнаковых величин
Умножение двух 16-битных чисел может дать 32-разрядный результат, поэтому инструкция умножения MUL (multiply — умножить) размещает результат в двух регистрах DX и AX. Старшие 16 бит помещаются в регистр DX, а младшие в AX.
При выполнении операции умножения одним из множителей всегда является значение из регистра AX.
Выполните умножение чисел 7C4Bh (в регистр AX) и 100h (BX).
Сделать выводы каков результат операции и почему?
5. Деление беззнаковых величин
Команды микропроцессора предназначены для выполнения целочисленных операций. Так как деление целых чисел нацело происходит далеко не всегда, то результат деления формируется из двух целых чисел — частного и остатка от деления.
Делимое всегда помещается в пару регистров AX, DX, поэтому в инструкции деления DIV (divide — делить) необходимо указать только регистр с делителем. После выполнения деления регистр AX будет содержать частное, а регистр DX — остаток.
Выполните деление числа 7C4B12h (DX=007Ch, AX=4B12h) на 0100h (BX).
Каков результат выполнения операции и почему?
Пример программы
Чтобы всё стало совсем понятно, напишем небольшую программу. Требуется вычислить значение формулы: e=a-(b+c-1)+(-d). Все числа являются 8-битными целыми со знаком. Объявим их после кода и придумаем какие-нибудь значения.
Assembler: Arithmetic
Доброго времени суток, читатель! Снова с вами команда IT Root и на повестке, как вы уже могли догадаться — Assembler. А именно работа с положительными и отрицательными числами. Ассемблер на платформе x86 содержит множество арифметических инструкций, начиная с обычного сложения и вычитания и заканчивая логическими операторами. Но сперва поговорим о числах)
Положительные числа
Это самое простое. Вы уже должны представлять, как хранится положительное число в памяти компьютера. Напомню, что биты двоичного числа принято нумеровать от младшего к старшему, то есть справа налево. Каждый бит с порядковым номером n беззнакового целого двоичного числа, соответствует значению числа 2n.
В процессорах семейства Intel основной единицей хранения всех типов является байт. Байт, как уже говорилось, состоит из восьми битов.
При работе с числами помните, что в байт можно записать число со значением не более 255, в слово — со значением не более 65 535 и т.д. Поэтому будьте внимательны, особенно при операциях сложения/умножения. Например, если при работе с байтом вы выполните операцию сложения 255 + 1, то в результате должно получиться число 256. Однако если вы запишите результат в байт, то, к вашему удивлению, результатом будет не 256, а 0.
Например, максимальное значение, которое может хранить слово данных — это 65 535 или (216–1). Почему? Потому что 216 = 65 536 — столько различных чисел может хранить слово данных. Однако сюда же входит число 0, поэтому слово данных может хранить числа от 0 до 65 535.
Отрицательные числа
Мы разобрались, как в памяти компьютера представлены положительные числа, то есть числа без знака. Но ведь числа бывают и отрицательными, то есть числа со знаком минус. Чтобы применять те же самые байты и слова для представления отрицательных чисел, существует специальная операция, которая называется дополнение до двух. Но прежде чем рассмотреть эту операцию, покажем, как отрицательное число отличить от положительного.
Для того чтобы указать знак числа, достаточно одного разряда (бита). Обычно знаковый бит занимает старший разряд числа. Если старший бит числа равен 0, то число считается положительным. Если старший разряд числа равен 1, то число считается отрицательным.
Как вы успели заметить, для представления числа со знаком требуется использовать старший бит для определения знака числа. Это означает, что этот старший бит уже нельзя использовать для записи самого числа, то есть максимальное значение, которое мы сможем записать в байт, будет уже не 255, а всего 127.
Инверсия двоичного числа
А теперь разберёмся с загадочной операцией дополнение до двух. Для изменения знака числа выполняется инверсия, то есть для числа в двоичном представлении все нули заменяются единицами, а все единицы — нулями. Затем к полученному результату прибавляют 1. Возьмём, например, десятичное число 110 (в двоичной системе это число 01101110). Как видим, после выполнения этих преобразований в старшем разряде у нас 1, то есть число отрицательное.
Преобразования положительного десятичного числа со знаком в двоичное число выполняется также как и для чисел без знака. Преобразования отрицательного десятичного числа со знаком в отрицательное двоичное число выполняется при помощи операции дополнение до двух (с использованием двоичного дополнительного кода).
Преобразования положительного двоичного числа со знаком (в старшем бите 0) в десятичное число выполняется также как и для чисел без знака. Преобразование отрицательного двоичного числа (в старшем бите 1) в десятичное число выполняется путём нахождения его дополнительного кода. То есть для двоичного числа 10010010 операция будет следующей:
В итоге получаем число 110, но поскольку в исходном числе старший бит был равен 1, то это отрицательное число, то есть –110.
AfterWords
В этом посте мы разобрались с положительными и отрицательными числами в двоичной системе счисления, а также поняли принцип инверсии двоичного числа. Для работы с языками низкого уровня эти знания очень важны! Что ж, пожалуй на этом мы закончим статью. Успехов!)
Не забывайте, что множество интересных статей можно найти на нашем Telegram канале и в Telegram боте, также все статьи мы публикуем в Twitter и Facebook
FasmWorld Программирование на ассемблере FASM для начинающих и не только
Учебный курс. Часть 8. Числа со знаком и без
Автор: xrnd | Рубрика: Учебный курс | 27-03-2010 | Распечатать запись
Числа со знаком и дополнительный код
Помимо того, что процессор работает с двоичными числами, эти числа могут быть со знаком или без знака. Если число без знака, то оно просто представляет собой результат перевода десятичного числа в двоичный вид. Все биты в таком числе являются информационными и оно может принимать только неотрицательные значения.
Для представления чисел со знаком используется специальное кодирование. Старший бит в этом случае обозначает знак числа. Если знаковый бит равен нулю, то число положительное, иначе — отрицательное. Понятно, что положительное число со знаком будет выглядеть точно так же, как и число без знака.
С отрицательными числами чуть сложнее. Исторически для представления отрицательных чисел в компьютерах использовались разные виды кодирования: прямой, обратный и дополнительный код. В настоящее время наиболее часто используется дополнительный код, в том числе и в процессорах x86.
Чтобы сделать из положительного числа отрицательное, необходимо проинвертировать все его биты (0 заменяем на 1, а 1 заменяем на 0) и затем к младшему разряду прибавить единицу. Например, представим -5 в дополнительном коде:

В обратную сторону переводится точно также
Синтаксис FASM
Для записи отрицательного числа в программе на ассемблере используется символ ‘-‘, например:
Кстати, это работает и с числами в других системах счисления, и даже с символами
y db -25h z db -77o k db -101b s db -‘a’
Со знаковыми и беззнаковыми числами нужно быть внимательным, потому что только вы знаете, какие числа используются в вашей программе! Процессору абсолютно по барабану, какие данные он обрабатывает, поэтому невнимательность может привести к ошибке. Один и тот же байт может интерпретироваться по-разному, в зависимости от того со знаком число или без. Например, числу со знаком -5 соответствует число без знака 251:

Диапазоны значений чисел со знаком и без
При программировании на ассемблере (как, впрочем, и на многих других языках) необходимо учитывать ещё один важный момент. А именно — ограничение диапазона представления чисел. Например, если размер беззнаковой переменной равен 1 байт, то она может принимать всего 256 различных значений. Это означает, что мы не сможем представить с её помощью число, больше 255 (111111112). Для такой же переменной со знаком максимальным значением будет 127 (011111112), а минимальным -128 (100000002). Аналогично определяется диапазон для 2- и 4-байтных переменных.
Кстати, так как процессор Intel 8086 был 16-битным и обрабатывал за одну команду 16-бит, то 16-битная переменная называется слово (word), а 32-битная — двойное слово (double word, dword). Эти названия сохранились в ассемблере даже для 32-битных процессоров (и в WIN32 API, например). И от них же происходят названия директив dw (Define Word) и dd (Define Dword). Ну а db — это Define Byte.
Для наглядности вот табличка диапазонов чисел:
| Размер переменной |
Число без знака | Число со знаком | ||
|---|---|---|---|---|
| min | max | min | max | |
| байт | 00000000 | 11111111 | 10000000 | 01111111 |
| 0 | 255 | -128 | 127 | |
| слово | 00000000 00000000 | 11111111 11111111 | 10000000 00000000 | 01111111 11111111 |
| 0 | 65 535 | -32 768 | 32 767 | |
| двойное слово |
0000…0000 | 1111…1111 | 1000…0000 | 0111…1111 |
| 0 | 4 294 967 295 | -2 147 483 648 | 2 147 483 647 | |
| и т.д. | … | … | … | … |
Если результат какой-то операции выйдет за пределы диапазона представления чисел, то случится переполнение и результат будет некорректным. (Например, при сложении двух положительных чисел, можно получить отрицательное число!) Поэтому нужно быть внимательным при программировании и предусмотреть обработку таких ситуаций, если они могут возникнуть.
Комментарии:
Спасибо за этот учебный курс, читаю с самого начала, занимался ассемблером лет 6 назад, забылось многое, сейчас вот читаю и вспоминаю =) очень понятно изложен материал. С числами со знаками не отложилось и в прошлый раз..или просто не предал этому значения, но в процессе отладки встречаются знаковые и беззнаковые операторы перехода, и тут приходиться анализировать аргументы. Материал помог разложить мне у себя в мозгу всё по полочкам =) ..Спасибо.
Спасибо. Такие комментарии мне нравятся
Только не операторы перехода, а операнды сравнения.
Если быть совсем точным, то команды условного перехода. В ассемблере знаковые и беззнаковые операнды никак не проверяются компилятором и сравниваются одной и той же командой CMP. Разница только в выборе команды условного перехода.
Приветствую, xrnd и всех. Такой вот вопрос замучил: почему при сложении 106+25=131 возникает переполнение, а в случае 2+129=131 нет ? Складываю в байтовом регистре, т.е. в обоих случаях получаю одно и то же отрицат. число.
Тут надо быть внимательным с флагами.
Дело в том, что числа со знаком и числа без знака складываются и вычитаются совершенно одинаково. Разница только в интерпретации флагов и знакового бита.
Флаг переполнения OF имеет смысл только для чисел со знаком.
Переполнение возникает в следующих случаях:
1. положительное + положительное = отрицательное
2. отрицательное + отрицательное = положительное
В этих случаях результат сложения некорректен. Например, два положительных числа должны давать в сумме положительное, а не отрицательное.
Если записать твои примеры в виде чисел со знаком, то всё становится понятным:
106+25 = -125 (переполнение!)
2 + (-127) = -125 (нет переполнения)
Для чисел без знака проверять переполнение нужно по флагу переноса CF. Если был перенос из старшего разряда, то результат вышел за допустимые пределы.
Значит, если подразумевается, что числа со знаком, поднятый of будет означать неверный результат, если же числа беззнаковые, на of можно не обращать внимания, а смотреть на cf. Спасибо, теперь понятно.
я что то не понял при записи 101b=5 но при 010b получается 2. Так как при инвертации нельзя записать число с начальным нулем получается 10b
А при -5 получаеться 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1011b
я что то не понимаю вас
Имеются два числа: 11111011 и 11111011. Как ассемеблер понимает что нужно выводить отрицательное число или положительное?
Мне тоже интересно. Вопрос еще актуален, ответьте пожалуйста, о, гуру ассемблера
test ax,ax
во флаги будут помещены конкретные значения свойств числа, в том числе и s флаг, который и говорит о знаковом бите.
По флагу жеж. Если есть флаг SF=1 то данное число отрицательное.
Ассемблер никак не понимает, для него это одно и то же самое значение. Интерпретация ложится на программиста который это значение использует.
Например при сравнении двух чисел, записаных в регистры процессоа, с использованием команды cmp:
cmp ax, bx
процессор вычтет одно число из другого, и установит по результату вычитания значение флагов
ZF. SF, OF CF. Если надо интерпритировать сравнение как сравнение знаковых чисел, то проверяются одни флаги (ZF, OF), если как беззнаковые, то другие флаги (ZF, CF). Для удобства в ассемблере используется отдельные команды сравнения знаковых чисел (jl,jle, jg,jge) и беззнаковых (jb,jbe,ja,jae)
Тут дело вообще не в ассемблере. Сложение/вычитание выполняется одинаково, но в завимисости от результата выставляются флаги SF(знаковый),CF(переноса),OF(переполнения).
Некоторые операторы учитывают знаки(переходы ja,jb), в противовес им jg,jl -считают числа беззнаковыми (http://asmworld.ru/spravochnik-komand/jcond/).
В остальных случаях флаги нужно учитывать самому.
Всё зависит от алгоритма вывода. Последний бит — бит знака. Если его не считать, то получается число без знака, имеющее обычно диапазон [0.255]. Иначе, нужно выводить знак и остальные семь бит выводить в десятичной системе счисления (как я понимаю).
Подробнее разобрано тут: http://asmworld.ru/uchebnyj-kurs/022-vyvod-chisel-na-konsol/
Ассемблер — никак… Это задача программиста на ассемблере…
А вот отдельные команды, IDIV (целочисленное деление со знаком) например, по наличию «1» в старшем (знаковом) разряде своих операндов.
Отрицательные числа
Мы разобрались, как в памяти компьютера представлены положительные числа, то есть числа без знака. Но ведь числа бывают и отрицательными, то есть числа со знаком минус. Чтобы применять те же самые байты и слова для представления отрицательных чисел, существует специальная операция, которая называется дополнение до двух. Но прежде чем рассмотреть эту операцию, покажем, как отрицательное число отличить от положительного.
Для того чтобы указать знак числа, достаточно одного разряда (бита). Обычно знаковый бит занимает старший разряд числа. Если старший бит числа равен 0, то число считается положительным. Если старший разряд числа равен 1, то число считается отрицательным. Пример байта со знаком приведён на рис. 2.3.
| Разряды | Число | |||||||
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | -110 |
| 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | +110 |
Рис. 2.3. Отрицательное число в памяти компьютера.
Как вы успели заметить, для представления числа со знаком требуется использовать старший бит для определения знака числа. Это означает, что этот старший бит уже нельзя использовать для записи самого числа, то есть максимальное значение, которое мы сможем записать в байт, будет уже не 255, а всего 127. Диапазоны возможных значений чисел со знаком приведены в таблице 2.5.
Таблица 2.5. Диапазоны возможных значений чисел со знаком.
| Тип числа | Диапазон значений | Степени двойки |
| Байт | –128…+127 | -2 7 …(2 7 – 1) |
| Слово | –32 768…+32 767 | -2 15 …(2 15 – 1) |
| Двойное слово | –2 147 483 648… +2 147 483 647 |
-2 31 …(2 31 – 1) |
| Учетверённое слово | –9 223 372 036 854 775 808… +9 223 372 036 854 775 807 |
-2 63 …(2 63 – 1) |
А теперь разберёмся с загадочной операцией дополнение до двух. Для изменения знака числа выполняется инверсия, то есть для числа в двоичном представлении все нули заменяются единицами, а все единицы – нулями. Затем к полученному результату прибавляют 1. Возьмём, например, десятичное число 110 (в двоичной системе это число 01101110). Тогда
| Исходное число | 0110 | 1110 |
| Инверсия | 1001 | 0001 |
| Прибавляем 1 | 1001 | 0010 |
Как видим, после выполнения этих преобразований в старшем разряде у нас 1, то есть число отрицательное. А теперь проверим, что это число действительно отрицательное. Как это сделать? Вспомним, что (–110 + 110 = 0). То есть сложение полученных нами двоичных чисел тоже должно дать в результате ноль. Итак То есть у нас получилось девятиразрядное число 100000000 (десятичное 256). Однако мы работаем с байтом, а в байте, как известно, только 8 бит (от 0 до 7). То есть единица у нас перешла в 8-й бит, которого в байте попросту нет. То есть для байта это эквивалентно числу 0.
В некоторых источниках дополнение до двух носит название двоичный дополнительный код.
Подведём итоги
Преобразования положительного десятичного числа со знаком в двоичное число выполняется также как и для чисел без знака.
Преобразования отрицательного десятичного числа со знаком в отрицательное двоичное число выполняется при помощи операции дополнение до двух (с использованием двоичного дополнительного кода). То есть сначала число преобразовывается в двоичное, а потом используется двоичный дополнительный код.
Преобразования положительного двоичного числа со знаком (в старшем бите 0) в десятичное число выполняется также как и для чисел без знака.
Преобразование отрицательного двоичного числа (в старшем бите 1) в десятичное число выполняется путём нахождения его дополнительного кода. То есть для двоичного числа 10010010 операция будет следующей:
| Исходное число | 1001 | 0010 |
| Инверсия | 0110 | 1101 |
| Прибавляем 1 | 0110 | 1110 |
В итоге получаем число 110, но поскольку в исходном числе старший бит был равен 1, то это отрицательное число, то есть –110.