Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Assembly (x86-64, AArch64, RISC-V)  Module 17 – 32-bit Arm: A32, Thumb-2, CPSR and IRQ/FIQ

A32 data processing and the barrel shifter

How A32 data processing shifts or rotates its second operand in the barrel shifter, what the S suffix changes, and the arithmetic, logical and multiply groups.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 8 of 25 minutes free
Free: The second operand is shifted on its way into the ALU · An immediate is eight bits and a rotation, not a 12-bit number · Four groups of instructions take Operand2, and S decides about the flags · Practise. With a pass: 3 in-depth parts and 3 interview answers, about 17 minutes.

What you will learn

  • Use the flexible second operand: shifted registers and rotated 8-bit immediates
  • Write arithmetic, logical, compare and move instructions, with and without the S suffix
  • Predict the N, Z, C and V flags after an S-suffixed instruction of any group
  • Load any 32-bit constant with MOV, MVN, MOVW and MOVT or a literal pool
  • Write multiply instructions and 64-bit additions for lab programs

Before you start

What you’ll learn · Before you start

What you will learn

  • Use the flexible second operand: shifted registers and rotated 8-bit immediates
  • Write arithmetic, logical, compare and move instructions, with and without the S suffix
  • Predict the N, Z, C and V flags after an S-suffixed instruction of any group
  • Load any 32-bit constant with MOV, MVN, MOVW and MOVT or a literal pool
  • Write multiply instructions and 64-bit additions for lab programs

Before you start

On this page

One A32 instruction multiplies a register by 9 with no multiplier: add r12, r2, r2, lsl #3 adds r2 to r2 shifted left three places, so 5 becomes 45. The barrel shifter in front of the ALU does that shift, on the second operand of A32 data processing instructions as they run.

Every number below comes from a Python model of the A32 instruction set that runs what llvm-mc 23.1.2 and ld.lld 21.0.0 built, flag by flag as Arm’s pseudocode specifies, counting instructions, not cycles. Bitwise operations and shifts explains the shifts themselves.

The second operand is shifted on its way into the ALU

In add r1, r2, r3, lsl #3, r2 goes straight to the ALU, while r3 first passes through the barrel shifter: The circuit in front of a 32-bit Arm ALU that shifts or rotates one operand by any amount in a single step, so an A32 instruction such as add r1, r2, r3, lsl #3 shifts r3 and adds in one go. and moves three places left. This last operand, Operand2 or the flexible second operand: Operand2, the last operand of an A32 data-processing instruction: a rotated 8-bit immediate, or a register that the barrel shifter can shift by a constant or by another register's bottom byte, or rotate with rrx., has five forms:

Operand2 Example The value the ALU gets
Immediate mov r0, #0xFF000000 a constant from the instruction
Register add r1, r2, r3 r3 as it is
Register, shift by a constant add r1, r2, r3, lsl #3 lsl or ror by 1 to 31, lsr or asr by 1 to 32
Register, shift by a register mov r10, r2, lsl r9 the amount from the bottom byte of r9
Register, rrx mov r11, r2, rrx one place right, the C flag into bit 31

Step through that add:

Interactive · step by step One add whose second operand is shifted on the way to the ALU Step through add r1, r2, r3, lsl #3 from shifts.s, with r2 = 5 and r3 = 3. The left column is the path of the first operand and the result; the right column is the shifter.
One add whose second operand is shifted on the way to the ALU, step 6 of 6. Showing add with lsl #3, r2, first operand (5), r3, second operand (3), barrel shifter (lsl r9, or rotate), ALU: add (5 + 24 = 29), r1, destination (0x0000001D). In focus: barrel shifter.add with lsl #3word 0xE0821183r2, first operand5r3, second operand3barrel shifterlsl r9, or rotateALU: add5 + 24 = 29r1, destination0x0000001D

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. One word names all three parts. The word 0xE0821183 holds the operation (add), the first operand r2, the destination r1 and the second operand: bit 25 is 0, so it is a register, r3, shifted left (type 00) by the 5-bit amount 3 in bits 11 to 7.
  2. Both registers are read. r2 holds 5 and goes straight to the first input of the ALU. r3 holds 3 and goes to the shifter; the first operand never passes through it.
  3. The shifter moves r3 left by 3. 3 shifted left by three places is 24 (0x18). The shift happens inside this instruction, on the way to the ALU, so it costs no extra instruction. r3 itself still holds 3 afterwards.
  4. The ALU adds the two inputs. The ALU gets 5 from r2 and 24 from the shifter and adds them: 29. Without the shifter, A32 would need a separate shift instruction and a spare register for the 24.
  5. r1 receives the result. r1 = 29, 0x0000001D, the value the model printed for r1. There is no S on add, so the flags are left as they were.
  6. Other forms, same path. With lsl r9 the amount comes from the bottom byte of r9 instead of the word, as in mov r10, r2, lsl r9 (r10 = 0x50). With an immediate, the shifter rotates the 8-bit field: mov r0, #0xFF000000 is 0xFF rotated right by 8.

shifts.s uses every form on r2 = 5 and r3 = 3:

shifts.s: ten results, each from a shifted or rotated operand S · shifts.s
@ shifts.s: one data-processing instruction per line, each with its second operand shifted on the way in.
@ r2 = 5 and r3 = 3 are the inputs; every other register holds one result.
        .syntax unified
        .text
        .global _start
_start: mov     r2, #5
        mov     r3, #3
        add     r12, r2, r2, lsl #3     @ 5 + (5 << 3): times 9 in one instruction
        add     r1, r2, r3, lsl #3      @ 5 + (3 << 3)
        rsb     r4, r2, r2, lsl #4      @ (5 << 4) - 5: times 15
        mov     r5, r2, ror #1          @ bit 0 of 5 comes back in at bit 31
        mvn     r6, #15                 @ NOT 15, which is -16
        mov     r7, r6, asr #2          @ -16 >> 2, copies of the sign bit fill the top
        mov     r8, r6, lsr #2          @ the same bits, zeros fill the top
        mov     r9, #4
        mov     r10, r2, lsl r9         @ the shift amount comes from a register
        mov     r11, r2, rrx            @ one place right, the C flag enters at bit 31
        mov     r0, #0xFF000000         @ an immediate: 0xFF rotated right by 8
stop:   b       stop
shifts.s on the A32 model, with the word each line became Python · shifts_session.py
"""Builds shifts.s, lists the words it assembled to, and runs it on the A32 model to read every result register."""
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import armlab

elf = armlab.build("shifts.s")                       # assemble with llvm-mc, link with ld.lld
armlab.sh(f"llvm-objdump -d --triple=armv7a {elf}")  # each instruction beside the 32-bit word it became
armlab.run(elf, regs=["r12", "r1", "r4", "r5",       # the results, four to a line
                      "r6", "r7", "r8", "r10",
                      "r11", "r0"])

Output

$ llvm-mc --triple=armv7a-none-eabi -filetype=obj -o shifts.o shifts.s
$ ld.lld -T sessions/a32.ld -o shifts.elf shifts.o
$ llvm-objdump -d --triple=armv7a shifts.elf

shifts.elf:	file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
       0: e3a02005     	mov	r2, #5
       4: e3a03003     	mov	r3, #3
       8: e082c182     	add	r12, r2, r2, lsl #3
       c: e0821183     	add	r1, r2, r3, lsl #3
      10: e0624202     	rsb	r4, r2, r2, lsl #4
      14: e1a050e2     	ror	r5, r2, #1
      18: e3e0600f     	mvn	r6, #15
      1c: e1a07146     	asr	r7, r6, #2
      20: e1a08126     	lsr	r8, r6, #2
      24: e3a09004     	mov	r9, #4
      28: e1a0a912     	lsl	r10, r2, r9
      2c: e1a0b062     	rrx	r11, r2
      30: e3a004ff     	mov	r0, #-16777216

00000034 <stop>:
      34: eafffffe     	b	0x34 <stop>             @ imm = #-0x8
Model run of shifts.elf: stopped at 0x00000034: b . (a branch to itself); 13 instructions
r12 = 0x0000002D   r1  = 0x0000001D   r4  = 0x0000004B   r5  = 0x80000002
r6  = 0xFFFFFFF0   r7  = 0xFFFFFFFC   r8  = 0x3FFFFFFC   r10 = 0x00000050
r11 = 0x00000002   r0  = 0xFF000000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shifts_session.py

r12 = 0x2D is 5 × 9 and r4 = 0x4B is 5 × 15, since rsb subtracts the other way round. Two places right, −16 became 0xFFFFFFFC with arithmetic shift: A right shift that fills the vacated top bits with copies of the sign bit, so a negative number stays negative; it divides by a power of two rounding towards minus infinity, so -7 shifted right by 1 is -4. More in “Bitwise logic, shifts and rotates” and 0x3FFFFFFC with lsr. ror #1 moved bit 0 of 5 to bit 31. The rotate right extended: RRX, a 32-bit Arm rotation by one place through the C flag: every bit moves one place right, the old C enters at bit 31, and bit 0 is the bit that leaves., rrx, put C into bit 31; C was 0 because the model starts the flags, UNKNOWN after reset, at 0. The listing spells a shifted mov by its shift, ror r5, r2, #1: one encoding, two names.

Checkpoint

Check: Operand2 and its shifted forms

  1. Question 1 of 2 r2 holds 7. Which one instruction puts 7 × 9 = 63 in r12?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: add r12, r2, r2, lsl #3

    9 = 8 + 1, so the shifter makes 8 × r2 and the ALU adds r2 to it in the same instruction. The recorded run computed 5 × 9 = 0x2D the same way.

  2. Question 2 of 2 r2 = 5 and r9 = 0x104. What does mov r10, r2, lsl r9 leave in r10? Answer in decimal.

    Type a number.

    A hint

    Only one byte of r9 gives the shift amount.

    Show the answer: Question 2 of 2

    Answer: 80

    A shift by a register uses bits 7 to 0 of that register, here 0x04, so 5 is shifted left by 4 places: 80, which is 0x50, the value the run printed with r9 = 4.

An immediate is eight bits and a rotation, not a 12-bit number

An A32 data-processing instruction keeps its constant in 12 bits, yet cannot hold every number up to 4095: the low 8 bits are a value, the top 4 a rotation, and the constant is the value rotated right by twice the rotation. mov r0, #0xFF000000 became 0xE3A004FF: field 0x4FF, 0xFF rotated right by 8. A rotated immediate: The A32 way of storing a constant in 12 bits of an instruction: an 8-bit value and a 4-bit field, the value rotated right by twice the field. 0xFF000000 is 0xFF rotated right by 8; 257 has no such form. holds any constant whose set bits fit in eight neighbouring bits from an even position, even across bit 31, as 0xF000000F does.

shifter.py is the same shifter in Python, with the immediate field decoded too. Change a value in either loop and run it:

The shifter and the immediate field, in Python you can change Python · explore/shifter.py
"""The A32 barrel shifter in a few lines of Python: the value each shift passes on, and the constant a 12-bit
immediate field stands for. Change the values in the two loops at the bottom and run it again."""

MASK = 0xFFFFFFFF


def shift(value, kind, amount, c=0):
    """The value the shifter passes on to the ALU; c is the C flag, which rrx brings in at bit 31."""
    value &= MASK
    if kind == "rrx":
        return (c << 31) | (value >> 1)
    if amount == 0:
        return value
    if kind == "lsl":
        return (value << amount) & MASK if amount < 32 else 0
    if kind == "lsr":
        return value >> amount if amount < 32 else 0
    if kind == "asr":
        signed = value - (1 << 32) if value >> 31 else value
        return (signed >> min(amount, 31)) & MASK
    amount %= 32                                    # ror: the bits that leave at the bottom re-enter at the top
    return ((value >> amount) | (value << (32 - amount))) & MASK


def expand_imm(imm12):
    """The constant an immediate field stands for: its low 8 bits rotated right by twice its top 4 bits."""
    return shift(imm12 & 0xFF, "ror", 2 * (imm12 >> 8))


for kind, amount in (("lsl", 3), ("lsr", 2), ("asr", 2), ("ror", 1), ("rrx", 1)):
    operation = kind if kind == "rrx" else f"{kind} #{amount}"   # rrx always moves one place
    for value in (5, 0xFFFFFFF0):
        print(f"{value:#010x} {operation} = {shift(value, kind, amount):#010x}")

for imm12 in (0x005, 0x4FF, 0x2FF, 0xF3F):
    print(f"field {imm12:#05x}: {imm12 & 0xFF:#04x} rotated right by {2 * (imm12 >> 8):2} = {expand_imm(imm12):#010x}")

Output

0x00000005 lsl #3 = 0x00000028
0xfffffff0 lsl #3 = 0xffffff80
0x00000005 lsr #2 = 0x00000001
0xfffffff0 lsr #2 = 0x3ffffffc
0x00000005 asr #2 = 0x00000001
0xfffffff0 asr #2 = 0xfffffffc
0x00000005 ror #1 = 0x80000002
0xfffffff0 ror #1 = 0x7ffffff8
0x00000005 rrx = 0x00000002
0xfffffff0 rrx = 0x7ffffff8
field 0x005: 0x05 rotated right by  0 = 0x00000005
field 0x4ff: 0xff rotated right by  8 = 0xff000000
field 0x2ff: 0xff rotated right by  4 = 0xf000000f
field 0xf3f: 0x3f rotated right by 30 = 0x000000fc

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shifter.py

For 5 it repeats the model: 0x80000002 after ror #1, 2 after rrx. Field 0xF3F, rotation 15, turns 30 places right, the same as 2 places left: 0x3F becomes 0xFC.

Checkpoint

Check: An 8-bit value and a rotation

  1. Question 1 of 2 An immediate field holds 0x2FF. Which constant does it stand for?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 0xF000000F

    The constant is the 8-bit value rotated right by twice the rotation: 0xFF rotated right by 4 is 0xF000000F, the line the Python program printed for field 0x2ff.

  2. Question 2 of 2 Why can 0x1FE not be a rotated immediate, although its set bits fit in eight neighbouring bits?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Its window starts at bit 1, and the rotation only moves the value by an even number of places

    The rotation field counts in steps of two places, so an 8-bit window can start only at bit 0, 2, 4 and so on, wrapping round at bit 31. The window of 0x1FE starts at bit 1.

Four groups of instructions take Operand2, and S decides about the flags

A32 data processing comes in four groups, and each of their instructions takes Operand2 as its last operand:

Group Instructions What they compute
Arithmetic add, adc, sub, sbc, rsb, rsc sums and differences, adc, sbc and rsc with the carry
Logical and, orr, eor, bic bit by bit; bic clears the bits set in Operand2
Compare cmp, cmn, tst, teq flags from a sub, add, and or eor, no register written
Move mov, mvn Operand2, or its bitwise NOT

Multiplies are a family of their own: mul r10, r0, r1 keeps the low 32 bits, and its operands never pass through the shifter. groups.s runs one instruction per group on 240 and 60:

groups.s: one instruction from each group, on 240 and 60 S · groups.s
@ groups.s: one instruction from each group of A32 data processing, on the same two inputs 240 and 60.
@ The last three lines show what the S suffix changes: only cmp and subs write the flags.
        .syntax unified
        .text
        .global _start
_start: mov     r0, #240
        mov     r1, #60
        add     r2, r0, r1              @ arithmetic: r0 + r1
        sub     r3, r0, r1              @ r0 - r1
        rsb     r4, r0, r1              @ reverse subtract: r1 - r0
        and     r5, r0, r1              @ logical: bits set in both
        orr     r6, r0, r1              @ bits set in either
        eor     r7, r0, r1              @ bits set in one only
        bic     r8, r0, r1              @ r0 with r1's bits cleared
        mvn     r9, r0                  @ move: NOT r0
        mul     r10, r0, r1             @ multiply: the low 32 bits of r0 * r1
        cmp     r0, r1                  @ compare: r0 - r1 for the flags, no register written
        sub     r11, r1, r1             @ no S: the result is 0, the flags stay as cmp left them
        subs    r11, r1, r1             @ S: the same result, and now the flags describe it
stop:   b       stop
groups.s on the A32 model, with the flags after each line Python · groups_session.py
"""Runs groups.s on the A32 model and prints, after every instruction, the register it wrote and the N, Z, C, V flags."""
import pathlib
import sys

sys.path.insert(0, str(pathlib.Path(__file__).parent / "sessions"))
import armlab

elf = armlab.build("groups.s")
text = armlab.listing(elf)                      # address -> instruction, as llvm-objdump prints it
COMPARES = ("cmp", "cmn", "tst", "teq")         # these write the flags and no register


def watch(machine):
    def step(m, address, word, executed):
        line = text[address]
        if line.startswith("b "):               # the final b . that ends the run
            return
        flags = "".join(str(m.flag(f)) for f in "NZCV")
        if line.startswith(COMPARES):
            wrote = "flags only"
        else:
            rd = line.split()[1].rstrip(",")    # the first operand is the destination
            wrote = f"{rd} = {armlab.hex32(m.reg(int(rd[1:])))}"
        print(f"  {line:<20} {wrote:<17} NZCV = {flags}")
    machine.on_step = step


armlab.run(elf, setup=watch)

Output

$ llvm-mc --triple=armv7a-none-eabi -filetype=obj -o groups.o groups.s
$ ld.lld -T sessions/a32.ld -o groups.elf groups.o
  mov r0, #240         r0 = 0x000000F0   NZCV = 0000
  mov r1, #60          r1 = 0x0000003C   NZCV = 0000
  add r2, r0, r1       r2 = 0x0000012C   NZCV = 0000
  sub r3, r0, r1       r3 = 0x000000B4   NZCV = 0000
  rsb r4, r0, r1       r4 = 0xFFFFFF4C   NZCV = 0000
  and r5, r0, r1       r5 = 0x00000030   NZCV = 0000
  orr r6, r0, r1       r6 = 0x000000FC   NZCV = 0000
  eor r7, r0, r1       r7 = 0x000000CC   NZCV = 0000
  bic r8, r0, r1       r8 = 0x000000C0   NZCV = 0000
  mvn r9, r0           r9 = 0xFFFFFF0F   NZCV = 0000
  mul r10, r0, r1      r10 = 0x00003840  NZCV = 0000
  cmp r0, r1           flags only        NZCV = 0010
  sub r11, r1, r1      r11 = 0x00000000  NZCV = 0010
  subs r11, r1, r1     r11 = 0x00000000  NZCV = 0110
Model run of groups.elf: stopped at 0x00000038: b . (a branch to itself); 14 instructions

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 groups_session.py

Only two lines changed the flags: without S an instruction leaves N, Z, C and V alone, so sub r11, r1, r1 kept the compare’s 0010. subs wrote the same 0 and set Z = 1 and C = 1, no borrow: 0110. A compare is an S instruction that keeps only the flags.

Three parts follow. The first finds where C comes from after each kind of S instruction, and the 64-bit addition this breaks. The second loads other constants. movw loads 16 bits and movt the top half. The pseudo-instruction: A name an assembler accepts where an instruction goes although the instruction set has no instruction of that name: RISC-V li and la become one or more real instructions. The NASM manual also uses the word for its data lines, such as db, equ and times. More in “Assembler syntax: NASM, GNU as, Intel and AT&T” ldr rX, =value lets the assembler choose, using a Words of data that the assembler puts after the code, within reach of a PC-relative ldr, to hold constants that no single instruction can build. when it must, and adr makes an address from the pc. The third covers mla, the 64-bit long multiplies and 64-bit sums.

Checkpoint

Check: Instruction groups and the S suffix

  1. Question 1 of 2 cmp r0, r1 has just left NZCV = 0010. Which of these instructions, run next, changes the flags?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: subs r11, r1, r1

    Only an instruction with the S suffix, or a compare, writes the flags. In groups.s the sub kept 0010 and the subs, with the same result 0, changed it to 0110.

  2. Question 2 of 2 r0 = 240 (0xF0) and r1 = 60 (0x3C). What does bic r8, r0, r1 leave in r8? Answer in decimal.

    Type a number.

    A hint

    bic keeps the bits of r0 that are clear in r1.

    Show the answer: Question 2 of 2

    Answer: 192

    bic clears in r0 every bit that is set in r1: 0xF0 AND NOT 0x3C = 0xC0, which is 192, the value the run printed for r8.

Practise

Work each one out on paper from the rules and the runs above; with LLVM installed (toolchain setup), llvm-mc shows whether a changed line assembles:

  1. In shifts.s, make the first add use lsl #2 and work out r12.
  2. Give r4 the value 5 × 31 with a single rsb.
  3. In groups.s, swap the operands of the cmp: which flags does it leave?

The exercise turns the rule for immediates into code.

Exercise · Medium · Python

Encode a constant as an A32 rotated immediate, or with MVN

An A32 data-processing instruction stores a constant as 12 bits: an 8-bit value imm8 and a 4-bit rotation. The constant is imm8 rotated right by 2 * rotation places, within 32 bits. Write two Python functions that do what the assembler does with a constant:

  1. encode(value) takes a whole number from 0 to 0xFFFFFFFF and returns the tuple (imm8, rotation) that stands for it, or None when no 8-bit value rotated right by an even amount gives it. Some constants have more than one encoding (0x40000000 is 0x01 rotated right by 2, and also 0x04 rotated right by 4): return the one with the smallest rotation, which is the one llvm-mc chose in every run of this lesson.
  2. mov_or_mvn(value) returns ("mov", imm8, rotation) when encode(value) succeeds, otherwise ("mvn", imm8, rotation) when the bitwise NOT of value (within 32 bits) can be encoded, because mvn writes the NOT of its immediate, and None when neither works.

So encode(0xFF000000) is (0xFF, 4), the field 0x4FF of the recorded mov r0, #0xFF000000, and mov_or_mvn(0xFFFFFFF0) is ("mvn", 15, 0), the mvn r6, #15 that made −16 in shifts.s.

The sample tests use constants from the lesson's runs and listings, plus a few edges. Plain arithmetic is enough: no model, no assembler.

Starter code · immediates.py

def encode(value):
    """(imm8, rotation) with value == imm8 rotated right by 2 * rotation, the smallest rotation; None if none fits."""
    return None  # replace with your code


def mov_or_mvn(value):
    """("mov", imm8, rotation) or ("mvn", imm8, rotation) for one instruction that loads value; None if neither fits."""
    return None  # replace with your code
The sample tests · test_immediates.py
"""Sample tests: constants from the lesson's runs and listings, plus a few edges."""

from immediates import encode, mov_or_mvn


def test_small_values_need_no_rotation():
    assert encode(5) == (5, 0)
    assert encode(0) == (0, 0)
    assert encode(0xFF) == (0xFF, 0)


def test_recorded_mov_of_0xff000000():
    assert encode(0xFF000000) == (0xFF, 4)


def test_values_eight_and_sixteen_places_up():
    assert encode(0x5A00) == (0x5A, 12)
    assert encode(0xFF00) == (0xFF, 12)


def test_a_value_that_wraps_round_bit_31():
    assert encode(0xF000000F) == (0xFF, 2)


def test_rotation_15_shifts_left_by_two():
    assert encode(0x3FC) == (0xFF, 15)
    assert encode(0x104) == (0x41, 15)


def test_the_smallest_rotation_wins():
    assert encode(0x10) == (0x10, 0)
    assert encode(0x40000000) == (0x01, 1)


def test_constants_no_rotated_immediate_holds():
    assert encode(0x101) is None          # bits 0 and 8, nine places apart
    assert encode(0x1004) is None         # bits 12 and 2
    assert encode(0x12345678) is None
    assert encode(0x1FE) is None          # eight bits wide, but it would need an odd rotation


def test_mov_when_the_value_fits():
    assert mov_or_mvn(0xFF000000) == ("mov", 0xFF, 4)


def test_mvn_when_the_complement_fits():
    assert mov_or_mvn(0xFFFFFFF0) == ("mvn", 15, 0)       # mvn r6, #15 in shifts.s
    assert mov_or_mvn(0xFFFFFF00) == ("mvn", 0xFF, 0)
    assert mov_or_mvn(0x7FFFFFFF) == ("mvn", 0x02, 1)     # NOT 0x80000000
    assert mov_or_mvn(0xFFFFFFFF) == ("mvn", 0, 0)


def test_neither_form():
    assert mov_or_mvn(0x101) is None
    assert mov_or_mvn(0x12345678) is None
A hint

Try every rotation from 0 to 15 in order. Undo the rotation: rotate value LEFT by 2 * rotation places within 32 bits (rotating right by 32 - 2 * rotation is the same thing), and if the result is below 256 you have imm8. The first rotation that works is the smallest. For mov_or_mvn, try value first, then ~value & 0xFFFFFFFF.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

The questions start from the section your checkpoint answers left weakest.

Practice that adapts to you

Practise this lesson

9 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 Which of these is not a form of Operand2 in A32?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: A memory operand such as [r1]

    Operand2 is an immediate, a register, a register shifted by a constant or by a register, or a register with rrx. A32 is a load-store architecture, so no data-processing operand is ever in memory.

  2. Question 2 of 9 r6 holds 0xFFFFFF00, which is −256. What do mov r7, r6, asr #4 and mov r8, r6, lsr #4 leave?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: r7 = 0xFFFFFFF0 and r8 = 0x0FFFFFF0

    As in the recorded run, where −16 became 0xFFFFFFFC with asr #2 and 0x3FFFFFFC with lsr #2: an arithmetic shift keeps the sign, a logical one fills with zeros.

  3. Question 3 of 9 Which single instruction puts r0 × 17 in r1?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: add r1, r0, r0, lsl #4

    A multiplier of the form 2ᵏ + 1 is one add with lsl #k, and 2ᵏ − 1 is one rsb, as r12 = 5 × 9 and r4 = 5 × 15 showed in shifts.s.

  4. Question 4 of 9 How does an A32 data-processing instruction store its 12-bit immediate field?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: As an 8-bit value and a 4-bit rotation; the constant is the value rotated right by twice the rotation

    Twelve bits are not enough for large constants as plain numbers, so A32 spends 4 of them on a rotation: 256 values in 16 positions instead of 4,096 small numbers.

  5. Question 5 of 9 Which 12-bit immediate field stands for the constant 0x00003FC0?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: 0xDFF

    0x3FC0 is 0xFF shifted left by 6. A rotation right by 26 does the same within 32 bits, and 26 = 2 × 13, so the field is 13 for the rotation and 0xFF for the value: 0xDFF.

  6. Question 6 of 9 Which one instruction loads 0xFFFFFF0F into r9?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: mvn r9, #0xF0

    groups.s computed the same value as mvn r9, r0 with r0 = 0xF0. A constant whose NOT is a rotated byte is one mvn away, just as shifts.s made −16 with mvn r6, #15.

  7. Question 7 of 9 What does cmp r0, r1 write?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: Only the flags, from r0 − r1

    cmp, cmn, tst and teq exist only for their flags, which is why the run listed cmp r0, r1 as flags only.

  8. Question 8 of 9 With r0 = 240 and r1 = 60, which NZCV does cmp r1, r0 leave?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: 1000

    A compare sets N from the sign of the difference, Z when it is 0, C = 1 when no borrow was needed and V on signed overflow. Swapping the operands turns 0010 into 1000.

  9. Question 9 of 9 Which one instruction computes 100 − r2 into r3?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: rsb r3, r2, #100

    Operand2 is always the last operand, so a constant can only be subtracted from by reversing the subtraction: that is what rsb is for, and rsb r4, r0, r1 in groups.s gave 60 − 240 = 0xFFFFFF4C.

Where C comes from after an S instruction, and a carry chain it breaks Pro: opens with a Pro, Premium or Ultimate pass

Every S instruction sets N and Z from its result, but C and V depend on the kind of instruction. This part runs each kind on the model, gathers the rules in one table and reproduces a 64-bit addition that they break.

In this part:

  • Where each kind of S instruction takes its carry, run line by line
  • A 64-bit addition broken by one flag-setting instruction, reproduced

2 runnable examples (Python, carry.s, carry_chain.s) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Any 32-bit constant: four ways to load it, and what each one costs Pro: opens with a Pro, Premium or Ultimate pass

One rotated byte covers few of the constants a program needs, so A32 code has three more ways to load a value. This part records what LLVM’s assembler did with four constants on two architectures and counts the instructions and bytes each way costs.

In this part:

  • What the assembler does with 257 on ARMv5TE and on Armv7-A
  • What the ldr pseudo-instruction chose on each architecture, in bytes
  • Two halves with movw and movt, and an address adr cannot reach

2 runnable examples (Python, adr_far.s, adr_fix.s, constants.s, mov_257.s, movw_movt.s) · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Multiplies, multiplying by constants and 64-bit sums, with lab numbers Pro: opens with a Pro, Premium or Ultimate pass

A32 has multiply instructions of its own, yet compilers still avoid them for many constants. This part runs the multiplies on the values lab programs use, reads what clang made of seven constant multipliers, and adds and subtracts two 64-bit numbers in two instructions each.

In this part:

  • Three multiplies and two long ones, run on 0xBEEF, 0x1234 and 0xFFFFFFFF
  • Multiplying by 5, 7, 9 and 15 without a multiplier
  • A 64-bit sum and difference with ADDS and ADC, SUBS and SBC

3 runnable examples (Python, S, add64.s, mulk.c) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

What does the barrel shifter add to an A32 data-processing instruction, and what does it cost?

It shifts or rotates the second operand before the ALU uses it, in the same instruction: add r1, r2, r3, lsl #3 adds r3 × 8 to r2. The amount is a constant or a register’s bottom byte, rrx rotates through C, and an immediate is a byte rotated right by an even amount. The shifted register keeps its value. No extra instruction is needed; the cost is encoding space, which is why immediates are only rotated bytes.

Three harder ones follow: a 64-bit counter that loses counts, constants for two cores, and multiplying with no mul.

  1. Mid to senior

    A hand-written A32 routine keeps a 64-bit event counter in two registers and adds each batch's count to it. A colleague later added a flag-setting status test between the two instructions of the addition. Now the counter sometimes comes out exactly 2^32 short, but only for some status masks. How do you find the cause and fix it?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    Firmware must build for an older ARMv5TE core and for an Armv7-A core from the same assembly source. It loads masks, small counts and a few 32-bit configuration words. How do you write those loads, what does each choice cost on each core, and what can go wrong?

    Model answer, Pro. First step and follow-ups

  3. Fresher to senior

    A lab asks for x times 5, 7, 9, 15, 10 and 100 on a 32-bit Arm core without using MUL wherever one or two instructions can do it. Which instructions do you write, when is MUL still the better choice, and how do you check the results?

    Model answer, Pro. First step and follow-ups

Flashcards · 5 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What does the barrel shifter do in an A32 data-processing instruction?

    It shifts or rotates the second operand on its way into the ALU, within the same instruction: add r1, r2, r3, lsl 3 adds r3 times 8 to r2, so one add can multiply by 9. The shifted register keeps its value.

  2. What can Operand2 of an A32 data-processing instruction be?

    A rotated immediate, a register, or a register shifted by a constant, shifted by the bottom byte of another register, or rotated one place through C with rrx.

  3. How does A32 fit a constant into 12 bits of an instruction?

    As an 8-bit value rotated right by twice a 4-bit field, so 0xFF000000 is 0xFF rotated right by 8. A value such as 257 has no such form.

  4. What does the S suffix change in an A32 instruction, and what is a compare?

    Without S the instruction leaves the flags alone; with S it also sets N and Z from its result. A compare is an S instruction that keeps only the flags and discards the result.

  5. What does rrx do?

    Rotates a register right by one place through the C flag: each bit moves one place right, the old C enters at bit 31, and the bit that leaves from bit 0 is the new carry candidate.

Key takeaways

  • The barrel shifter shifts or rotates the second operand of A32 data processing instructions on the way into the ALU, so one add multiplies by 9.
  • Operand2 is a rotated immediate, a register, or a register shifted by a constant, by another register’s bottom byte, or by rrx.
  • An immediate is an 8-bit value rotated right by twice a 4-bit field, so the field 0x4FF stands for 0xFF000000.
  • Without S an instruction leaves the flags alone; with S it sets N and Z from its result, and a compare is an S instruction that keeps only the flags.

Terms in this lesson

barrel shifter
The circuit in front of a 32-bit Arm ALU that shifts or rotates one operand by any amount in a single step, so an A32 instruction such as add r1, r2, r3, lsl #3 shifts r3 and adds in one go.
flexible second operand
Operand2, the last operand of an A32 data-processing instruction: a rotated 8-bit immediate, or a register that the barrel shifter can shift by a constant or by another register's bottom byte, or rotate with rrx.
long multiply
A multiply that keeps the whole 64-bit product of two 32-bit registers, its high word in one register and its low word in another, so that no bit of the product is lost, unlike a plain multiply that keeps only the low 32 bits.
multiply-accumulate
One instruction that multiplies two registers and adds a third register to the product, such as A32 mla. Loops that sum products, as in filters and dot products, use it.
rotate right extended
RRX, a 32-bit Arm rotation by one place through the C flag: every bit moves one place right, the old C enters at bit 31, and bit 0 is the bit that leaves.
rotated immediate
The A32 way of storing a constant in 12 bits of an instruction: an 8-bit value and a 4-bit field, the value rotated right by twice the field. 0xFF000000 is 0xFF rotated right by 8; 257 has no such form.
shifter carry-out
The last bit a shift or rotation moves out of a register, which the 32-bit Arm barrel shifter passes on as a carry alongside the shifted value.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.