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

Учебный курс. Урок 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. Разместите результат в комментариях к этому уроку.

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

Вот вариант ДЗ «по 3 байта». Значения — для наглядности.

use16
org 100h

mov ax,word[k]
sub ax,1
mov word[x],ax

mov al,byte[k+2]
sbb al,0
mov [x+2],al

mov ax,word[x]
add ax,word[n]
mov word[x],ax

mov al,[x+2]
adc al,byte[n+2]
mov [x+2],al

mov ax,word[x]
sub ax,word[m]
mov word[x],ax

mov al,[x+2]
sbb al,byte[m+2]
mov [x+2],al

mov ax,4C00h
int 21h
;——————-
k dw 0x6666
db 0x66
n dw 0x3333
db 0x3
m dw 0x1111
db 0x1
x rb 3

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

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