← Back to Articles

Shift Operations

What are Shift Operations?

Shift operations move the bits in a register left or right by a specified number of positions. They're extremely useful for efficient multiplication, division, and bit manipulation. Our compiler supports two types of shifts:

  • LSL (Logical Shift Left): Shifts bits to the left
  • LSR (Logical Shift Right): Shifts bits to the right

Logical Shift Left (LSL)

LSL shifts bits to the left, filling the right side with zeros. The syntax is:

LSL Rd, Rn, <shift_amount>

How it Works

When you shift left by 1 position, each bit moves one position to the left, and a 0 enters from the right. The leftmost bit is discarded.

Example: Basic Left Shift

MOV R0, #5      // Binary: 0101
LSL R1, R0, #1  // Shift left by 1
// R1 = 10 (Binary: 1010)

Notice that shifting left by 1 is equivalent to multiplying by 2!

LSL for Fast Multiplication

Shifting left by N positions multiplies by 2^N. This is much faster than using MUL:

MOV R0, #3
LSL R1, R0, #2  // Shift left by 2 = multiply by 4
// R1 = 12 (3 × 4)

MOV R2, #5
LSL R3, R2, #3  // Shift left by 3 = multiply by 8
// R3 = 40 (5 × 8)

Powers of Two Table

  • LSL by 1 → multiply by 2
  • LSL by 2 → multiply by 4
  • LSL by 3 → multiply by 8
  • LSL by 4 → multiply by 16
  • LSL by 5 → multiply by 32

Logical Shift Right (LSR)

LSR shifts bits to the right, filling the left side with zeros. The syntax is:

LSR Rd, Rn, <shift_amount>

Example: Basic Right Shift

MOV R0, #20     // Binary: 10100
LSR R1, R0, #1  // Shift right by 1
// R1 = 10 (Binary: 01010)

Shifting right by 1 divides by 2 (integer division)!

LSR for Fast Division

Shifting right by N positions divides by 2^N (integer division):

MOV R0, #32
LSR R1, R0, #2  // Shift right by 2 = divide by 4
// R1 = 8 (32 ÷ 4)

MOV R2, #100
LSR R3, R2, #3  // Shift right by 3 = divide by 8
// R3 = 12 (100 ÷ 8 = 12.5, truncated)

Note: LSR performs integer division, discarding any remainder.

Shift with Immediate or Register

The shift amount can be a constant or from a register:

Immediate Shift Amount

MOV R0, #10
LSL R1, R0, #2  // Shift by constant 2
// R1 = 40

Register Shift Amount

MOV R0, #10     // Value to shift
MOV R1, #3      // Shift amount
LSL R2, R0, R1  // Shift R0 left by R1
// R2 = 80 (10 × 2^3)

Practical Applications

Example 1: Calculate Array Index

// Calculate memory address: base + (index * 4)
MOV R0, #100    // Base address
MOV R1, #5      // Index
LSL R2, R1, #2  // index * 4 (assuming 4-byte elements)
ADD R3, R0, R2  // Final address = 100 + 20 = 120

Example 2: Extract Bits

// Get upper 4 bits of an 8-bit value
MOV R0, #0xF5   // Binary: 11110101
LSR R1, R0, #4  // Shift right by 4
// R1 = 0xF (Binary: 00001111)

Example 3: Fast Power of 2 Check

// Check if number is power of 2
// Power of 2 has only one bit set
MOV R0, #8      // Test value
SUB R1, R0, #1  // R1 = 7
AND R2, R0, R1  // R2 = 0 if power of 2

Combining Shifts with Other Operations

Shifts are often combined with arithmetic and bitwise operations for complex calculations:

// Calculate (x * 10) efficiently as (x * 8) + (x * 2)
MOV R0, #7      // x = 7
LSL R1, R0, #3  // R1 = x * 8 = 56
LSL R2, R0, #1  // R2 = x * 2 = 14
ADD R3, R1, R2  // R3 = 56 + 14 = 70

Performance Tips

  • Use shifts instead of MUL/DIV when working with powers of 2
  • Shifts execute faster than multiplication and division
  • Combine multiple shifts and adds for complex multiplications
  • Remember: LSL/LSR are deterministic - no flags affected

Next Steps

Now that you understand shift operations, you can write more efficient assembly programs. Continue to learn about program structure and best practices to organize your code effectively.