Code in ARM Assembly: Bit operations

In the previous article I looked at the rich families of instructions to perform arithmetic on integer values in the general-purpose registers. This article completes most of this survey of instructions by considering those which operate with bits and other representations of data.

Move as bits

In addition to the MOV instruction to move the contents of a register to another register, there are three variants which perform bit operations as part of the move:

  • MOVK – move with keep, which inserts the first operand (a 16-bit immediate value) into an X or W register without changing the bits outside it,
  • MOVN – move with NOT, which performs a one’s complement (bitwise NOT) on the first operand (a 16-bit immediate value) as it’s moved,
  • MOVZ – move with zero, which inserts the first operand (a 16-bit immediate value) into an X or W register, zeroing the other bits.

Some examples explain these better:
MOVK W0, #0x1234, LSL #16
moves the halfword 0x1234 which has been left-shifted (see below) 16 places to form the most significant bytes in the register W0.

Because of the limitation on size of immediate values, you can load a 64-bit immediate using a sequence of one MOV followed by three MOVKs:
MOV X0, #0xDEF0 // this is assembled as MOVZ to zero the other bits in the register
MOVK X0, #0x9ABC, LSL #16
MOVK X0, #0x5678, LSL #32
MOVK X0, #0x1234, LSL #48

which sets X0 to 0x123456789ABCDEF0.

Shifting bits

There are four ways in which the bits in a register can be shifted and rotated: LSL (logical shift left), LSR (logical shift right), ASR (arithmetic shift right) and ROR (rotate right).

LSL is an alias for LSLV, which shift bits to the left by moving in zero bits at the right:
LSL W0, W1, #1
shifts the contents of W1 one place to the left and puts the result in W0. The shift can be in a register, and for W registers can be 0-31, for X registers 0-63 in places.

For example, if W1 contained 0x0000 00F0 = 1111 0000, then shifting it left by one place converts it to 1 1110 0000 = 0x0000 01E0. Logical shift right moves the bits in the other direction.

If the integer being shifted is signed and negative, then LSR unfortunately changes its sign. To accommodate that, when shifting signed values to the right, you should use ASR, which is an alias of ASRV. This shifts in copies of the sign bit of the number rather than zeros. LSL and ASR are handy quick methods of multiplying and dividing integers by powers of 2.

LSL, LSR and their relatives all move in zeros. The other option is to move in the bits that are shifted out, which is performed by ROR, and its relative RORV.

Bitwise operations

The most common operations are:

  • AND performs a bitwise AND, and has a sibling ANDS which does the same and sets NZCV flags;
  • BIC and BICS perform a bitwise AND with the complement of a register value, which can be shifted;
  • ORR performs a bitwise (inclusive) OR of a register value; ORN performs a bitwise (inclusive) OR of a register value and the complement of a register value;
  • EOR performs a bitwise exclusive OR (XOR) of a register value; EON performs a bitwise exclusive OR (XOR) of the complement of a register value;
  • MVN performs a bitwise inverse of a register value.

Miscellaneous operations you should be aware of, which are detailed in the instruction set reference, include:

  • BFC, BFI, BFM, BFXIL – these clear, insert and move bitfields within registers;
  • REV reverses the byte order in a register, and has relatives which work within sub-units: REV16 for each halfword, and REV32 for each word.

As promised, here’s a cheat sheet summary of the main operations using these registers:


and a tear-out PDF is here: armgpinstructions1

The next article will tackle the remaining topic involving the general-purpose registers, that of conditional operations, and their use to avoid branching. After that, I’ll move on to floating point.

Previous articles in this series:

1: Building an app to develop assembly routines, including an explanation of calling assembly language from Swift, with a complete Xcode project
2: Registers explained
3: Working with pointers
4: Controlling flow
5: Conditional loops
6: Flow, pipelines and performance
7: Moving data around
8: Integer arithmetic


ARM register summary
ARM operand architecture
Conditions and conditional branching instructions
Control Flow
ARM instructions for GP registers
AsmAttic 2, a complete Xcode project (version 2)
AsmAttic, a complete Xcode project (version 1)


Procedure Call Standard for the Arm 64-bit Architecture (ARM) from Github
Writing ARM64 Code for Apple Platforms (Apple)
Stephen Smith (2020) Programming with 64-Bit ARM Assembly Language, Apress, ISBN 978 1 4842 5880 4.
Daniel Kusswurm (2020) Modern Arm Assembly Language Programming, Apress, ISBN 978 1 4842 6266 5.
ARM64 Instruction Set Reference (ARM).