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

Простые арифметические операции

Предположим, у нас есть два числа, которые мы хотим сложить. Для этого мы можем использовать инструкцию ADD. Инструкция ADD принимает назначение для результата, а также два числа для сложения. В приведенном ниже коде показано, как мы можем загрузить два числа в память регистров, сложить их и сохранить результат.

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

Эти базовые инструкции хорошо работают, когда мы не ожидаем отрицательных чисел, переносов или переполнения. Так, например, если вы хотите создать счетчик, который начинается с 0 и увеличивается на 1 до конца списка, инструкция ADD будет отличным выбором. Если вместо этого вы хотите добавить или вычесть два числа, которые вводит пользователь, вам нужно будет рассмотреть такие случаи, как отрицание и переполнение. Для этих ситуаций вам понадобится другой набор арифметических инструкций.

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

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

Если мы хотим указать ARM обновить регистр CSPR для арифметики, мы используем специальный набор инструкций, называемый инструкциями установки флага. Эти инструкции выглядят так же, как и основные инструкции с добавлением S в конце. Так например добавить с флагами в ADDS.

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

Когда мы работаем с таким числом, нам нужен дополнительный контекст, чтобы понять, является ли это отрицательным числом или просто числом, которое оказалось очень большим. При использовании SUBS регистр CPSR устанавливается для отображения результата как отрицательного. Это позволяет нам точно знать, что результат отрицательный, а не большой.

В некоторых случаях мы также можем увидеть результат операции переноса. Например, перенос может происходить дополнительно, когда сумма двух чисел не помещается в 32 бита. В этом случае бит переноса устанавливается в регистре CPSR, если используется знаковая инструкция. Чтобы получить доступ к биту переноса, мы можем использовать еще один тип арифметической операции, известный как арифметика с переносом.

Арифметика с переносом

Предположим, вы сложили два числа вместе, и в результате есть перенос. Затем вы хотите добавить перенос к следующей выполненной операции. Для этого можно использовать арифметику с инструкцией переноса. Эти инструкции заканчиваются на C и могут использоваться для добавления переноса к операции. Для сложения инструкция переноса — ADC. Для вычитания это SBC. В приведенном ниже коде показан пример операции переноса.

В этом примере ADDS приведет к переносу, поскольку добавление FFFFFFFF и 4 будет слишком большим для хранения 32 бит. В результате следующая операция АЦП добавит R2 к R3 и добавит перенос к результату. Этот полный результат будет сохранен в R5.

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

Теперь вы понимаете основы арифметики в сборке ARM. Эти операции часто используются в программировании на ассемблере и будут появляться во многих ситуациях в процессе программирования.