Run real RISC-V Linux on a Nintendo 3DS.
3DS-CLI boots a stock RV32 Linux 6.6 kernel with a glibc userspace off your SD card, using mini-rv32ima-mmu, mini-rv32ima with a full Sv32 MMU, S-mode and a built-in SBI, running as ordinary homebrew inside Horizon OS.
- Top screen is a terminal (ANSI/xterm, 24-bit colour, zoom, panning). Bottom screen is a touch keyboard.
- Writes go straight to
rootfs.ext2on the SD card. No save step, and nothing is lost if the battery dies. - Networking over the console's WiFi.
wget,sshandntpdall work. - The guest gets real hardware: SD card, NAND, battery, sensors, sliders, cameras, mic, speakers.
- Comes with bash, vim, nano, htop, tree, wget, dropbear, BusyBox.
- Runs on Old 3DS and New 3DS.
Universal Updater: find 3DS-CLI in the app and it does the rest.
Manual, from the latest release zip:
- Copy
3ds-cli.3dsxtosdmc:/3ds/andImageto the root of the SD card. The rootfs is bundled insideImage, so that's everything. - Launch it from the Homebrew Launcher. First boot is slower because it
unpacks the rootfs to
sdmc:/rootfs.ext2. - Log in as
rootwith a blank password.
Delete rootfs.ext2 to reset everything. It gets recreated next boot.
| Button | Action |
|---|---|
| L / Y | Zoom out |
| R / X | Zoom in |
| ZL | Toggle auto-follow cursor |
| ZR | Toggle font (8x8 / 5x7 compact) |
| Circle Pad | Pan the viewport (turns off auto-follow) |
| D-Pad | Arrow keys |
| START | Quit to the Homebrew Launcher |
| Key | Action |
|---|---|
| SHF | Shift (uppercase) |
| ?#1 / #+= | Symbol layers |
| ABC | Back to letters |
| CTL | Ctrl modifier. Tap CTL, then a letter |
| TAB / ESC / ENT / DEL | Tab, escape, enter, backspace |
The console shows up under /mnt/3ds:
sd the real SD card, read-write
nand CTR NAND, read-only
twl TWL NAND, read-only
hw sensors, power, camera, mic, speakers
sd is the same card the Homebrew Launcher reads, so it's the easiest way to
get files in and out. nand and twl need Luma3DS extended homebrew
permissions. Without those they just don't show up.
hw is just files:
cat /mnt/3ds/hw/battery # 87
cat /mnt/3ds/hw/accel # 12 -4 251 (x y z)
cat /mnt/3ds/hw/info # summary of everything
echo "info 255 0 0" > /mnt/3ds/hw/leds # notification LED (R G B)
cat /mnt/3ds/hw/camera_outer.rgb565 > shot.raw # one 400x240 RGB565 frame
dd if=/mnt/3ds/hw/mic.pcm of=rec.pcm count=100 # signed 16-bit mono, 16kHz
cat music.pcm > /mnt/3ds/hw/audio.pcm # signed 16-bit stereo, 32730HzThere's also charging, gyro, slider_3d, slider_volume, model,
firmware, region, language, steps, wifi, shell, adapter and
battery_voltage. The sensors show up on /dev/input/event0 as evdev axes
too.
mic.pcm never hits EOF, so read it with dd count=… instead of cat.
Grabbing a camera frame freezes the guest for up to a second.
If the 3DS is on WiFi, the guest gets a DHCP lease on 10.0.2.0/24 and
outbound TCP/UDP/DNS goes out through the console. Homebrew can't open raw
sockets, so ping only answers from the gateway (10.0.2.2), and nothing can
connect in.
Guest RAM is whatever the app can get from the 3DS heap: about 54MB on a New
3DS, about 20MB on an Old 3DS. There's another 64MB of swap on
sdmc:/swap.img. An Old 3DS has no spare CPU core so it's slower, and the app
drops the UI to ~8fps to leave more for Linux. The first line of the boot log
tells you which model it detected.
If you get Image too large for RAM, your Image and 3ds-cli.3dsx are from
different releases. Update both.
Needs devkitPro with devkitARM and libctru. The emulator core and the touch keyboard are submodules, so clone with them:
git clone --recursive https://github.com/cmdada/3DS-CLImake # 3ds-cli.3dsx
make cia # also 3ds-cli.cia (needs bannertool + makerom)
make dtb # regenerate the device tree after editing source/3ds-cli.dtsThe kernel and rootfs are built with the Buildroot tree in buildroot/:
wget https://buildroot.org/downloads/buildroot-2024.02.9.tar.gz
tar xf buildroot-2024.02.9.tar.gz && cd buildroot-2024.02.9
make BR2_EXTERNAL=../buildroot O=../out 3ds_defconfig
make O=../out
cd .. && python3 tools/mkimage.py out/images/Image out/images/rootfs.ext4 ImageKernel config is buildroot/board/3ds-cli/linux.config. Packages are in
buildroot/configs/3ds_defconfig. You can also just put a plain kernel Image
and a separate rootfs.ext2 on the SD card, which is easier while iterating.
- Built with devkitARM / libctru
- Powered by mini-rv32ima-mmu, a fork of mini-rv32ima by cnlohr
- Bottom-screen keyboard is ctr-osk-rt, which started life in this repo and is now usable in any 3DS homebrew
GPL-3.0. See LICENSE.
