MapacheSPIM is a SPIM-like simulator for assembly programming built on the Unicorn Engine CPU emulator. It provides an interactive, console-based environment for learning assembly language, debugging programs instruction-by-instruction, and exploring computer architecture concepts across multiple ISAs.
When teaching computer architecture or learning a new ISA, you need a simple, interactive way to see exactly what's happening at the machine level. Traditional simulators are often complex, opaque, or tied to a single architecture. MapacheSPIM provides a SPIM-like experience - familiar commands, clear output, and the ability to step through code one instruction at a time - powered by the battle-tested Unicorn Engine CPU emulator.
MapacheSPIM is designed for:
- Students learning assembly programming and computer architecture
- Educators teaching courses on computer systems
- Researchers exploring ISA design and formal methods
- Anyone who wants to understand what's really happening inside the machine
The simulator shows you everything: instruction bytes, disassembly, register changes, memory contents, and symbol information. You can set breakpoints by function name, step through code, and see exactly which registers changed and why.
MapacheSPIM is a pure Python package that runs on Windows, macOS (Intel and Apple Silicon), and Linux with Python 3.9 or newer. No cross-compiler or other toolchain is needed: it includes its own assembler.
The easiest way to install it is with pipx, which puts the
mapachespim command on your path in its own isolated environment:
pipx install git+https://github.com/UCSBarchlab/MapacheSPIM.gitor, with uv: uv tool install git+https://github.com/UCSBarchlab/MapacheSPIM.git.
Plain pip install git+https://github.com/UCSBarchlab/MapacheSPIM.git works too (ideally inside a
virtual environment).
Check that it worked:
mapachespim --versionDependencies (installed automatically):
unicorn- CPU emulator frameworkcapstone- Disassemblerpyelftools- ELF file parsing
The assembler is built in (pure Python), so there is nothing else to install on any platform.
Start the console by typing:
mapachespimType examples to see the example programs that come with MapacheSPIM, quickstart for a short
tutorial, or help for all commands.
Here is an example loading the bundled RISC-V Fibonacci program, stopping at a function, and looking at the source and machine state:
$ mapachespim
Welcome to MapacheSPIM. Type help or ? to list commands, or quickstart for a tutorial.
(mapachespim) load riscv/fibonacci
Loaded riscv/fibonacci (RISCV)
Entry point: 0x0000000080000000
Source info: fibonacci.s (32 address mappings)
(mapachespim) break fibonacci
Breakpoint set at fibonacci (0x80000038)
(mapachespim) run
Breakpoint hit at 0x0000000080000038 after 6 instructions
PC = 0x0000000080000038
(mapachespim) list
fibonacci.s:
81: # sp+16: saved a0 (original n value)
82: # Total: 24 bytes
83: # ============================================================================
84: fibonacci:
85: # Base case 1: if n == 0, return 0
86> beqz a0, base_case_zero # <-- PC: 0x80000038
87:
88: # Base case 2: if n == 1, return 1
89: li t0, 1
90: beq a0, t0, base_case_one
(mapachespim) step
[0x80000038] 0x63040504 beqz a0, 0x48 <fibonacci>
(mapachespim) step
[0x8000003c] 0x93021000 addi t0, zero, 1 <fibonacci+4>
(mapachespim) regs
x0 (zero) = 0x0000000000000000 x1 ( ra) = 0x0000000080000018 ★
x2 ( sp) = 0x0000000080181000 ★ x3 ( gp) = 0x0000000000000000
x4 ( tp) = 0x0000000000000000 x5 ( t0) = 0x0000000000000001 ★
x6 ( t1) = 0x0000000000000000 x7 ( t2) = 0x0000000000000000
x8 ( s0) = 0x0000000000000000 x9 ( s1) = 0x0000000000000000
x10 ( a0) = 0x0000000000000007 ★ x11 ( a1) = 0x0000000000000000
x12 ( a2) = 0x0000000000000000 x13 ( a3) = 0x0000000000000000
x14 ( a4) = 0x0000000000000000 x15 ( a5) = 0x0000000000000000
x16 ( a6) = 0x0000000000000000 x17 ( a7) = 0x0000000000000000
x18 ( s2) = 0x0000000000000000 x19 ( s3) = 0x0000000000000000
x20 ( s4) = 0x0000000000000000 x21 ( s5) = 0x0000000000000000
x22 ( s6) = 0x0000000000000000 x23 ( s7) = 0x0000000000000000
x24 ( s8) = 0x0000000000000000 x25 ( s9) = 0x0000000000000000
x26 ( s10) = 0x0000000000000000 x27 ( s11) = 0x0000000000000000
x28 ( t3) = 0x0000000000000000 x29 ( t4) = 0x0000000000000000
x30 ( t5) = 0x0000000000000000 x31 ( t6) = 0x0000000000000000
pc = 0x0000000080000040
(mapachespim) mem fibonacci 64
0x80000038: 63 04 05 04 93 02 10 00 63 04 55 04 13 01 81 fe |c.......c.U.....|
0x80000048: 23 38 11 00 23 34 81 00 23 30 a1 00 13 05 f5 ff |#8..#4..#0......|
0x80000058: ef f0 1f fe 13 04 05 00 03 35 01 00 13 05 e5 ff |.........5......|
0x80000068: ef f0 1f fd 33 05 a4 00 83 30 01 01 03 34 81 00 |....3....0...4..|
(mapachespim) quit
Goodbye!
At any point when execution is stopped, you can inspect registers and memory. The full 64-bit value of each
register is shown in hex along with its ABI name (like a0, sp, ra). A star (★) appears next to registers
that have changed since you last looked, to help you follow the execution of the program. Memory is shown in
bytes, grouped into 4-byte words for easier reading. Here mem fibonacci 64 shows the machine
code of the fibonacci function: its first word, 63 04 05 04, is the beqz instruction that the
first step executed.
Write your program in a .s file and load it directly; MapacheSPIM assembles it for you (with debug
info, so list shows your source):
# hello.s
.isa riscv64 # which ISA this file is for
.data
msg: .asciz "Hello, world!\n"
.text
.globl _start
_start:
la a0, msg # print_string(msg)
li a7, 4
ecall
li a7, 10 # exit
ecall(mapachespim) load hello.s
Assembled hello.s (1096 bytes)
Loaded hello.s (RISCV)
Entry point: 0x0000000080000000
Source info: hello.s (5 address mappings)
(mapachespim) run
Hello, world!
Program exited with code 0 after 6 instructions
After editing the file, type reload to re-assemble and load the new version; breakpoints on labels
follow their labels. If your file has no .isa line, give the ISA when loading: load hello.s riscv64.
To start from the examples, copy them somewhere you can edit them:
mapachespim --copy-examples my-examplesYou can also run a program without the interactive console, which is handy for testing and
autograding. The exit status is the program's exit code (1 on a runtime error, 124 if it doesn't finish
within --max-steps instructions):
mapachespim -e hello.sThe standalone assembler writes an ELF file if you want one: mapachespim-as -g hello.s -o hello.
Every step shows:
- The address (
0x80000030) - The instruction bytes (
0x13050005) - The disassembly (
addi x10, x0, 0x5) - The symbol name (
<main>)
Use function names instead of memorizing addresses:
(mapachespim) break fibonacci # Set breakpoint by name
(mapachespim) info symbols # List all functions
(mapachespim) disasm fibonacci # Disassemble a function
SPIM-compatible syscalls for printing and input:
# Print "Hello, World!"
la a0, msg # Load string address
li a7, 4 # Syscall 4 = print_string
ecall
# Exit program
li a7, 10 # Syscall 10 = exit
ecallSupported syscalls: print_int (1), print_string (4), read_int (5), exit (10), print_char (11), read_char (12), exit2 (17), exit_code (93)
See Syscall Reference for complete details.
Familiar SPIM-like interface:
| Command | Alias | Description |
|---|---|---|
load <file> |
Load a .s file (assembled for you), an ELF executable, or an example |
|
reload |
Re-assemble and reload the current program after editing | |
examples |
List the bundled example programs | |
step [n] |
s |
Execute n instructions (default 1) |
run [max] |
r |
Run until exit, breakpoint, or max instructions |
break <addr> |
b |
Set breakpoint at address or label |
continue |
c |
Continue after breakpoint |
reset |
Restart the program from the beginning | |
regs |
Show all registers | |
pc |
Show program counter | |
mem <addr> [len] |
Show memory contents (address, label, or section) | |
disasm <addr> [n] |
d |
Disassemble n instructions |
list |
l |
Show source code around the PC |
info symbols |
List all symbols | |
info sections |
List ELF sections | |
quit |
q |
Exit simulator |
See Console Guide for complete command reference.
MapacheSPIM is powered by the Unicorn Engine, a battle-tested CPU emulator framework based on QEMU. This means:
- Reliable - Built on the same codebase that powers countless virtual machines
- Multi-ISA - Support for RISC-V, ARM64, x86-64, and MIPS32
- Fast - Efficient emulation using proven QEMU technology
- Easy to Install - Pure
pip install, no C/C++ compilation required
MapacheSPIM supports four instruction set architectures, each with the same console commands, syscalls, and a matching set of example programs:
| ISA | Examples |
|---|---|
| RISC-V 64-bit (RV64IM) | examples/riscv/ |
| MIPS32 (big-endian) | examples/mips/ |
| ARM64 (AArch64) | examples/arm/ |
| x86-64 (AT&T or Intel syntax) | examples/x86_64/ |
All four are assembled by MapacheSPIM's built-in assembler, which works on every platform. For
everything it accepts, it produces byte-for-byte the same machine code as GNU as (the standard
assembler used in textbooks and courses), including pseudo-instruction expansions (RISC-V li, MIPS
la, ARM64 ldr x0, =value literal pools), data directives, alignment padding, and branch
relaxation (x86-64 short and long jumps). This is checked continuously by randomized differential
tests that assemble hundreds of thousands of instructions with both and compare the results.
x86-64 accepts both AT&T syntax (movq $1, %rax) and Intel syntax (mov rax, 1), detected per
instruction, as well as the .intel_syntax / .att_syntax directives.
Loading an ELF file detects its ISA automatically.
Inspired by SPIM, designed for education:
- Clear Output - See exactly what changed, no guessing
- Symbolic Debugging - Use function names, not just addresses
- Helpful Errors - Understand what went wrong
- Progressive Complexity - Start simple, add features as needed
- Instant Feedback - See results of every instruction
- Quick Start Guide - Get running in 5 minutes
- Console Guide - Complete command reference
- Syscall Reference - I/O syscalls for programs
- Examples Guide - Learn from example programs
git clone https://github.com/UCSBarchlab/MapacheSPIM.git
cd MapacheSPIM
pip install -e ".[dev]"
python -m pytest tests/ # run the test suite
ruff check mapachespim/ # lint
ruff format mapachespim/ # format
mypy mapachespim/ # type check
make -B -C examples DEBUG=1 # rebuild the example binariesSee docs/dev/architecture.md for how the code is organized and how to add an ISA, and docs/RELEASING.md for how to publish a release.
- Unicorn Engine - GPLv2 License
- Capstone - BSD License
- MapacheSPIM - MIT License
- Examples - Educational use
- Issues: GitHub Issues
- Discussions: GitHub Discussions
