Skip to content
/ dsrisc Public

A custom 32-bit RISC CPU architecture with assembler, emulator, and interactive debugger for educational purposes

Notifications You must be signed in to change notification settings

dougvj/dsrisc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 

Repository files navigation

Doug's Simple RISC (dsrisc)

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.

Overview

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

Building

Dependencies

  • GCC
  • GNU Make
  • SDL2 (libSDL2-dev or equivalent)

Build

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/emulator

For an optimized release build:

make -C src/common release
make -C src/assembler release
make -C src/emulator release

The assembler binary is produced at src/assembler/as and the emulator at src/emulator/emu.

Usage

Assembler

# 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

Emulator

# Run a binary in the emulator (opens an SDL2 window)
src/emulator/emu program.bin

The 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

Example Assembly

.ORIGIN 0x1000

STRING:
  .ASCIZ "Hello, World!\n"

START:
  ld r0, 123
  ld r1, 0x5
  st r1, [r2]
  syscall 0x1

Architecture

Registers

There 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.

Instruction Format

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.

Instruction Set Summary

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).

Memory-Mapped I/O

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

Exceptions and Interrupts

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).

Machine Registers (M0-M31)

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

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.

Detailed Instruction Reference

Load/Store Instructions

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

ALU Instructions

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

Control Flow Instructions

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

Project Structure

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)

Computer Architecture Presentation

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.

Future Aspirations

  • 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

About

A custom 32-bit RISC CPU architecture with assembler, emulator, and interactive debugger for educational purposes

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •