RISC Lab Assembler

On the "RISC Lab" levels (part 5 of the course) the Assembler tab works differently from levels 18–29. It is now a true RV32I subset: 32 registers, 32-bit machine words, R/I/S/B/U/J formats. Every program line assembles into a word you can compare with the reference bitwise, and from level 54 you can also execute it cycle by cycle on the reference core.

The assembler grows with your schematic: build a new instruction at a level — the assembler learns it. Because of this design, an "unknown instruction" error is not a bug: check which level unlocks the instruction.

What a program looks like

# comment — everything after # is ignored
loop:                      # label — a name and a colon
  addi x5, x0, 3           # instruction, comma-separated operands
  addi x5, x5, -1          # numbers: decimal or 0x…
  bne  x5, x0, loop        # branches go to labels
.end                       # halt — a required line

Growth stages: which instruction comes from which level

LevelNew instructionsNew pseudo-instructions
51add sub sll slt sltu xor srl sra or and, addi slti sltiu xori ori andi slli srli sraili, mv, nop
52lw, sw—
53beq—
54bne—
55luili learns large constants (lui + addi)
56jal, jalrj, ret

At level 51 the manual "Word from fields" exercise replaces a schematic task: build the machine word from instruction fields and compare it with the assembler bitwise — you see exactly where each operand lives inside the word.

Instruction formats

Every instruction is one 32-bit word. Its fields are laid out in one of six formats:

FormatInstructionsExample
Rregister-register arithmeticadd x7, x5, x6
Iaddi slti sltiu xori ori andi slli srli srai, lw, jalraddi x5, x0, 7, lw x6, 4(x5)
Sswsw x7, 8(x5)
Bbeq, bnebeq x5, x6, done
Uluilui x10, 0xFFFFF
Jjaljal ra, sum

Memory instructions use the "offset(register)" pair: lw x6, 0(x5) reads the word at address x5 + 0. The offset ranges from −2048 to 2047; lui takes 0…0xFFFFF.

Pseudo-instructions

Pseudo-instructions are convenient shorthands the assembler expands into real instructions:

PseudoExpands to
li x5, 7addi x5, x0, 7; a large number from level 55 — lui + addi (2 words)
mv x6, x5addi x6, x5, 0
nopaddi x0, x0, 0
j loopjal x0, loop
retjalr x0, 0(ra)
.endjal x0, 0 — the halt word

li with a large number takes two words — label addresses below it shift by 4. Before level 55 a large li gives ERR_ASM_IMM_RANGE.

Branches and labels

Assembly errors

Errors appear under the editor with a line number. The text tells you what to check:

CodeWhat happened and what to do
ERR_ASM_SYNTAXThe line could not be parsed — check the format: mnemonic and comma-separated operands.
ERR_ASM_UNKNOWN_MNEMONICUnknown instruction — check the spelling and the stage: the instruction may not exist yet.
ERR_ASM_UNKNOWN_PSEUDOThe pseudo-instruction is not taught yet — write it with plain instructions.
ERR_ASM_UNKNOWN_REGBad register name — use x0…x31 or ra.
ERR_ASM_IMM_RANGEThe number does not fit the instruction — check the range (−2048…2047 for addi, 0…0xFFFFF for lui).
ERR_ASM_UNALIGNED_TARGETThe label sits on an odd address — you can only branch to an even address.
ERR_ASM_UNDEF_LABELLabel not found — make sure it is declared and spelled the same in both places.
ERR_ASM_DUP_LABELA label with this name already exists — label names must be unique.
ERR_ASM_NO_ENDNo .end line — add it at the end of the program.
ERR_ASM_EMPTYThe program is empty — write code in the editor.

The assembler reports all errors at once, not just the first one — fix them top to bottom and press "Assemble" (Ctrl+Enter) again.

Execution from level 54

From level 54 "Assemble" not only builds the words but also executes the program on the reference single-cycle core: one instruction — one cycle. The "Execution" block shows registers, the cycle counter, and the instruction trace. Run-time errors (a misaligned address, a read from the instruction window, an address outside the data window) freeze the machine with an error code — they are documented in the Execution tab help.

Example: a bne loop (level 54)

# x5 = 3; run the body 3 times
  addi x5, x0, 3      # x5 = 3 — the counter
loop:
  addi x5, x5, -1     # x5 -= 1
  bne  x5, x0, loop   # while x5 != 0 — jump back
.end

The last bne executes but does not jump: x5 is already 0. A loop of N iterations costs N×(body + 1) cycles.

Example: a data base with lui (level 55)

# read a word from the data array at 0x1000
  lui  x10, 0x1       # x10 = 0x1000 — the array base
  lw   x6, 0(x10)     # x6 = the first word of the array
.end

lui places a number into the upper 20 bits: lui x10, 0x1 yields 0x1000, because the value is shifted 12 bits left.

Try it in the game →

Related materials

FAQ

How does the RISC Lab assembler differ from the assembler of levels 18–29?

It is a true RV32I subset: 32 registers, 32-bit words, R/I/S/B/U/J formats, and real byte branch offsets. The instruction list grows with the levels: every new schematic level teaches the assembler new instructions.

What does .end at the end of a program mean?

It is the halt. The assembler turns .end into the word jal x0, 0 — a jump to itself. The processor executes it and freezes; the cycle counter stops growing.

Why does an instruction fail to assemble even though it is written correctly?

The assembler only knows instructions from levels you have already reached. For example, beq appears at level 53, bne at 54, lui at 55. Before that level they produce ERR_ASM_UNKNOWN_MNEMONIC or ERR_ASM_UNKNOWN_PSEUDO.