Assembly Language - Logical Instructions
Logical instructions are used for bit-level operations in assembly programming, widely used for masking, bit extraction, flag setting, and efficient multiplication/division.
AND - Bitwise AND
ANDPerforms a bitwise AND operation on two operands (result is 1 only when both bits are 1).
Example
; AND instruction example
section .text
global _start
_start:
; Basic bitwise AND
mov eax, 0x0F0F ; 0000 1111 0000 1111
and eax, 0x00FF ; 0000 0000 1111 1111
; Result: eax = 0x000F ; 0000 0000 0000 1111
; Common technique 1: Mask the low 4 bits (keep the low 4 bits)
mov eax, 0xAB ; 1010 1011
and eax, 0x0F ; 0000 1111
; eax = 0x0B ; 0000 1011
; Common technique 2: Check parity (AND with 1)
mov eax, 42 ; Even
and eax, 1 ; eax = 0 (even)
; The binary of 42 ends in 0, 42 & 1 = 0
mov eax, 43 ; Odd
and eax, 1 ; eax = 1 (odd)
; The binary of 43 ends in 1, 43 & 1 = 1
; Common technique 3: Clear a register
xor eax, eax ; Equivalent to mov eax, 0, but faster and shorter
; AND sets flags: CF=0, OF=0, ZF and SF depend on the result
mov eax, 1
mov ebx, 0
int 0x80
OR - Bitwise OR
ORPerforms a bitwise OR operation on two operands (result is 1 if at least one bit is 1).
Example
; Merge flag bits
mov eax, 0x0F00 ; 0000 1111 0000 0000
or eax, 0x00FF ; 0000 0000 1111 1111
; eax = 0x0FFF ; 0000 1111 1111 1111
; Set a specific bit (set bit 3 to 1)
mov eax, 0 ; 0000 0000
or eax, 0x08 ; 0000 1000
; eax = 0x8 ; 0000 1000
; Case conversion: uppercase to lowercase
mov al, 'A' ; al = 0x41 (0100 0001)
or al, 0x20 ; 0x20 = 0010 0000
; al = 0x61 = 'a' ; 0110 0001
NOT - Bitwise NOT
NOTInverts every bit of the operand (0 becomes 1, 1 becomes 0).
Example
mov eax, 0x0F0F0F0F ; 0000 1111 0000 1111 ...
not eax ; 1111 0000 1111 0000 ...
; eax = 0xF0F0F0F0
; NOT does not affect any flags (unlike AND/OR/XOR)
XOR - Bitwise XOR
XORPerforms a bitwise XOR operation on two operands (result is 1 if bits differ, 0 if they are the same).
Example
; XOR instruction example
section .text
global _start
_start:
; Basic XOR
mov eax, 0x0F0F ; 0000 1111 0000 1111
xor eax, 0x00FF ; 0000 0000 1111 1111
; Result: eax = 0x0FF0 ; 0000 1111 1111 0000
; Classic technique 1: Clear a register (more efficient than mov reg, 0)
xor eax, eax ; eax = 0, only occupies 2 bytes
xor ebx, ebx
xor ecx, ecx
; Classic technique 2: Swap two registers (no third temporary register needed)
mov eax, 100 ; eax = 100
mov ebx, 200 ; ebx = 200
xor eax, ebx ; eax = 100 xor 200
xor ebx, eax ; ebx = 200 xor (100 xor 200) = 100
xor eax, ebx ; eax = (100 xor 200) xor 100 = 200
; Now eax = 200, ebx = 100 (swap complete!)
; Classic technique 3: Simple encryption/decryption
mov al, 'A' ; Original character 'A' = 0x41
xor al, 0x55 ; Encryption: al = 'A' xor 0x55
; al is now some garbled value
xor al, 0x55 ; Decryption: XOR with 0x55 again to restore
; al = 'A' is back!
mov eax, 1
mov ebx, 0
int 0x80
The XOR swap trick is cool, but on modern CPUs it's less efficient than using
XCHGan instruction or a temporary register. It's good to know, but no need to insist on using it.
Shift Instructions
Shift instructions move binary bits to the left or right, commonly used for efficient multiplication and division (multiply by 2, divide by 2, etc.).
| Instruction | Function | Example |
|---|---|---|
| SHL | Logical left shift (bits exit left, zeros fill right) | shl eax, 1(multiply by 2) |
| SHR | Logical right shift (bits exit right, zeros fill left) | shr eax, 1(unsigned division by 2) |
| SAL | Arithmetic left shift (same as SHL) | sal eax, 1 |
| SAR | Arithmetic right shift (bits exit on the right, sign bit preserved on the left) | sar eax, 1(signed division by 2) |
Example
; Shift instruction example
section .text
global _start
_start:
; SHL: logical left shift = multiply by a power of 2
mov eax, 10 ; eax = 10 (1010)
shl eax, 1 ; eax = 20 (10100), i.e., 10×2
shl eax, 2 ; eax = 80 (1010000), i.e., 20×4
; SHR: logical right shift = unsigned divide by a power of 2
mov eax, 80 ; eax = 80
shr eax, 3 ; eax = 10, i.e., 80÷8
; SAR: arithmetic right shift = signed divide by a power of 2 (preserves the sign bit)
mov eax, -16 ; eax = -16 (0xFFFFFFF0)
sar eax, 2 ; eax = -4 (0xFFFFFFFC), i.e., -16÷4
; SAR vs SHR:
; If eax = 0xFFFFFFF0 (-16), SHR yields 0x3FFFFFFC (a very large positive number)
; whereas SAR yields 0xFFFFFFFC (-4), preserving the sign bit
; The last bit shifted out goes into the CF flag
mov eax, 5 ; 0101
shr eax, 1 ; eax = 2, CF = 1 (the trailing 1 was shifted out)
jc carry_was_set ; If CF=1, the original number was odd
carry_was_set:
mov eax, 1
mov ebx, 0
int 0x80
Rotate Instructions
Rotate shifts fill the bit shifted out back into the other end, forming a rotation:
| Instruction | Description |
|---|---|
| ROL | Rotate left (bypasses CF) |
| ROR | Rotate right (bypasses CF) |
| RCL | Rotate left through carry (involves CF) |
| RCR | Rotate right through carry (involves CF) |
Example
; ROL: rotate left
mov al, 0x85 ; 1000 0101
rol al, 1 ; Shift left by 1 bit: 0000 1011
; CF = 1 (the most significant bit is shifted out into CF)
; At the same time, the original 1 in CF is shifted into the lowest bit, forming a rotation
; ROL actual effect: shift left by 1 bit, the most significant bit goes into both CF and the lowest bit
; 1000 0101 -> ROL 1 -> 0000 1011
; RCL: rotate left through carry (CF participates in the rotation)
clc ; Clear CF = 0
mov al, 0x85 ; 1000 0101
rcl al, 1 ; CF participates: CF bit7...bit0 -> CF
; Result: 0000 1010, CF = 1
; Rotate shifts are commonly used in encryption algorithms and bit manipulation
Practical Uses of Logical Operations
A comprehensive example — implementing a simple bit-flag system using logical operations:
Example
; Implementing a bit-flag system using logical operations
section .data
flags db 0 ; 8 flag bits, initially all 0
; bit0: whether active
; bit1: whether visible
; bit2: whether needs saving
; bit3: whether modified
FLAG_ACTIVE equ 1 ; 0000 0001
FLAG_VISIBLE equ 2 ; 0000 0010
FLAG_NEED_SAVE equ 4 ; 0000 0100
FLAG_MODIFIED equ 8 ; 0000 1000
section .text
global _start
_start:
; Setting flag bits: OR
mov al, [flags]
or al, FLAG_ACTIVE ; Set bit0
or al, FLAG_VISIBLE ; Set bit1
; al = 0000 0011 = 3
mov [flags], al
; Checking flag bits: AND + TEST
test byte [flags], FLAG_ACTIVE ; Test whether bit0 is 1
jnz is_active ; ZF=0 means the bit is 1
is_active:
; Clearing flag bits: AND + NOT
mov al, [flags]
and al, ~FLAG_VISIBLE ; Clear bit1 (keep other bits unchanged)
; al = 0000 0001 = 1
mov [flags], al
; Toggling flag bits: XOR
mov al, [flags]
xor al, FLAG_MODIFIED ; Toggle bit3
; If it was 0 it becomes 1, if it was 1 it becomes 0
mov eax, 1
mov ebx, 0
int 0x80
Other extensionsBit-flag manipulation is extremely common in system programming. OS kernels, device drivers, and embedded systems widely use this technique to manage state.