Рубрики
Учебный курс

Учебный курс. Урок 10. Сложение и вычитание с переносом

Регистры процессоров ограничены в разрядности архитектурой процессора (как правило, x86/64), поэтому сложение чисел, как из реального мира, так и из виртуального в ассемблере имеет дополнительные нюансы. В предыдущем Уроке 9. Сложение и вычитание операции сложения и вычитания выполнялись только в границах размеров регистров.

Сложение операндов превышающих размеры регистров приводит к переполнению и установке флага CF. Команда ADC предназначена для сложения с учетом флага CF, а команда SBB вычитает с учетом флага CF. Данные инструкции корректно складывают и вычитают любые целые числа, превышающие вместимость регистров процессора (16 бит в рамках курса).

Алгоритм таких арифметических операции прост:

  • в коде программы командами ADD и SUB сначала складываются или вычитаются младшие разряды чисел
  • затем командами ADC и SBB складываются или и вычитаются старшие части

Команды ADC и SBB используют значение флага CF, куда записываются перенос из старшего разряда, что позволяет складывать числа любой длины по частям. Представьте эту процедуру как сложение и вычитание чисел столбиком.

Обратите внимание на иллюстрацию, показывающую операцию сложения 16 битных чисел (с использованием ADD):

Побитное сложение 2 байтовых чисел.

Обратите внимание, произошел перенос из младшей части результата (старший 7-й бит) в старшую часть (младший 8-й бит). Если бы мы были ограничены разрядностью процессора в 8 бит, то складывая эти числа по частям, сначала младшую, а затем старшую часть, и оба раза командой ADD, мы потеряли бы этот вынесенный бит и получили некорректный результат. Ситуацию исправляет флаг CF, который хранит перенос из старшего разряда. Поэтому нам нужно использовать для сложения старшей части только команду ADC:

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

Ровно так же обстоит дело с вычитанием чисел превышающих размеры регистров. Для наглядности рассмотрим на примере программы, которая выполнит вычисление формулы x = k + l – m + 1, где операнды x, k, l и m являются беззнаковыми 32-битными целыми числами. Как вы уже поняли, мы будем складывать и вычитать их с учетом флага переноса, то есть в два подхода: первыми выполнив команды над младшими разрядами чисел, и завершив вычисления командами ADC и SBB с учётом флага переноса.

Обратите внимание, что в контексте этой программы нельзя использовать команду INC, поскольку INC не влияет на флаг переноса! К примеру, у нас беззнаковое десятичное число 65535 в регистре AX, то, сделав INC AX, мы получим 0 в AX, а флаг CF не изменится!

Директива word в коде программы ограничивает размер операнда до 16 бит. Первым в памяти хранится младший разряд, поэтому для чтения старшего разряда нужно сместиться на 2 байта вправо. В нашем случае word[k] обращается к младшему разряду операнда k, а word[k+2] к старшему.

Рассмотрим, как в отладчике представлены переменные k, l, m и x:

Окно Tourbo Debugger. Представление чисел в дампе памяти.

Процессоры Intel  имеют особенный порядок представления значений в памяти. Первым в памяти (по младшему адресу) идет младший байт числа (little endian). Вправо по старшенству идут остальные байты числа. То есть в окошке дампа значения правильнее читать справа налево. В отличие от дампа памяти значения в регистрах показаны в привычном порядке. Для наглядности приведены значения операнда k в окне дампа и в регистрах (старший разряд в BX, а младший в AX).

«Плюшка» ассемблера в том, что программист не ограничен в длине целых чисел. То есть складывать и вычитать можно сколь угодно длинные числа. В противоположность к языкам высокого уровня, где размеры переменных, как правило, ограничены компилятором. От слов к делу, чтобы наглядно показать сложение двух 7-байтных числа (с использованием только одного регистра процессора):

Чтобы обратиться к самому старшему байту значения используется директива byte[x+6]. Думаю, здесь вы уже поняли, что к чему 🙂 Использование команды MOV безопасно между командами сложения или вычитания, поскольку команда MOV не затрагивает флаги процессора.

Упражнение

Внимательно прочитали материал? Отлично! Самое время выполнить упражнение к уроку — вычислить формулу x = k — 1 + n — m. Объявите все операнды как 3-х байтные значения без знака. Скомпилируйте программу без ошибок и проверьте в отладчике Tourbo Debugger. Разместите результат в комментариях к этому уроку.

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