Расширения 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.
В дополнение 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 выполняет логическую операцию 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 посчитывает количество начальных нулей до наименьшего установленного бита:
.code main proc mov rdx, 100011000b ; помещаем в регистр rdx число 280 tzcnt rax, rdx ; rax = 3 ret main endp end
Инструкция 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