Манипуляции битами. BMI

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

Расширения BMI1 предоставляют набор инструкций по манипуляции битами. В частности, инструкция bextr позволяет извлечь биты из 32- или 64-битного регистра общего назначения:

bextr regdest, regsrc, regctrl
bextr regdest, varsrc, regctrl

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

  • Биты с 0 по 7 в regctrl определяют начальную позицию извлекаемого бита во втором операнде (это должно быть значение в диапазоне от 0 до 31 для 32-битных операндов и от 0 до 63 для 64-битных операндов).

  • Биты с 8 по 15 регистра regctrl определяют количество битов, которые нужно извлечь из второго операнда.

Рассмотрим пример:

.code
main proc
    mov rcx, 100011011b     ; помещаем в регистр rcx  число 283 - 100011011b
    mov bl, 1               ; начальная позиция извлечения - со второго бита
    mov bh, 4               ; сколько бит извлекаем - 4 бита
    bextr rax, rcx, rbx
    ret
main endp
end

Исходное число, откуда извлекаем биты, хранится в регистре RCX и равно 283 или 100011011b в двоичной системе.

В регистр RBX помещаем начальную позицию для извлечения и количество извлекаемых битов. В данном случае удобно разбить установку параметров: в регистр BL поместить начальную позицию, а в регистр BH - количество битов. То есть извлекаем со второго бита (передается позиция 1 - отсчет идет с нуля), и извлекаем 4 бита. То есть в числе 100011011b берем 4 бита начиная со второго бита и получаем последовательность 1101 или число 13, которые инструкция bextr помещает в регистр RAX.

blsi

В дополнение BMI1 также предоставляет инструкцию blsi, которая извлекает из набора бит с наименьшим номером:

blsi regdest, regsrc
blsi regdest, varsrc

Операнды должны быть одинакового размера и могут быть 32- или 64-битными. Эта инструкция находит младший установленный бит во втором операнде (регистре или переменной). Она копирует этот бит в той же позиции первый операнд - целевой регистр, остальные все биты в целевом регистре обнуляются.

.code
main proc
    mov rcx, 100011000b     ; помещаем в регистр rcx  число 280
    blsi rax, rcx
    ret
main endp
end

Здесь инструкция blsi извлекает первый установленный бит из числа 280 или 100011000b и сохраняет его в той же позиции в регистр RAX. То есть здесь первый установленный бит находится в позиции 3 (индексация начинается с 0). И в той же позиции копирует этот бит в регистр RAX. Поэтому в регистре RAX в конечном счете окажется число 1000 или 8.

andn

Инструкция andn выполняет логическую операцию AND NOT

andn regdest, regsrc1, regsrc2
andn regdest, regsrc1, varsrc2

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

Эту инструкцию можно использовать, например, чтобы удалить наименьший установленный бит сразу после извлечения с помощью инструкции blsi. Например:

.code
main proc
    mov rax, 100011000b     ; помещаем в регистр rax число 280
    blsi rdx, rax           ; в rdx - 1000 или 8
    andn rax, rdx, rax      ; rax = rax - rdx = 272
    ret
main endp
end

Здесь извлекаем из числа 280 наименьший установленный бит с предыдущими нулями, что представляет число 8. Затем с помощью andn исключаем извлеченное число из исходного 280. Таким образом, в RAX окажется число 272.

Поскольку извлечение наименьшего бита и сохранение оставшихся битов представляют довольно распространенную задачу, то для этой цели в BMI1 есть специальная инструкция - blsr - она сбрасывает наименьший установленный бит:

blsr regdest, regsrc
blsr regdest, varsrc

Оба операнда должны быть одинакового размера - 32 или 64 бита. Эта инструкция получает данные из второго операнда, устанавливает наименьший бит в 0 и копирует результат в первый операнд. Если исходный операнд содержит 0, эта инструкция копирует 0 в первый операнд и устанавливает флаг переноса.

.code
main proc
    mov rdx, 100011000b     ; помещаем в регистр rdx число 280
    blsr rax, rdx           ; rax = 280 - 8 = 272
    ret
main endp
end

tzcnt

Инструкция tzcnt посчитывает количество начальных нулей до наименьшего установленного бита:

.code
main proc
    mov rdx, 100011000b     ; помещаем в регистр rdx число 280
    tzcnt rax, rdx           ; rax = 3
    ret
main endp
end

blsmsk

Инструкция blsmsk создает битовую маску, которая состоит из всех битов 1 до самого младшего установленного бита включительно. Инструкция blsmsk устанавливает остальные биты в 0. Если исходное значение равно 0, blsmsk устанавливает все биты в регистре назначения в 1 и устанавливает флаг переноса

blsmsk regdest, regsrc
blsmsk regdest, varsrc

Например:

.code
main proc
    mov rdx, 100011000b     ; помещаем в регистр rdx число 280
    blsmsk rax, rdx         ; rax = 1111 = 15
    ret
main endp
end

Наименьший установленный бит располагается по 3-му индексу - 1000, соответственно будет создана маска 1111

Если надо, чтобы маска НЕ включала наименьший установленный бит, то можно использовать комбинацию инструкций blsi и dec:

.code
main proc
    mov rdx, 100011000b     ; помещаем в регистр rdx число 280
    blsi rax, rdx         ; rax = 1000 = 8
    dec rax                ; rax = rax-1 = 1000 - 1 = 0111 = 7
    ret
main endp
end
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850