A RISC-V 32-bit assembler written from scratch in C. It translates RV32I+M assembly into ELF32 executables that can be run by the companion riscv-emulator.
Designed for Unix systems, or WSL on Windows.
Source is processed in stages, each with explicit ownership of its allocations through an arena allocator:
- Lexer turns source text into tokens
- Parser builds instructions, directives, and labels
- Expander decomposes pseudoinstructions into base instructions
- First pass builds the symbol table and assigns addresses
- Second pass resolves symbols and encodes the binary
- ELF writer emits an ELF32 executable
Instruction sets: RV32I and the M extension
Pseudoinstructions: nop, mv, not, neg, seqz, snez, sltz, sgtz, beqz, bnez, blez, bgez, bltz, bgtz, bgt, ble, bgtu, bleu, j, jal, jr, jalr, ret, call, tail, li, la, lla
Directives: .text, .data, .bss, .rodata, .byte, .half, .word, .string/.asciz, .ascii, .zero/.space, .equ/.set, .align/.balign/.p2align
- cmake 3.21 or newer
- gcc 13 or newer
- clangd (optional, for editor tooling)
cmake -B build/release -DCMAKE_BUILD_TYPE=Release
cmake --build build/releaseOther build types: Debug (default, AddressSanitizer), Test (builds the unit tests), and Profile.
riscv-assembler [-o <output>] <file.s>The output defaults to a.out. To assemble and run a program:
riscv-assembler -o hello.elf hello.s
riscv-emulator run hello.elfcmake -B build/test -DCMAKE_BUILD_TYPE=Test
cmake --build build/test
ctest --test-dir build/test --output-on-failure- Operands cannot contain operator expressions (for example
li x1, (1 << 4) - 1) orsymbol+offset - Numeric local labels (
1:,1f,1b) are not supported fenceandfence.iare not supported, even though the emulator supports them