Lux Simulator - RISC-V Compiler

Lux Simulator runs RV32IM RISC-V assembly in the browser, a free online alternative to the Venus simulator. Write code, assemble it, and step through every instruction with beginner lessons and a full instruction reference.

Toolkit

Lux Simulator - RISC-V Compiler

What is the Lux RISC-V Simulator?

Lux is a free, browser-based RISC-V simulator and assembler. It lets you write, assemble, and run RV32IM assembly language directly online — no installation, no toolchain, and no account required. Whether you are learning computer architecture, studying for an exam, or prototyping low-level code, this online RISC-V simulator runs entirely in your browser.

Built as a modern successor to the venus simulator, Lux supports the base RV32I integer instruction set together with the RV32M multiply and divide extension, a full register file, byte-addressable little-endian memory, and system calls through the standard a7 ecall convention.

Online RISC-V Assembler and Step Debugger

The integrated assembler translates RISC-V assembly into an executable program, resolving labels, pseudo-instructions, and .data / .text directives automatically. You can run a program to completion or single-step through every instruction to watch the program counter, registers, and memory change in real time — an ideal way to learn how a RISC-V CPU actually executes code.

Supported instructions include arithmetic and logic operations (add, sub, and, or, xor, sll, srl, sra, slt), immediates (addi, andi, ori, slli), loads and stores (lw, lh, lb, sw, sh, sb), branches (beq, bne, blt, bge), jumps (jal, jalr), the multiply/divide extension (mul, mulh, div, rem), and common pseudo-instructions such as li, la, mv, j, call, and ret.

Understanding RISC-V Assembly Language

RISC-V is an open-standard reduced instruction set computer (RISC) architecture used across academia and industry, from microcontrollers to high-performance processors. Its clean, modular instruction set makes it the most popular choice for teaching computer organization, assembly programming, and computer architecture.

In the RISC-V calling convention, function arguments are passed in registers a0–a7, return values come back in a0, and system calls place their service number in register a7 before executing ecall. Lux follows these standard RISC-V patterns so what you learn here transfers directly to real hardware and toolchains.

Who is it for?

Students and educators in computer science and computer engineering use online RISC-V simulators to write and test RV32IM assembly without setting up GCC, QEMU, or Spike. Lux is a fast, free alternative for classrooms, homework, lab assignments, and self-study in computer architecture.

A Beginner's Guide to RISC-V Assembly

Assembly is the human-readable form of the actual instructions a processor runs. RISC-V is a clean, modern, open instruction set, which makes it a great place to learn. This guide starts from zero — no prior assembly experience required.

The big picture

A program is just a list of tiny instructions. The CPU reads them one at a time, top to bottom, unless an instruction tells it to jump somewhere else. Each instruction does one small job: add two numbers, copy a value from memory, compare two values, and so on. Real programs are simply thousands of these small steps stacked together.

Registers: the CPU's workbench

The processor cannot do math directly on memory. Instead it has 32 registers — think of them as 32 small, lightning-fast boxes, each holding one 32-bit whole number. The normal rhythm of assembly is: move data into registers, do the work there, then move results back out.

Every register has a number (x0x31) and a friendlier ABI name that hints at its usual job:

Name Number Typical use
zero x0 Always 0 — writes to it are ignored
ra x1 Return address (where a function goes back to)
sp x2 Stack pointer
a0a7 x10–x17 Function arguments and system-call numbers
t0t6 x5–x7, x28–x31 Temporary scratch values
s0s11 x8–x9, x18–x27 Saved values that survive function calls

That zero register looks useless but is everywhere: it gives you a constant 0 without spending a real register.

How to read an instruction

Most instructions share the same shape — the destination comes first, then the inputs:

operation  destination, source1, source2

So add t2, t0, t1 reads as "add the value in t0 to the value in t1 and put the result in t2." Once you internalise "destination first," the whole language gets easier.

Step 1 — putting numbers into registers

li means load immediate. "Immediate" is just jargon for a fixed number written directly in the code:

li t0, 5      # t0 now holds 5
li t1, 12     # t1 now holds 12

Step 2 — doing arithmetic

Now we can compute. Read the comments to see each register update:

li  t0, 3        # t0 = 3
li  t1, 4        # t1 = 4
add t2, t0, t1   # t2 = 3 + 4 = 7
sub t3, t1, t0   # t3 = 4 - 3 = 1

Many operations come in two flavours: a register version (add) and an immediate version whose name ends in i (addi) that takes a constant directly. Counting up by one is the classic example:

addi t0, t0, 1   # t0 = t0 + 1

Step 3 — talking to the outside world with ecall

Your program cannot print by itself; it has to ask the operating system. That request is a system call, made with ecall. Before calling, you fill in two registers:

  • a7 chooses which service you want, by number.
  • a0 carries the argument for that service.

To print the number 7:

li a0, 7      # the value we want to print
li a7, 1      # service 1 means "print integer"
ecall         # make the request

Putting the service number in a7 is the real RISC-V standard (the same one RARS uses), so the habits you build here carry over to actual hardware and toolchains.

Step 4 — making decisions with branches

A branch compares two registers and, only if the condition holds, jumps to a label. A label is just a name for a spot in your code, written with a colon at the end:

li  t0, 5
li  t1, 5
beq t0, t1, equal   # if t0 == t1, jump down to "equal"
li  a0, 0           # this line is skipped when they match
equal:
li  a0, 1

The everyday branches are beq (equal), bne (not equal), blt (less than), and bge (greater than or equal).

Step 5 — loops

A loop is nothing more than a branch that jumps backward. Here is the classic "add up 1 + 2 + … + 10," explained line by line:

main:
    li   t0, 0          # sum = 0   (our running total)
    li   t1, 1          # i = 1     (the counter)
loop:
    li   t2, 11
    bge  t1, t2, done   # if i >= 11, leave the loop
    add  t0, t0, t1     # sum = sum + i
    addi t1, t1, 1      # i = i + 1
    j    loop           # go back up and test again
done:
    mv   a0, t0         # copy the final sum into a0
    li   a7, 1          # print it
    ecall
    li   a7, 10         # service 10 means "exit"
    ecall

j jumps unconditionally, mv copies one register into another, and done: marks where to land when the loop is finished. Paste this into the editor and press Step to watch t0 climb 1, 3, 6, 10, 15 … all the way to 55.

Step 6 — working with memory

There are only 32 registers, so anything bigger lives in memory. You shuttle values between memory and registers using loads (memory → register) and stores (register → memory). The address is written offset(base) — a base register plus a small constant offset:

lw  a0, 0(sp)    # load the 32-bit word at address sp into a0
sw  a0, 4(sp)    # store a0 into memory at address sp + 4

A "word" is 32 bits (4 bytes). Smaller sizes exist too: lh/sh move 16-bit halves and lb/sb move single bytes.


Full Instruction Reference

Once the ideas above click, this is the cheat-sheet you keep coming back to.

Arithmetic & logic (register, R-type)

Instruction Operation Meaning
add rd, rs1, rs2 rd = rs1 + rs2 Signed addition
sub rd, rs1, rs2 rd = rs1 − rs2 Subtraction
and rd, rs1, rs2 rd = rs1 & rs2 Bitwise AND
or rd, rs1, rs2 rd = rs1 | rs2 Bitwise OR
xor rd, rs1, rs2 rd = rs1 ^ rs2 Bitwise XOR
sll rd, rs1, rs2 rd = rs1 << rs2 Shift left
srl rd, rs1, rs2 rd = rs1 >> rs2 Shift right (zero-fill)
sra rd, rs1, rs2 rd = rs1 >> rs2 Shift right (sign-keeping)
slt / sltu rd = rs1 < rs2 ? 1 : 0 Set if less than (signed / unsigned)

Arithmetic with a constant (immediate, I-type)

Instruction Operation
addi rd, rs1, imm rd = rs1 + imm
andi / ori / xori bitwise op with a constant
slli / srli / srai shift by a constant amount
slti / sltiu set if less than a constant

Loads & stores

Instruction Meaning
lw rd, off(rs1) Load 32-bit word
lh / lhu Load 16-bit half (signed / unsigned)
lb / lbu Load 8-bit byte (signed / unsigned)
sw rs2, off(rs1) Store 32-bit word
sh / sb Store half / byte

Branches & jumps

Instruction Jumps when
beq / bne equal / not equal
blt / bge less / greater-or-equal (signed)
bltu / bgeu less / greater-or-equal (unsigned)
jal rd, label jump and save the return address in rd
jalr rd, rs1, imm jump to rs1 + imm and save the return address

Multiply & divide (RV32M)

Instruction Operation
mul low 32 bits of the product
mulh / mulhu / mulhsu high 32 bits (signed / unsigned / mixed)
div / divu quotient (signed / unsigned)
rem / remu remainder (signed / unsigned)

System calls — ecall (service in a7, argument in a0)

a7 Service Argument
1 print integer a0 = value
4 print string a0 = address of text
11 print char a0 = character code
34 print hex a0 = value
10 exit
17 exit with code a0 = exit code

Handy pseudo-instructions

These are shortcuts the assembler expands into real instructions for you:

Pseudo Does
li rd, imm load a constant into rd
mv rd, rs copy one register to another
nop do nothing for one step
j label jump, no condition
call label / ret call a function / return from one
beqz / bnez branch if a register is (not) zero

FAQ

Is Lux free to use?

Yes. Lux is a completely free online RISC-V simulator. It runs entirely in your browser with no installation, no toolchain setup, and no account required.

Which RISC-V instructions does it support?

It supports the full RV32I base integer instruction set plus the RV32M multiply and divide extension, along with common pseudo-instructions such as li, la, mv, j, call, and ret.

Does it follow the standard a7 ecall convention?

Yes. System calls place their service number in register a7 and pass arguments in a0–a1, matching the standard RISC-V / RARS convention used on real hardware.

Can I use it to learn computer architecture?

Absolutely. You can single-step through RV32IM assembly while watching the program counter, registers, and memory update, making it ideal for computer architecture and assembly programming courses.