RISC-V Assembler: Logical
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
- RISC-V Technical Specifications (riscv.org)