Вычитание. Инструкция SUB

Последнее обновление: 11.07.2023

Инструкция SUB выполняет вычитание данных. По синтаксису она похожа на инструкцию ADD и имеет аналогичные формы:

SUB Xd, Xn, Xm      // Xd = Xn - Xm
SUB Xd, Xn, #imm    // Xd = Xn - #imm
SUB Xd, Xn, #imm, shift     // вычитание со сдвигом - непосредственный операнд #imm сдвигается с помощью выражения shift
SUB Xd, Xn, Xm, shift #N    // вычитание со сдвигом - регистр Xm сдвигается с помощью выражения shift #N
SUB Xd, Xn, Xm, extend #N   // вычитание с расширением - регистр Xm расширяется с помощью выражения extend #N

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

Например, вычтем из значения из регистра X1 число 22 и поместим результат в регистр X0

.global _start
 
_start: 
    MOV X1, 22
    SUB X0, X1, 2     // X0 = X1 - 2 = 22 - 2 = 20
    // X0 = X2 + младший байт из X1, сдвинутый влево на 4 бита = 0x2 + (0xFD << 4) =0x2 + 0xD0 = 0xD2
 
    MOV X8, #93         // номер функции Linux для выхода из программы - 93
    SVC 0               // вызываем функцию и выходим из программы

Передаваемый в качестве третьего параметра непосредственный операнд может иметь длину 12 бит (0-4096)

Вместо непосредственного операнда можно использовать значение из другого регистра:

MOV X1, 20
MOV X2, 5
SUB X0, X1, X2     // X0 = X1 - X2 = 20 - 5 = 15

Используя операцию вычитания из нулевого регистра, мы можем получить отрицательное значение числа (фактически умножить на -1):

MOV X1, 2
SUB X0, XZR, X1 // X0 = 0 - X1 = 0 - 2 = -2

Вычитание со сдвигом

Можно применять сдвиг и поворот. При этом если сдвиг применяется к непосредственному операнду, то значение сдвига (количество разрядов, на которые происходит сдвиг) должно быть равно 0 или 12:

SUB X2, X1, #0x10, LSL 12     // X2 = X1 - 0x10000

При сдвиге значения из регистра такого ограничения нет:

MOV X1, 20
MOV X2, 2
SUB X0, X1, X2, LSL #3     // X0 = X1 - (2 << 2) = 20 - 26 = 4

Вычитание с расширением

Для вычитания с расширением применяются ранее рассмотренные операции, которые позволяют извлечь один байт, полслова (2 байта) или слово (4 байта) из одного регистра

  • uxtb: извлекает один младший байт, который рассматривается как число без знака

  • uxth: извлекает беззнаковые полслова - два младших беззнаковых байта

  • uxtw: извлекает беззнаковое слово - четыре младших беззнаковых байта

  • sxtb: извлекает один младший байт, который рассматривается как число со знаком

  • sxth: извлекает младшие полслова (2 байта) со знаком

  • sxtw: извлекает младшее слово (4 байта) со знаком

Например, выполним вычитание одного байта

.global _start
 
_start: 
    MOV X1, #0x12FA         // X1 = 4861
    MOV X2, #0xFE            // X2 = 2
    SUB X0, X2, X1, UXTB   // X0 = X2 - младший байт из X1 = 0xFE - 0xFA = 0x04
    MOV X8, #93         // номер функции Linux для выхода из программы - 93
    SVC 0               // вызываем функцию и выходим из программы

Здесь извлекается младший байт из регистра X1 и вычитается из регистра X2. То есть младший байт числа 0x12FA равен FA. Он вычитается из 0xАУ, и получаем 0x04 или 4 в десятичной системе.

Пример с расширением и сдвигом влево:

.global _start
 
_start: 
    MOV X1, #0x12FA         // X1 = 4861
    MOV X2, #0xFE            // X2 = 2
    SUB X0, X2, X1, UXTB #4  // X0 = X2 - младший байт из X1 = 0xFE - 0xFA = 0x04
    // X0 = X2 - младший байт из X1, сдвинутый влево на 4 бита = 0xFE - (0xFA << 4) =0xFE - 0xA0 = 0x5E = 94
 
    MOV X8, #93         // номер функции Linux для выхода из программы - 93
    SVC 0               // вызываем функцию и выходим из программы

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850