Lesson 23 of 48 intermediate

Logical, Shift, and Rotate Instructions

AND, OR, XOR, NOT, TEST, CMP plus shifts and rotates — bit manipulation mastery

Open interactive version (quiz + challenge)

Real-world analogy

Logical instructions are like filters and masks for bits. AND is like a stencil — only the bits that match both patterns survive. OR is like mixing two colors — any bit that is 1 in either input stays 1. XOR is like a secret code toggle — applying it twice gets you back to the original. Shifts are like a conveyor belt pushing bits left or right, while rotates are a circular carousel where bits wrap around.

What is it?

Logical instructions (AND, OR, XOR, NOT, TEST) manipulate individual bits for masking, setting, toggling, and testing. CMP performs non-destructive subtraction for comparisons. Shift instructions (SHL, SHR, SAR) multiply or divide by powers of 2. Rotate instructions (ROL, ROR, RCL, RCR) circularly shift bits, with RCL/RCR including the carry flag for multi-word operations. These instructions are essential for bit-level data processing and fast arithmetic.

Real-world relevance

Bitwise operations are everywhere in systems programming. AND masks are used in network subnet calculations (IP AND mask). XOR is the basis of many encryption algorithms and checksums (CRC). Shift-and-add is how hardware multipliers work. Device driver code constantly uses TEST to check status register bits. Modern compilers use shifts and adds instead of MUL for multiplication by constants (e.g., x*10 = x*8 + x*2) because shifts are faster.

Key points

Code example

; Logical, shift, and rotate showcase

; === Bit Masking with AND ===
; Extract bits 4-7 from AL (upper nibble)
MOV AL, 0B7h       ; AL = 10110111b
AND AL, 0F0h       ; AL = 10110000b (B0h)
; To get the value 0-15, shift right:
MOV CL, 4
SHR AL, CL         ; AL = 00001011b (0Bh = 11)

; === Setting bits with OR ===
; Set bit 5 to enable a feature flag
MOV AL, [status]   ; Read current status byte
OR AL, 20h         ; Set bit 5 (00100000b)
MOV [status], AL   ; Write back

; === Toggle with XOR ===
; Toggle an LED connected to bit 0
MOV AL, [port_data]
XOR AL, 01h        ; Flip bit 0
MOV [port_data], AL

; === Fast zero check ===
TEST AX, AX        ; Sets ZF without changing AX
JZ handle_zero     ; Branch if AX is zero

; === Multiply by 12 using shifts ===
; AX × 12 = AX × 8 + AX × 4
MOV BX, AX         ; BX = AX (original)
SHL AX, 1          ; AX = AX × 2
SHL AX, 1          ; AX = AX × 4
MOV CX, AX         ; CX = AX × 4
SHL AX, 1          ; AX = AX × 8
ADD AX, CX         ; AX = AX×8 + AX×4 = AX × 12

; === 32-bit shift left using RCL ===
; Shift DX:AX left by 1 bit
SHL AX, 1          ; Shift low word, bit 15 -> CF
RCL DX, 1          ; CF enters bit 0 of DX

; === Rotate to examine each bit ===
MOV CX, 8          ; Check all 8 bits
MOV BL, AL         ; BL = value to examine
check_bit:
    ROL BL, 1      ; Rotate left, MSB -> CF -> bit 0
    JC bit_is_one  ; CF=1 means that bit was 1
    ; bit is 0...
bit_is_one:
    DEC CX
    JNZ check_bit

Line-by-line walkthrough

  1. 1. AND AL, 0F0h with AL=B7h — the mask 0F0h has upper nibble all 1s and lower nibble all 0s. AND preserves only the upper nibble: B7h AND F0h = B0h.
  2. 2. SHR AL, CL with CL=4 shifts the upper nibble down to the lower position: B0h >> 4 = 0Bh (11 decimal). This is how you extract a bit field.
  3. 3. OR AL, 20h sets bit 5 regardless of its current value. The mask 20h = 00100000b has only bit 5 set, so OR forces that bit to 1 and leaves all others unchanged.
  4. 4. XOR AL, 01h toggles bit 0: if it was 0, it becomes 1; if 1, it becomes 0. Applying XOR 01h again restores the original value — XOR is its own inverse.
  5. 5. TEST AX, AX performs AX AND AX = AX but does not store the result. It only sets flags. ZF=1 if AX is zero. This is a common pattern for null pointer checks.
  6. 6. The multiply-by-12 sequence: SHL×1 gives ×2, SHL×2 gives ×4, SHL×3 gives ×8. Then AX×8 + saved AX×4 = AX×12. Shifts are much faster than the MUL instruction.
  7. 7. SHL AX, 1 / RCL DX, 1 — the SHL pushes the MSB of AX into CF. Then RCL shifts DX left and pulls CF into its bit 0. This propagates the carry from the low word to the high word.
  8. 8. The bit-checking loop uses ROL to rotate each bit into the CF position, then JC tests it. After 8 rotations, BL is back to its original value.

Spot the bug

; Goal: Test if bit 3 of AL is set
MOV AL, [input]     ; AL = some value
AND AL, 08h         ; Mask bit 3
JNZ bit3_set        ; Jump if bit 3 was 1
; ... bit 3 is 0 ...
bit3_set:
; ... bit 3 is 1 ...
; Programmer then tries to use AL later
MOV [output], AL    ; Store original value??
Need a hint?
AND modifies the destination. What happened to the other bits of AL?
Show answer
Bug: The AND AL, 08h instruction destroys the original value of AL! After AND, AL is either 08h (bit 3 was set) or 00h (bit 3 was clear) — all other bits are lost. When MOV [output], AL executes, it stores the masked value, not the original. Fix: Use TEST AL, 08h instead of AND AL, 08h. TEST performs the same AND operation but only sets flags — it does not modify AL. The original value is preserved for later use.

Explain like I'm 5

AND is like a gate that only lets through things that BOTH friends agree on. OR lets through anything that at least one friend likes. XOR is a 'disagree detector' — it only lets through things where exactly one friend says yes. NOT is like opposite day — everything flips. Shifts are like pushing coins along a ruler — they fall off one end. Rotates are like a merry-go-round — what goes off one end comes back on the other side.

Fun fact

XOR AX, AX to zero a register is not just a programmer trick — Intel's own optimization manuals recommend it. On modern x86 processors, XOR reg, reg is detected by the hardware as a 'zeroing idiom' and does not even execute in the ALU — the register renamer simply maps the register to a physical zero register. It also breaks dependency chains, making it faster than MOV AX, 0 in superscalar pipelines.

Hands-on challenge

Write assembly routines for: (1) Count the number of 1-bits in AL (population count) using rotate and TEST, (2) Swap the upper and lower nibbles of AL without using XCHG (hint: use rotates), (3) Multiply AX by 100 using only SHL and ADD (no MUL instruction), (4) Implement a simple XOR cipher: encrypt a byte in AL with a key in BL, then decrypt it to prove XOR is self-inverse.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z