Может кто-нибудь объяснить мне побитовые операции ARM? - PullRequest
6 голосов
/ 28 января 2012

Может ли кто-нибудь объяснить мне битовые сдвиги ARM, как будто мне пять лет? У меня очень плохое понимание всего, что связано с недесятичной системой счисления, поэтому понимание концепций битовых сдвигов и битовых операторов Мне трудно.

Что будет делать и почему каждый из следующих случаев (что закончится в R3 и что произойдет за кулисами на уровне битов)?

/** LSL **/
mov r0, #1
mov r3, r0, LSL#10

/** LSR **/
mov r0, #1
mov r3, r0, LSR#10

/** ORR **/
mov r0, #1
mov r1, #4
orr r3, r1, r0

/** AND **/
mov r0, #1
mov r1, #4
and r3, r1, r0

/** BIC **/
mov r0, #1
mov r1, #4
bic r3, r1, r0

PS. Не объяснять это в терминах C-побитовых операторов. Я тоже не знаю, что они делают (>>, <<, |, &).

Ответы [ 2 ]

19 голосов
/ 28 января 2012

Таблицы истинности, два входа, два числа слева и один выход, число справа:

ИЛИ

a b  c     
0 0  0
0 1  1
1 0  1
1 1  1

два левых входа a и b представляют собойчетыре возможных комбинации входов, не больше, не меньше, чем список.

Считайте, что 1 означает истину, а 0 - ложь.И слово OR в этом случае означает, что если OR b истинно, то c истинно.И, как вы видите в таблице, по горизонтали, если a или b - true, тогда c - true.

AND

a b  c
0 0  0
0 1  0
1 0  0
1 1  1

И означает, что они оба должны быть истинными, если a и bоба истина, то с истина.Существует только один случай, когда это существует выше.

Теперь возьмем два байта 0x12 и 0x34, которые в десятичном формате - это 18 и 52, но нас не очень заботит десятичное число.мы заботимся о двоичном 0x12 0b00010010 и 0x34 0b00110100.Битовые операторы, такие как AND и OR и XOR на ассемблере, означают, что вы берете один бит из каждого операнда, и это дает результат в том же месте бит.Это не то же самое, что добавить, где у вас есть такие вещи, как плюс, равный бла, несите тот.

, поэтому мы выстраиваем биты

0b00010010    0x12
0b00110100    0x34

Так что наклоните голову в сторону, как вы собираетесь взятьоткусите тако в левой руке и визуализируйте таблицу правды выше.Если мы посмотрим на два бита справа, они равны 0 и 0, следующие два бита равны 1 и 0 и так далее.Поэтому, если мы хотим выполнить операцию ИЛИ, правило таково: если a или b имеет значение true, тогда c, результат равен true

   0b00010010
   0b00110100
OR ==========
   0b00110110

Голова наклонена вправо, младший значащий бит (бит встолбец единиц в числе) 0 или 0 = 0, ни один не установлен.следующий столбец (столбец двойок) 1 или 0 = 1, по крайней мере, один является истинным.и так далее

0x12 ИЛИ 0x34 = 0x36

При сборке рычага это будет

mov r0,#0x12
mov r1,#0x34
orr r2,r0,r1

после того, как операция или r2 будет содержать значение 0x36.

Теперь давайте и те числа

    0b00010010
    0b00110100
AND ==========
    0b00010000

Помня, что наша таблица истинности и правила a и b должны быть истинными (a 1) мы наклоняем голову вправо, 0 и 0 равно 0,оба не соответствуют действительности.и при проверке только один столбец имеет оба входа с 1, столбец 16 с.это оставляет нам 0x12 И 0x34 = 0x10

При сборке рычага это будет

mov r0,#0x12
mov r1,#0x34
and r2,r0,r1

Теперь мы перейдем к инструкции BIC.Что означает побитовую ясность, которая, надеюсь, будет иметь смысл чуть позже.Bic на руке является anded с не б.Это не другая таблица истинности, но только один вход и один выход

NOT

a  c
0  1
1  0

Только с одним входом у нас есть только два варианта выбора 0 и 1, 1 истинно, 0 ложно.NOT означает, что если не a, то c истинно.когда a не является истинным, c является истинным, когда a является истинным, c не является истинным.В основном это инвертирует.

То, что делает bic, имеет два входа a и b, операция c = a AND (НЕ b), поэтому таблица истинности для этого будет:

a AND (NOT b)

a b  c
0 1  0
0 0  0
1 1  0
1 0  1

Я начал с таблицы истинности AND, а затем отметил биты b, где b было 0 в таблице истинности AND, я сделал это 1, где b был 1 в таблице истинности AND, которую я сделалэто 0.

Таким образом, bic-операция на 0x12 и 0x34 равна

    0b00010010
    0b00110100
BIC ==========
    0b00000010

Почему она называется bit clear?Понимание этого делает его намного проще в использовании.Если вы посмотрите на таблицу истинности и подумаете о первом и втором входах.Когда второй, b, вход равен 1, выход равен 0. где второй вход b равен 0, выход сам по себе не модифицирован.Итак, что делает эта таблица истинности или операция, говорит, что где-нибудь b установлено в ноль или обнуляет эти биты в A. Поэтому, если у меня есть число 0x1234 и я хочу обнулить младшие 8 бит, я бы BIC это с 0x00FF.И твой следующий вопрос - почему бы не И что с 0xFF00?(проанализируйте таблицу истинности AND и посмотрите, что где b равно 1, вы сохраняете значение a как есть, а где b равно 0, вы обнуляете выход).ARM использует 32-битные регистры и фиксированный набор 32-битных команд, по крайней мере, традиционно.Непосредственные инструкции

mov r0,#0x12

В руке ограничены 8 ненулевыми битами, смещенными в любом месте номера, будут сдвигаться в битах.Поэтому, если бы я имел значение 0x12345678 и хотел обнулить младшие 8 бит, я мог бы сделать это

; assume r0 already has 0x12345678
bic r0,r0,#0xFF

или

; assume r0 already has 0x12345678
mov r1,#0xFF000000
orr r1,r1,#0x00FF0000
orr r1,r1,#0x0000FF00
;r1 now contains the value 0xFFFFFF00
and r0,r0,r1

или

; assume r0 already contains 0x12345678
ldr r1,my_byte_mask
and r0,r0,r1
my_byte_mask: .word 0xFFFFFF00

, чтоэто не так уж плохо, по сравнению с использованием Move и Two Orrs, но все же сжигает больше тактов, чем BIC-решение, потому что вы записываете дополнительный цикл памяти, читая my_byte_mask из оперативной памяти, что может занять некоторое время.

или

; assume r0 already contains 0x12345678
mvn r1,#0xFF
and r0,r0,r1

Последний не является плохим компромиссом.обратите внимание, что mvn в документации arm является побитовым, а не немедленным, что означает rx = NOT (немедленный).Непосредственное здесь - 0xFF.NOT (0xFF) означает инвертирование всех битов, это 32-битный регистр, к которому мы собираемся, так что 0xFFFFFF00 является результатом NOT (0xFF), и это то, что получает регистр r1 перед выполнением и.

Так вот почему BIC имеет место в наборе команд ARM, потому что иногда требуется меньше команд или тактов, чтобы замаскировать (mask = AND, чтобы сделать несколько битов нулями), используя команду BIC вместоИнструкция and.

Я использовал маску слова как концепцию, чтобы сделать биты в нуле, оставив остальные в покое.Можно считать, что orring образует биты в числе один, оставляя в покое другие, если вы посмотрите на таблицу истинности ИЛИ в любое время, когда b равно 1, тогда c равно 1. Таким образом, 0x12345678 ИЛИ 0x000000FF приводит к 0x123456FF битам во второмоперанд установлены.Да, это также верно, что каждый раз, когда в таблице истинности ИЛИ задается a, тогда устанавливается выходной сигнал, но большую часть времени, когда вы используете эти побитовые операции, у вас есть один операнд, с которым вы хотите что-то сделать, установить определенное количество битов.один без изменения остатка или установить нулевое значение определенного количества бит без изменения остатка, или вы хотите обнулить все биты, кроме определенного количества бит.При таком использовании у вас есть один операнд, с которым вы хотите работать, и вы создаете второй операнд, основываясь на том, каким вы хотите, чтобы общий эффект был, например, в C, если мы хотим сохранить только младший байт, который мы могли быиметь один входной параметр, один выходной параметр:

unsigned int keep_lower_byte ( unsigned int a )
{
    return(a&(~0xFF));
}

~ означает НЕ так ~ 0xFF, для 32-битных чисел означает 0xFFFFFF00, тогда & означает AND, поэтому мы возвращаем & 0xFFFFFF00.a был единственным реальным операндом, который появился, и мы изобрели второй, основанный на операции, которую мы хотели сделать ... В большинстве побитовых операций вы можете поменять местами операнды в инструкции, и все получится хорошо, такие инструкции, как ARM BIC, хотя операндыв определенном порядке, так же как вычитание, вы должны использовать правильный порядок операндов.

Сдвиг ... Есть два вида, логический и арифметический.Логическое проще всего и это то, что вы получаете, когда используете >> или << в C. </p>

Начните с 0x12, что равно 0b00010010.Смещение на три позиции влево (0x12 << 3) означает </p>

00010010 < our original number 0x12
0010010x < shift left one bit location
010010xx < shift left another bit location
10010xxx < shift left a third bit location

То, какие биты «сдвигаются» в пустые позиции, x'ы выше, зависит от операции.Для программирования на Си это всегда нули:

00010010 < our original number 0x12
00100100 < shift left one bit location
01001000 < shift left another bit location
10010000 < shift left a third bit location

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

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

mov r0,#0x12
mov r3,r0,lsl#3 ; shift the contents of r0 3 bits to the left and store in r3

Это изменение того, что вы сдвигаете, демонстрируется между lsr и asr, логическим сдвигом вправо и арифметическим сдвигом вправо (вы увидите, что нет asl, арифметического сдвига влево, потому что это не имеет смысла, некоторые ассемблеры позволят вам использоватьинструкция asl, но закодируйте ее как lsl).

ЛОГИЧЕСКИЙ сдвиг вправо:

00010010 - our original number 0x12
x0001001 - shifted right one bit
xx000100 - shifted right another bit
xxx00010 - shifted right another bit

Как и в случае с C, существует версия, которая сдвигается в нули, то есть логический сдвиг вправо,смещение в нули

00010010 - our original number 0x12
00001001 - shifted right one bit
00000100 - shifted right another bit
00000010 - shifted right another bit

ARITHMETIC сдвиг вправо означает сохранение «знакового бита», что такое знаковый бит?это входит в два дополнительных числа, которые вы также должны узнать, если у вас их нет.По сути, если вы рассматриваете комбинацию битов / значение как число, дополняющее два, то самый старший бит, слева, является знаковым битом.если это 0, то число положительное, а 1 - отрицательное.Возможно, вы заметили, что сдвиг влево на один бит - это то же самое, что умножение на 2, а сдвиг вправо - это то же самое, что и деление на 2. 0x12 >> 1 = 0x9, 18 >> 1 = 9, но что, если бы мы сдвинулись?минус 2 справа, минус два 0xFE с использованием байтов или 0b11111110.используя логический сдвиг в стиле C вправо 0xFE >> 1 = 0x7F или в десятичном формате -2 >> 1 = 0x127.К сожалению, мы не можем решить это в C за одну операцию, но при сборке мы можем использовать арифметическое смещение, предполагая, что в вашем наборе команд есть такое, которое рычаг делает

ARITHMETIC смещением вправо

s1100100 - our starting value s is the sign bit whatever that is 0 or 1
ss110010 - one shift right
sss11001 - another shift right
ssss1100 - another shift right

Итак, если при запуске мы использовали знаковый бит s, равный 0, если число было 01100100, то

01100100 - our starting value
00110010 - one shift right
00011001 - another shift right
00001100 - another shift right

, но если этот знаковый бит был равен единице

11100100 - our starting value
11110010 - one shift right
11111001 - another shift right
11111100 - another shift right

И мыможет решить смещение 0xFE вправо:

11111110 - 0xFE a minus 2 in twos complement for a byte
11111111 - shifted right one

, поэтому в псевдокоде 0xFE ASR 1 = 0xFF, -2 ASR 1 = -1.-2 делится на 2 = -1

Последнее, что вам нужно прочитать самостоятельно, связано с вращением и / или тем, что происходит с битом, сдвинутым с конца.сдвиг вправо, lsbit сдвигается «с конца» числа, как блоки, скользящие по столу, и тот, который падает, может просто попасть в «ведро с битами» (эфир, небеса или ад, одно из этих мест, где битыиди умирать, когда они исчезнут из этого мира).Но некоторые инструкции в некоторых наборах команд будут сдвигать этот бит и помещать его в флаг переноса (чтение при сложении и вычитании) не потому, что это обязательно перенос, а потому, что в alu и бите переноса есть биты состояния.это тот, который вроде имеет смысл.Итак, что такое вращение? Допустим, у вас есть 8-битный процессор, и вы повернули один бит, бит падает с торца в бит переноса, AND сдвиг бит на другой стороне - это то, что былов переносном бите перед операцией.В основном это музыкальные стулья, биты ходят вокруг стульев, один человек остался стоять, человек, стоящий - это ручная кладка, люди на стульях - биты в регистре.Почему это вообще полезно?Допустим, у нас был 8-битный процессор, например, Atmel AVR, но мы хотели сделать 64-битную смену.64 бита занимает 8, 8 бит, регистры, скажем, у меня есть 64-битное число в этих 8 регистрах, и я хочу сделать 64-битное смещение влево на один бит.Я бы начал с младшего байта и выполнил бы lsl, который сдвигает ноль, но сдвиг бита переходит в бит переноса.затем следующий наиболее значимый байт, который я делаю, вращается влево на один бит, входящий бит - это бит, выходящий из предыдущего байта, а выходящий бит переходит в бит переноса.Я повторяю инструкцию rol для других байтов, глядя на 16-битный сдвиг:

00100010 z0001000 - our original number
00100010 z 0001000 - lsl the least significant byte, the ms bit z is in carry
0100010z 00010000 - rotate left the most significant byte pulling the z bit from carry

00100010z0001000 - if it had been a 16 bit register
0100010z00010000 - a logical shift left on a 16 bit with a zero coming in on the left

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

3 голосов
/ 28 января 2012

Я сделаю первый, а затем, может быть, вы сможете попробовать и остальное, используя аналогичный подход:

/** LSL **/
mov r0, #1            ; r0 = 0000 0000 0000 0000 0000 0000 0000 0001
mov r3, r0, LSL#10    ; r3 = r0 logically shifted left by 10 bit positions 
                           = 0000 0000 0000 0000 0000 0100 0000 0000
                                                       ^           ^
                                                       +<<<<<<<<<<<+
                                                     shift left 10 bits

Обратите внимание, что, если вы еще не понимаете логические операции, такие как ИЛИ(|), И (&) и т. Д., Тогда вам будет трудно понять соответствующие инструкции ARM (ORR, AND и т. Д.).

...