Операции сдвига больших чисел

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

Реализация операций сдвига в отношении больших чисел подразумевает сочетание операций сдвига и вращения. Для реализации сдвига влево можно применить инструкции shl и rcl

Например, чтобы выполнить сдвиг влево 128-разрядного числа, сначала необходимо сдвинуть младшие 64 разрядов влево (с помощью инструкции shl). При этом надо также получить значение самого старшего бита из этих младших 64 разрядов, который отбрасывается при сдвиге влево. Для этого применяется обычно флаг переноса CF. Затем надо сдвинуть этот бит в самый младший бит старщих 64 разрядов числа, одновременно сдвигая все остальные биты влево. Для этого применяется инструкция rcl.

Рассмотрим на примере сдвига влево 192-разрядного числа:

.data
    ; 192-разрядное число
    num qword  0ffffffffffffffffh, 
               0e000000000000000h, 
               0
.code
main proc
    shl qword ptr num, 1       ; сдвигаем младшие 64 бита влево
    rcl qword ptr num[8], 1    ; средние 64 бита
    rcl qword ptr num[16], 1   ; старшие 64 бита
    ret
main endp
end

Чтобы учесть сдвигаемый старший бит из предыдущих 64 разрядов для сдвига вторых и последующих 64 разрядов числа применяется инструкция вращения влево с переносом rcl. Однако поскольку нам надо учитывать сдвигаемый старший бит из предыдущих 64 разрядов, то одномоментно мы можем сдвинуть число только на 1 бит. Что может быть не очень удобно, особенно если нам надо сдвинуть на большее количество разрядов. В этом случае можно использовать циклические конструкции:

.data
    ; 192-разрядное число
    num qword  0ffffffffffffffffh, 
               0e000000000000000h, 
               0
.code
main proc
    mov cl, 4                   ; сдвигаем на 4 разряда
shift:
    shl qword ptr num, 1       ; сдвигаем младшие 64 бита влево
    rcl qword ptr num[8], 1    ; средние 64 бита
    rcl qword ptr num[16], 1   ; старшие 64 бита
    dec cl
    jnz shift                  ; пока не сдвинем все разряды, повторяем цикл

    mov rax, qword ptr num[16] ; RAX = 0eh или 14
    ret
main endp
end

Аналогичным образом можно использовать сдвиг вправо (инструкция shr) и арифметический сдвиг вправо (инструкция sar), только сдвиг начинается со старших 64 разрядов числа:

; обычный сдвиг вправо
 sar qword ptr num[16], 1
 rcr qword ptr num[8], 1
 rcr qword ptr num[0], 1

; арифметический сдвиг вправо
 shr qword ptr num[16], 1
 rcr qword ptr num[8], 1
 rcr qword ptr num[0], 1

Многобитный сдвиг

Для сдвига на несколько бит мы можем использовать циклическую конструкцию, как было показано выше. Однако ассемблер также предоставляет и другой способ - инструкции shld (сдвиг влево) и shrd (сдвиг вправо). Они позволяют сдвинуть число на несколько бит. Они имеют следующий синтаксис:

shld Operand1, Operand2, constant
shld Operand1, Operand2, cl
shrd Operand1, Operand2, constant
shrd Operand1, Operand2, cl

В качестве первого операнда регистр или переменная, которые могут быть 16-, 32- или 64-разрядными. В первый операнд помещается результат сдвига. Второй операнд - собственно сдвигаемое число, в качестве которого должен выступать регистр. Оба операнда должен иметь одинаковый размер. Третий операнд - константа или регистр CL указывают, на сколько битов надо выполнить сдвиг.

При сдвигах с помощью данных иснтрукций также могут меняться знаки. При сдвиге влево инструкцией shld флаг переноса CF содержит последний сдвинутый старший бит из первого операнда. Если сдвигаем только на 1 бит, флаг переполнения OF будет содержать 1, если при сдвиге изменится знак первого операнда. Если сдвигаем на большее число битов, флаг переполнения не определен. Флаг будет ZF равен 1, если результатом сдвига является 0. Флаг знака SF будет содержать старший бит результата.

Сдвиг влево в ассемблере с флагом переноса

При сдвиге вправо инструкцией shrd флаг переноса CF содержит последний сдвинутый младший бит из первого операнда. Если сдвигаем только на 1 бит, флаг переполнения OF будет содержать 1, если при сдвиге изменится старший бит первого операнда. Если сдвигаем на большее число битов, флаг переполнения не определен. Флаг будет ZF равен 1, если результатом сдвига является 0. Флаг знака SF будет содержать старший бит результата.

Сдвиг вправо в ассемблере с флагом переноса

Например, перепишем предыдущий пример со сдвигом 192-разрядного числа на 4 бита с помощью инструкции shld:

.data
    ; 192-разрядное число
    num qword  0ffffffffffffffffh,  ; младшие 64 бита
               0e000000000000000h,  ; средние 64 бита
               0                    ; старшие 64 бита
.code
main proc
    mov rax, num[8]
    shld num[16], rax, 4

    mov rax, num[0]
    shld num[8], rax, 4
    shl num[0], 4

    ; mov rax, qword ptr num[16]  ; RAX = 14
    ; mov rax, qword ptr num[8]  ; RAX = 15
    ret
main endp
end

Первая инструкция shld сдвигает биты из num[8] в num[16], не затрагивая значения в num[8]. Вторая инструкция shld сдвигает биты из num в num[8]. Наконец, инструкция shl сдвигает младшие 64 бита из num на 4 разряда влево.

Аналогичным образом сдвиг вправо с помощью shrd.

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