dart_emu 0.3.0
dart_emu: ^0.3.0 copied to clipboard
A RISC-V system emulator (RV64 and RV32) ported from TinyEMU. Boots Linux with stream-based I/O for CLI, Flutter, and web applications.
dart_emu #
A RISC-V system emulator for Dart, ported from TinyEMU. Boots Linux with stream-based I/O for embedding in CLI, Flutter, and web applications.
Features #
- RV64IMAFDC and RV32IMAFDC instruction sets with full privilege levels (Machine, Supervisor, User)
- SV39 (RV64) and SV32 (RV32) virtual memory with hardware page table walking
- RV32 runs on all Dart platforms including web (no 64-bit integer dependency)
- VirtIO console, block device, and network device
- User-mode networking with DNS, DHCP, and TCP/UDP proxy
- Stream-based facade for platform-agnostic embedding
- Lifecycle status tracking via
EmulatorStatus - YAML-based machine configuration with ZIP bundle support
- Supports both file-path and in-memory BIOS/kernel loading
Library Usage #
import 'package:dart_emu/dart_emu.dart';
final config = MachineConfig(
xlen: Xlen.rv64, // or Xlen.rv32 for 32-bit (web-compatible)
biosData: biosBytes, // Uint8List
kernelData: kernelBytes, // Uint8List
cmdLine: 'console=hvc0 root=/dev/vda rw',
blockDevices: [MemoryBlockDevice.fromData(rootfsBytes)],
ethDevices: [UserNetDevice()], // user-mode networking
);
final emulator = Emulator(config);
// Listen to console output
emulator.output.listen((bytes) => handleOutput(bytes));
// Track lifecycle
emulator.status.listen((status) => print('Status: $status'));
// Send console input
emulator.sendInput(inputBytes);
// Run (completes when guest shuts down or stop() is called)
await emulator.start();
// Clean up
await emulator.dispose();
Flutter / Web Integration #
Use Xlen.rv32 for web targets. RV32 avoids 64-bit integer operations that
are unsupported in JavaScript.
final config = MachineConfig(
xlen: Xlen.rv32,
biosData: biosBytes,
kernelData: kernelBytes,
cmdLine: 'console=hvc0 root=/dev/vda rw',
blockDevices: [MemoryBlockDevice.fromData(rootfsBytes)],
ethDevices: [UserNetDevice()],
);
final emulator = Emulator(config);
emulator.output.listen((bytes) {
terminalController.write(bytes);
});
emulator.status.listen((status) {
setState(() => _status = status);
});
emulator.start(); // runs in the event loop
See the example directory for a complete Flutter app with a terminal UI, config picker, and ZIP bundle loading.
CLI Usage #
Install globally:
dart pub global activate dart_emu
Run with a configuration file:
dart_emu run --config data/alpine_vm.yaml
Run with individual options:
dart_emu run --bios bbl64.bin --kernel kernel-riscv64.bin --drive rootfs.bin
Web Platform #
The RV32 configuration is fully web-compatible. It uses Uint32List for
integer registers and ByteData-backed storage for FP registers, avoiding
all 64-bit integer APIs (Int64List, getUint64, setUint64) that are
unsupported in JavaScript.
Build the example for web:
cd example
flutter build web --release
To skip the config picker and boot the demo directly, add ?boot=32 to the
URL.
Building Root Filesystems #
Docker-based image builders are included for creating rootfs images.
RV64 (Alpine Linux):
tool/image_builder/build.sh riscv64 # minimal (256MB)
tool/image_builder/build.sh riscv64 dev # with gcc, make, git, nano (512MB)
RV32 (Buildroot + musl):
tool/image_builder/build_buildroot.sh # minimal (256MB)
tool/image_builder/build_buildroot.sh dev # with tcc, make, git, nano (512MB)
The RV32 dev image includes TCC (Tiny C Compiler) instead of GCC for practical compile times inside the emulator.
Images are packaged as ZIP bundles in data/ that the Flutter app can load
via drag-and-drop or file picker.
Configuration #
Machine configuration uses YAML files:
version: 1
machine: riscv64
memory_size: 256
bios: bbl64.bin
kernel: kernel-riscv64.bin
cmdline: "console=hvc0 root=/dev/vda rw"
drive0:
file: rootfs/alpine-riscv64-rootfs.bin
eth0:
driver: user
For RV32:
version: 1
machine: riscv32
memory_size: 256
bios: bbl32.bin
kernel: kernel-riscv32.bin
cmdline: "console=hvc0 root=/dev/vda rw"
drive0:
file: rootfs/alpine-riscv32-rootfs.bin
eth0:
driver: user