← Back to FaresHere
EC351 — Spring 2025 · RISC-V Assembly

Piano Sound via RISC-V

aC5
sD5
dE5
fF5
gG5
hA5
jB5
kC6
w
e
r
t
y

Click a key, or just type a,s,d,f,g,h,j,k and w,e,r,t,y all play. (This is only a broswer preview); the real program runs as RISC-V inside RARS.

A playable piano simulated entirely in RISC-V assembly — keyboard input mapped to MIDI notes, an automated "Twinkle Twinkle Little Star," and a hidden surprise for anyone who types the right four letters.

Scroll
01 — Overview

Built for EC351 with Elias Abuamer and Mohammed Almoldy, this project simulates a playable piano using nothing but the RISC-V instruction set, run inside the RARS simulator. Every key press is read raw, matched against a note table, and turned into sound through RARS's built-in MIDI syscall — no audio library, no abstraction layer underneath.


The interesting part isn't the piano itself — it's that something as fluid as music has to be expressed entirely through branches, lookups, and register juggling. Thirteen notes from C5 to C6, a 42-note melody played from two parallel arrays, and a four-key Easter egg tracked by a tiny hand-rolled state machine.

13
Notes mapped, C5 → C6
42
Notes in the Twinkle melody
31
RARS sound syscall number
4
Key states for the Easter egg
02 — How the Sound Happens

RARS exposes a single syscall for audio — load four registers and call it. No instrument files, no buffers, just a number for pitch, a number for how long, and a number for how loud.

RegisterValueMeaning
a731Sound system call
a072–84MIDI note number (C5 to C6)
a1msDuration the note plays for
a20Instrument — piano
a30–127Volume / velocity
03 — Program Flow
Main input loop
  1. Print the key map, then block on syscall 12 to read one character.
  2. Compare it against 'q' (quit) and 'm' (play Twinkle) first.
  3. Run it through the Easter-egg state check (more on that below).
  4. Chain-compare it against the 13 piano keys with beq, falling through to an invalid-key message if nothing matches.
  5. On a match, set the note index and jump into play_note.
  6. Walk note_names to print the note, look up its MIDI value in notes, fire the sound syscall, and loop back to step 1.
Melody playback
  1. Point two registers at twinkle_notes and twinkle_durs in parallel.
  2. For each of the 42 entries: load the note and duration, fire the syscall, then call the delay subroutine.
  3. Advance both pointers by 4 bytes and repeat until the counter hits 42.
  4. Fall back into the main input loop.
04 — Code Highlights
Looking up and playing a note
la t2, notes
slli t3, t1, 2          # index * 4 bytes
add  t2, t2, t3
lw   a0, 0(t2)         # MIDI note number
li   a1, 1000          # duration, ms
li   a2, 0             # instrument: piano
li   a3, 127           # volume
li   a7, 31            # sound syscall
ecall
Busy-wait delay between notes
delay:
    li   t4, 150000
delay_loop:
    addi t4, t4, -1
    bnez t4, delay_loop
    ret
Easter egg

Type r → i → c → k in that order and the program quietly switches melodies. Register s7 holds the current position in the sequence — any wrong key resets it back to zero.

check_k:
    li   t1, 3
    beq  s7, t1, play_rickroll
    j    cont
05 — Debugging & Test Runs

Verified by ear, mostly. Each key was checked against its expected pitch, notes were played back to back to confirm the loop doesn't drop or stall, and the melody and Easter-egg paths were both run end to end before falling back to the main loop.

01

Pitch accuracy — each of the 13 keys plays its correct MIDI note.

02

Sequencing — multiple notes and melodies play cleanly back to back.

03

Clean exit — 'q' terminates the program via syscall 10 every time.