userland.rundocs70Try the demo
Reference

Host API

The boundary between the RISC-V runner's WebAssembly module and its JavaScript host is small: one shared memory and five function imports, 78 exported functions, and a shared-memory protocol for file I/O.

SDK · JavaScript

Inspect the module

Reads the import and export lists straight from /nano/nano.wasm with the WebAssembly JS API.

javascript
const mod = await WebAssembly.compileStreaming(fetch("/nano/nano.wasm"));
const imports = WebAssembly.Module.imports(mod);
const exports = WebAssembly.Module.exports(mod);
print(imports.length, "imports:", imports.map((i) => `${i.name}(${i.kind})`).join(" "));
print(exports.length, "exports,", exports.filter((e) => e.kind === "function").length, "functions");
print(exports.filter((e) => e.name.startsWith("vm_")).length, "vm_* exports, e.g.", ["vm_create", "vm_step", "vm_load_elf", "vm_inject_connection"].filter((n) => exports.some((e) => e.name === n)).join(" "));
loading…
Expected output
6 imports: memory(memory) abort_js(function) emscripten_date_now(function) emscripten_random(function) debug_log(function) console_write(function)
80 exports, 78 functions
52 vm_* exports, e.g. vm_create vm_step vm_load_elf vm_inject_connection

Imports

Import
Signature
Purpose
memory
shared WebAssembly.Memory
192 MiB initial, 2 GiB maximum
console_write
(fd, ptr, len)
guest stdout/stderr; also feeds the terminal grid
debug_log
(val)
diagnostics (per-syscall events in trace builds)
abort_js
() → !
panic handler
emscripten_random
() → f32
entropy for getrandom
emscripten_date_now
() → f64
wall clock for clock_gettime and timers

Exports

Group
Exports
Lifecycle
vm_create, vm_init, vm_step, vm_load_elf, vm_load_raw, vm_exit_code, vm_signal
Memory info
vm_ram_ptr, vm_ram_size, vm_struct_ptr, vm_struct_size (12,680 bytes)
File I/O protocol
vm_fs_request_ptr, vm_fs_response_ptr, vm_fs_path_ptr, vm_fs_path2_ptr, vm_fs_path_cap, vm_thread_fs_request_ptr, vm_thread_fs_response_ptr, vm_io_retry, vm_note_host_write
Threads
vm_alloc_thread, vm_thread_step, vm_fork_return, vm_shared_efd_ptr, vm_epoll_timeout_ms
Virtual server
vm_inject_connection, vm_read_response, vm_close_connection, vm_sockets_ptr, vm_sockets_size
Terminal and stdin
term_feed, term_reset, term_cells_ptr, term_cols, term_rows, term_cursor_row, term_cursor_col, …, vm_stdin_push, vm_stdin_eof, vm_stdin_set_interactive, vm_tty_enable, vm_tty_set_size
Snapshots
vm_evloop_dump, vm_evloop_load, vm_evloop_size, vm_signals_dump, vm_signals_load, vm_snapshot_restore_reset, vm_blocks_ptr, vm_blocks_size, vm_reset_blocks
Bundled binaries
vm_bundled_busybox_ptr/_size, vm_bundled_node_ptr/_size, vm_bundled_devenv_ptr/_size, vm_bundled_elf_ptr/_size (0 when not embedded)
Debug and counters
debug_pc, debug_reg, debug_status, debug_read_guest, debug_fault_pc, debug_fault_addr, debug_block_*, debug_baseline_insns, …
Allocator
malloc, free (bump allocator; free is a no-op)

The FS_PENDING protocol

File syscalls don't call the host per operation. The guest fills a request block (syscall number, fd, three arguments, buffer pointer and length, two 256-byte paths), sets the VM status to 6 (FS_PENDING) and returns from vm_step. The host performs the operation on the in-memory filesystem, writes the result into register a0, resets the status to 0 and calls vm_step again.

javascript
// host loop, simplified
for (;;) {
  const status = step(vm, budget);
  if (status === FS_PENDING) { completeFsRequest(vm); continue; }   // 6
  if (status === EPOLL_BLOCKED) { setA0(vm, -EINTR); await tick(); continue; } // 7
  if (status === FAULT) break;                                       // 3: exit or trap
}
Status
Meaning
0 (OK)
ready to run
3 (FAULT)
the process exited or trapped
6 (FS_PENDING)
waiting for the host to complete a file request
7 (EPOLL_BLOCKED)
no runnable thread; the host lets time pass and injected connections arrive, then resumes with -EINTR
18 (RUNNING)
executing

The RISC-V emulator feature page shows the runner in action. The guest-memory layout and the 12,680-byte VM struct are on RISC-V runner. Most apps never touch this layer; the SDK and the raw API wrap it.