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
- One instruction per line; empty lines and comment-only lines are skipped.
- Comments start with
#(//and;also work in level templates). - Registers:
x0…x31plus the namera(≡ x1).x0always reads as 0. - Numbers: decimal (
-5) and hexadecimal (0x1F). - The
.endline is required — without it assembly givesERR_ASM_NO_END. It may also sit in the middle of a program: the halt word lands at its own address and parsing continues.
Growth stages: which instruction comes from which level
| Level | New instructions | New pseudo-instructions |
|---|---|---|
| 51 | add sub sll slt sltu xor srl sra or and, addi slti sltiu xori ori andi slli srli srai | li, mv, nop |
| 52 | lw, sw | — |
| 53 | beq | — |
| 54 | bne | — |
| 55 | lui | li learns large constants (lui + addi) |
| 56 | jal, jalr | j, 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:
| Format | Instructions | Example |
|---|---|---|
| R | register-register arithmetic | add x7, x5, x6 |
| I | addi slti sltiu xori ori andi slli srli srai, lw, jalr | addi x5, x0, 7, lw x6, 4(x5) |
| S | sw | sw x7, 8(x5) |
| B | beq, bne | beq x5, x6, done |
| U | lui | lui x10, 0xFFFFF |
| J | jal | jal 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:
| Pseudo | Expands to |
|---|---|
li x5, 7 | addi x5, x0, 7; a large number from level 55 — lui + addi (2 words) |
mv x6, x5 | addi x6, x5, 0 |
nop | addi x0, x0, 0 |
j loop | jal x0, loop |
ret | jalr x0, 0(ra) |
.end | jal 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
- A label is a name of Latin letters, digits and
_not starting with a digit, declared on a line likename:. - The
beq/bne/jaloffset counts bytes from the address of the branch instruction itself to the label. The assembler computes it — you only need the label. - A branch target must be even (addresses go in steps of 4, so an odd target usually means a typo in the label): an odd target gives
ERR_ASM_UNALIGNED_TARGET. beq/bnerange is −4096…+4094 bytes;jalreaches ±1 MB.
Assembly errors
Errors appear under the editor with a line number. The text tells you what to check:
| Code | What happened and what to do |
|---|---|
ERR_ASM_SYNTAX | The line could not be parsed — check the format: mnemonic and comma-separated operands. |
ERR_ASM_UNKNOWN_MNEMONIC | Unknown instruction — check the spelling and the stage: the instruction may not exist yet. |
ERR_ASM_UNKNOWN_PSEUDO | The pseudo-instruction is not taught yet — write it with plain instructions. |
ERR_ASM_UNKNOWN_REG | Bad register name — use x0…x31 or ra. |
ERR_ASM_IMM_RANGE | The number does not fit the instruction — check the range (−2048…2047 for addi, 0…0xFFFFF for lui). |
ERR_ASM_UNALIGNED_TARGET | The label sits on an odd address — you can only branch to an even address. |
ERR_ASM_UNDEF_LABEL | Label not found — make sure it is declared and spelled the same in both places. |
ERR_ASM_DUP_LABEL | A label with this name already exists — label names must be unique. |
ERR_ASM_NO_END | No .end line — add it at the end of the program. |
ERR_ASM_EMPTY | The 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.
.endis the halt: it adds no cycle, the counter freezes.- Data memory lives in the window
0x1000…0x13FF; the address step forlw/swis exactly 4. - The LED is at
0xFFFF_F000, write-only (appears at level 59); the switches at0xFFFF_F004, read-only.
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.
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.