A custom 32-bit RISC CPU architecture designed for educational purposes, complete with an assembler, an emulator with framebuffer graphics, and an interactive debugger. The project demonstrates the full stack of a simple computer system, from ISA design through software tooling.
dsrisc is a from-scratch RISC instruction set architecture featuring:
- 16-bit fixed-width instructions with a compact encoding scheme
- 32 general-purpose registers (R0-R31) plus 32 machine registers (M0-M31)
- Load/store architecture with byte, word, and long word access
- Interrupt and syscall support with configurable vector tables
- Memory-mapped I/O including a 1920x1080 framebuffer and UART
The project includes two main tools:
- Assembler (
as) -- translates dsrisc assembly into binary images, with support for labels, literals, directives, and disassembly - Emulator (
emu) -- executes dsrisc binaries with an interactive debugger, SDL2 framebuffer display, and UART output
- GCC
- GNU Make
- SDL2 (
libSDL2-devor equivalent)
Each component has its own Makefile under src/. Build the common library
first, then the assembler and emulator:
# Build everything (debug by default)
make -C src/common
make -C src/assembler
make -C src/emulatorFor an optimized release build:
make -C src/common release
make -C src/assembler release
make -C src/emulator releaseThe assembler binary is produced at src/assembler/as and the emulator at
src/emulator/emu.
# Assemble a source file
src/assembler/as input.asm --output program.bin
# Disassemble a binary
src/assembler/as program.bin --disassemble
# Test/parse without generating output
src/assembler/as input.asm --test# Run a binary in the emulator (opens an SDL2 window)
src/emulator/emu program.binThe emulator starts in an interactive debugger. Commands:
| Command | Description |
|---|---|
s / step |
Execute one instruction |
c / continue |
Run until breakpoint (mov r0, r0) |
r <n> / run <n> |
Execute n instructions |
m <addr> [count] |
Dump memory at address |
q / quit |
Exit |
.ORIGIN 0x1000
STRING:
.ASCIZ "Hello, World!\n"
START:
ld r0, 123
ld r1, 0x5
st r1, [r2]
syscall 0x1There are 32 general-purpose registers, R0-R31. There are no architecturally
special-purpose registers, although by convention R31 is used as the link
register for function calls and R30 is used as the stack pointer. sp and lr
are assembler aliases for these registers.
Instructions are 16 bits long. There are 3 instruction formats:
Format 1 -- Load Immediate / Load PC-Relative (3-bit opcode)
110rrrrriiiiiiii LD r<reg>, <imm8> (sign-extended immediate)
111rrrrriiiiiiii LD r<reg>, <label> (PC + displacement*4)
Format 2 -- Standard (6-bit opcode)
ooooooaaaaabbbbb <op> r<a>, r<b>/imm5
Format 3 -- Wide Immediate (6-bit opcode + 10-bit immediate)
ooooooiiiiiiiiii JMP/SYSCALL <imm10>
The 3-bit load opcodes occupy the 0x30-0x3F range in the 6-bit opcode space,
leaving 48 opcodes for other instructions.
| Category | Opcodes | Instructions |
|---|---|---|
| Illegal/Return | 0x00-0x01 | ILL, RETI |
| Load/Store | 0x02-0x09 | LD, LD.W, LD.B, LD.SW, LD.SB, ST, ST.W, ST.B |
| Stack/System | 0x0A-0x0F | PUSH, POP, MOVM, SYSCALL |
| Branches | 0x10-0x1F | BZ, BNZ, BLT, BGT, BLE, BGE, JMP, CALL (register and immediate variants) |
| ALU | 0x20-0x2F | ADD, SUB, AND, OR, XOR, NOT, SHL, SHR, MUL, DIV, MOV |
All branch and ALU instructions have both register and immediate variants (bit 3 of the opcode selects the immediate form).
| Device | Base Address | Description |
|---|---|---|
| UART | 0x100000 |
Serial output (writes go to stdout) |
| Framebuffer | 0x1000000 |
1920x1080 32-bit ARGB pixel buffer |
| Font ROM | 0x10000000 |
IBM VGA 8x16 font data |
There are 32 architectural exceptions (vectors 0-6 defined, rest reserved):
| Vector | Exception |
|---|---|
| 0 | Reset |
| 1 | Instruction Fault |
| 2 | Data Fault |
| 3 | Illegal Instruction |
| 4 | Divide by Zero |
| 5 | Invalid Interrupt |
| 6 | Invalid Syscall |
32 additional interrupt vectors start at vector 32, controlled by the Interrupt Enable Register (IER). The Interrupt Vector Table base is stored in machine register M1 (IVBR).
| Register | Name | Purpose |
|---|---|---|
| M0 | IER | Interrupt Enable Register |
| M1 | IVBR | Interrupt Vector Base Register |
| M2 | IVLR | Interrupt Vector Limit Register |
| M3 | SVBR | Syscall Vector Base Register |
| M4 | SVLR | Syscall Vector Limit Register |
| M5 | ILR | Interrupt Link Register (return address) |
| M6 | FAR | Fault Address Register |
Syscalls are triggered by the SYSCALL instruction (immediate or register
form). The CPU saves the PC to the ILR and jumps to the handler address in the
Syscall Vector Table (base in M3/SVBR). Argument passing conventions are left to
software.
| Opcode | Encoding | Instruction | Description |
|---|---|---|---|
| 0x00 | 000000xxxxxxxxxx |
ILL |
Illegal instruction exception (all zeros are never valid) |
| 0x01 | 0000010000000000 |
RETI |
Return from interrupt/syscall (restores PC from ILR) |
| 0x02 | 000010aaaaabbbbb |
LD ra, [rb] |
Load long word from [rb] into ra |
| 0x03 | 000011aaaaabbbbb |
ST ra, [rb] |
Store long word from ra to [rb] |
| 0x04 | 000100aaaaabbbbb |
LD.W ra, [rb] |
Load unsigned 16-bit word |
| 0x05 | 000101aaaaabbbbb |
ST.W ra, [rb] |
Store 16-bit word |
| 0x06 | 000110aaaaabbbbb |
LD.B ra, [rb] |
Load unsigned byte |
| 0x07 | 000111aaaaabbbbb |
ST.B ra, [rb] |
Store byte |
| 0x08 | 001000aaaaabbbbb |
LD.SW ra, [rb] |
Load sign-extended 16-bit word |
| 0x09 | 001001aaaaabbbbb |
LD.SB ra, [rb] |
Load sign-extended byte |
| 0x0A | 001010aaaaabbbbb |
PUSH ra, rb |
Store ra at [rb], decrement rb by 4 |
| 0x0B | 001011aaaaabbbbb |
POP ra, rb |
Increment rb by 4, load [rb] into ra |
| 0x0C | 001100aaaaammmmm |
MOVM ra, mm |
Move machine register mm to ra |
| 0x0D | 001101aaaaammmmm |
MOVM mm, ra |
Move ra to machine register mm |
| 0x0E | 001110iiiiiiiiii |
SYSCALL imm10 |
Syscall with 10-bit immediate |
| 0x0F | 00111100000bbbbb |
SYSCALL rb |
Syscall with number in register |
| Opcode | Instruction | Description |
|---|---|---|
| 0x20 / 0x28 | ADD ra, rb/imm5 |
Add |
| 0x21 / 0x29 | SUB ra, rb/imm5 |
Subtract |
| 0x22 / 0x2A | AND ra, rb/imm5 |
Bitwise AND |
| 0x23 | OR ra, rb |
Bitwise OR |
| 0x24 | XOR ra, rb |
Bitwise XOR |
| 0x25 | NOT ra, rb |
Bitwise NOT |
| 0x26 / 0x2E | SHL ra, rb/imm5 |
Shift left |
| 0x27 / 0x2F | SHR ra, rb/imm5 |
Shift right |
| 0x2B | MUL ra, rb |
Multiply (upper 32 bits in ra, lower in rb) |
| 0x2C | DIV ra, rb |
Divide (quotient in ra, remainder in rb) |
| 0x2D | MOV ra, rb |
Move register |
| Opcode | Instruction | Description |
|---|---|---|
| 0x10 / 0x18 | BZ ra, rb/imm5 |
Branch if zero |
| 0x11 / 0x19 | BNZ ra, rb/imm5 |
Branch if not zero |
| 0x12 / 0x1A | BLT ra, rb/imm5 |
Branch if less than zero (signed) |
| 0x13 / 0x1B | BGT ra, rb/imm5 |
Branch if greater than zero (signed) |
| 0x14 / 0x1C | BLE ra, rb/imm5 |
Branch if less than or equal to zero |
| 0x15 / 0x1D | BGE ra, rb/imm5 |
Branch if greater than or equal to zero |
| 0x16 | JMP rb / RET |
Unconditional jump to register |
| 0x1E | JMP imm10 |
Unconditional jump (PC + imm*2) |
| 0x17 | CALL ra, rb |
Jump to rb, link return in ra |
| 0x1F | CALL ra, imm5 |
Jump to PC + imm*2, link return in ra |
src/
common/ Shared data structures (list, map, types)
assembler/ Assembler with tokenizer, parser, and encoder
emulator/ CPU emulator with SDL2 framebuffer and UART
compiler/ (placeholder for future work)
The repository includes a self-hosted presentation on the history and principles
of computer architecture, rendered entirely by dsrisc assembly running in the
emulator. It covers topics from early mechanical computers through modern
superscalar and out-of-order execution, with supporting images rendered through
a palettized framebuffer. A Python compiler
(src/emulator/presentation/compile_presentation.py) takes a simple text-based
slide format (presentation.txt) with LINE, TEXT, IMAGE, PAUSE, and
CLEAR commands and compiles it into a binary blob with quantized 256-color
palettes and RLE-compressed image rows. The dsrisc assembly presentation engine
then interprets this blob at runtime to render slides and images to the
framebuffer. The presentation source is at src/emulator/presentation.asm with
assets in src/emulator/presentation/.
The presentation images were gathered from various sources across the internet. If you are the copyright holder of any image and would like it removed or properly attributed, please open an issue and the request will be honored promptly.
- LLVM backend for Clang -- an LLVM target for dsrisc would allow compiling C and C++ (and other LLVM-frontend languages) directly to dsrisc binaries, enabling real software to run on the architecture
- Synthesizable FPGA implementation -- a hardware description of the dsrisc CPU in Verilog or VHDL, suitable for synthesis on an FPGA, bringing the architecture from simulation to real silicon. The iCEBreaker board with a DVI Pmod is a natural target, as its 1920x1080 DVI output matches the emulator's framebuffer resolution