Project F

RISC-V Assembler: Logical

Published · Updated

This RISC-V assembler post covers bitwise logical instructions, such as and, not, and xori. Bitwise instructions carry out the specified operator on each bit of the sources in turn. These instructions are included in RV32I, the base integer instruction set.

In the last few years, we’ve seen an explosion of RISC-V CPU designs on FPGA and ASIC, including the RP2350 found on the Raspberry Pi Pico 2. Thankfully, RISC-V is ideal for assembly programming with its compact, easy-to-learn instruction set. This series will help you learn and understand 32-bit RISC-V instructions and programming.

RISC-V Assembler: Arithmetic | Logical | Shift | Load and Store | Branch and Set | Jump and Function | Multiply and Divide | Compiler Explorer | Assembler Cheat Sheet

AND

The and and andi instructions perform logical AND on individual bits. The AND instructions are commonly used to mask parts of a register. Immediates are sign extended; see arithmetic sign extension for details.

and  rd, rs1, rs2  # rd = rs1 & rs2
andi rd, rs1, imm  # rd = rs1 & imm

These examples select the 4 least significant bits from the register t0:

li   t0, 42       # t0 = 42 (0...101010)
li   t1, 15       # t1 = 15 (0...001111)

and  t2, t0, t1   # t2 = 42 (0...101010) & 15 (0...001111) = 10 (0...001010)
andi t3, t0, 15   # t3 = 42 (0...101010) & 15 (0...001111) = 10 (0...001010)

Binary values are in brackets with ... indicating many identical bits.

OR

The or and ori instructions perform logical OR on individual bits.

or  rd, rs1, rs2  # rd = rs1 | rs2
ori rd, rs1, imm  # rd = rs1 | imm

Examples:

li   t0, 42      # t0 = 42 (0...101010)
li   t1, 15      # t1 = 15 (0...001111)

or   t2, t0, t1  # t2 = 42 (0...101010) | 15 (0...001111) = 47 (0...101111)
ori  t3, t0, 15  # t3 = 42 (0...101010) | 15 (0...001111) = 47 (0...101111)

XOR

The xor and xori instructions perform logical XOR (exclusive OR) on individual bits.

xor  rd, rs1, rs2  # rd = rs1 ^ rs2
xori rd, rs1, imm  # rd = rs1 ^ imm

Examples:

li   t0, 42      # t0 = 42 (0...101010)
li   t1, 15      # t1 = 15 (0...001111)

xor  t2, t0, t1  # t2 = 42 (0...101010) ^ 15 (0...001111) = 37 (0...100101)
xori t3, t0, 15  # t3 = 42 (0...101010) ^ 15 (0...001111) = 37 (0...100101)

NOT

The not pseudoinstruction inverts the bits in a register (0 → 1 and 1 → 0). The assembler converts not to xori (see example below). Verilog and C use tilde ~ for bitwise NOT, so I have used that notation here.

not  rd, rs1     # rd = ~rs1 (pseudoinstruction)

Example:

li   t0, 42      # t0 = 42 (0...101010)

# these two examples generate the same machine code
not  t2, t0      # t2 = ~(0...101010) = (1...010101)
xori t3, t0, -1  # t3 = (0...101010) ^ (1...111111) = (1...010101)

Notice how -1 is sign extended to 32 bits, so all the bits of the immediate are 1.

What’s Next?

The next post looks at RISC-V Shift Instructions, including fast multiplication and division.

Check out the RISC-V Assembler Cheat Sheet and my FPGA & RISC-V Tutorials.

Share your thoughts with me on Mastodon or X. If you enjoy my work, please sponsor me. Sponsors help me create new projects for everyone, and they get early access to blog posts and source code. 🙏

References