Skip to content

eliot-akira/ulisp-wasm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

uLisp on WebAssembly

Screencast

uLisp is a programming language for microcontrollers and embedded devices. This project ulisp-wasm is a port of uLisp to C99 and WebAssembly that runs in the browser, on servers and the terminal command line.

See the Playground page and introduction post on the forum.

Current state

Early stage but the basics are working.

  • Parse and evaluate a Lisp expression. The runtime on Wasm has its own process thread with 64K of memory, and an event loop that optionally yields control to the host on every instruction.
  • Pass the entire test suite in the uLisp builder.
  • Run uLisp interpreter and REPL across platforms: plain C build, Wasm runtimes, Node, Bun, browser; Linux laptop, Raspberry Pi, ESP32.

Changes

Based on uLisp builder, uLisp ESP32, BL602 RISC-V fork

  • Rewrite and upgrade from uLisp 3.6 to 4.6b, 4.7, 4.8d
  • Load code and step through each instruction
  • Replace devices with host events for emulation
    • Analog port read/write
    • Digital port read/write
    • Serial interface
    • Graphics
    • Audio
    • LittleFS
    • EPROM read/write
    • Load/save/autorun image
    • I2C interface - See function with-i2c
    • SD card
    • Extended RAM - Define BOARD_HAS_PSRAM
  • REPL (read-eval-print loop) in web interface
  • REPL on server side
  • REPL and line editor in uLisp using serial interface
  • CLI on server side to run uLisp programs
  • Run in the browser
  • Run on JavaScript runtimes: Node, Bun, Deno
  • Run on WASM runtimes with WASI support
  • Standalone executable
  • Documentation
  • Web interface
    • Code editor with syntax highlight
    • Console output
    • Canvas
    • SVG
    • Web Audio

Develop

Prerequisites:

  • Docker to run Emscripten in a container; or directly use emcc from Emscripten SDK (install options)
  • Bun - JS runtime and build tool
  • Clang - Optional: Compile to local platform
  • Zig - Optional: Cross-compile to all supported platforms

See package.json for available CLI commands.

Install

git clone https://github.com/eliot-akira/ulisp-wasm
cd ulisp-wasm
bun install

Start

Build frontend app as static site, watch files for changes, rebuild and reload page.

bun run start

To run the above and develop/rebuild the C source in parallel:

bun run dev

Build

Build for production with minified assets.

bun run build

Below scripts are ways to run functions in build.ts.

bun build.ts [command] (...options)

Build targets

bun build:node # Node.js target: Wasm library and CLI
bun build:site # Web playground site
bun build:web  # Web target
Cross-platform builds

Requires Bun. Optionally Clang for native build, Zig to cross-compile.

bun build:bun  # Single-file executable with Bun runtime
bun build:cli  # CLI/REPL as native C binary

These produce binaries in the build folder for the following targets.

  • linux-arm64
  • linux-x64
  • macos-arm64
  • macos-x64
  • windows-x64 *
  • windows-arm64 *

* Windows support is partial: only Bun build on x64. REPL uses a readline library with terminal I/O, which doesn't exist on Windows. Possibly CLI could exclude the feature based on build target.

Experimental
bun build:wasi # Wasm target with WASI (WebAssembly System Interface)
bun build:zig  # Zig port of ulisp-c

Code organization

The project is a monorepo with a number of subprojects. The main ones are c99, site, and web.

  • arm - uLisp for ARM processors
  • arm-assembler - ARM assembler
  • arm-compiler - ARM compiler
  • avr - uLisp for AVR processors
  • bignums - Arbitrary-precision extension
  • builder - Original builder using Common Lisp
  • c99 - C99 port of uLisp
  • cli - Command-line interface
  • esp - uLisp for ESP32
  • examples - Example code collection
  • node - Node.js version using uLisp Wasm
  • riscv - uLisp for RISC-V processors
  • riscv-assembler - RISC-V assembler
  • riscv-compiler - RISC-V compiler
  • site - Web playground site
  • tests - Test suite
  • wasi - Wasm port for runtimes with WASI (WebAssembly System Interface)
  • web - Web version based on uLisp Wasm
  • zero - uLisp Zero is a minimal Lisp implementation for reference
  • zig - Zig port of uLisp automatically translated from C

ulisp.c

From documentation of uLisp builder

  • preface.lisp - the C macros and constant definitions
  • utilities.lisp - the C typedefs, global variables, and utility functions
  • saveload.lisp - the definitions for save-image, load-image, and autorun
  • assembler.lisp - the assembler for the ARM and RISC-V platforms
  • prettyprint.lisp - the prettyprinter
  • postscript.lisp - the definitions for the function table lookup, eval, read, print, and the REPL

Platform-specific features

  • The source file preamble
  • The platform-specific settings: WORKSPACESIZE, etc.
  • The stream definitions: I2C, SPI, serial, etc.
  • The analogread and analogwrite I/O pin definitions
  • Definitions for note, sleep, and keywords

Builder steps

From ulisp-builder/build.lisp

  • Header
  • Workspace
  • Macros
  • Constants
  • Typedefs
  • Enum declarations
  • Global variables
  • Error handling
  • Setup workspace
  • Make objects
  • Utilities
  • Feature list
  • Garbage collection
  • Compact image
  • Make filename
  • Save image
  • Tracing
  • Helper functions
  • Association lists
  • Array utilities
  • String utilities
  • Closures
  • In place
  • I2C interface
  • Stream interface
  • Note
  • Sleep
  • Pretty print
  • Assembler
  • Interrupts
  • Function definitions
  • Symbol names
  • Documentation strings
  • Built-in symbol lookup table
  • Eval
  • Print functions
  • Read functions
  • Setup 1 & 2
  • REPL
  • Loop

Reference

Streams

See the article on Streams on the uLisp site.

Stream Streamtype Description
SERIALSTREAM 0 Reading from and writing to a Serial interface
I2CSTREAM 1 Reading from and writing to an I2C device
SPISTREAM 2 Reading from and writing to an SPI device
SDSTREAM 3 Reading from and writing to SD cards
WIFISTREAM 4 Reading from and writing to Wi-Fi protocols
STRINGSTREAM 5 Reading from or writing to a Lisp string
GFXSTREAM 6 Writing text to a TFT colour display

Graphics

uLisp has graphics methods that use the Adafruit GFX Library with TFT LCD displays.

// Color definitions
#define BLACK    0x0000
#define BLUE     0x001F
#define RED      0xF800
#define GREEN    0x07E0
#define CYAN     0x07FF
#define MAGENTA  0xF81F
#define YELLOW   0xFFE0 
#define WHITE    0xFFFF

draw-bitmap

drawBitmap(x, y, canvas.getBuffer(), 128, 32, foreground, background)

draw-char

(draw-char x y char [colour background size]) Draws the character char with its top left corner at (x,y). The character is drawn in a 5 x 7 pixel font in colour against background, which default to white and black respectively. The character can optionally be scaled by size.

draw-circle

draw-line

draw-pixel

void drawPixel(uint16_t x, uint16_t y, uint16_t color);

draw-rect

draw-round-rect

draw-triangle

fill-circle

fill-rect

fill-round-rect

fill-screen

fill-triangle

get-text-bounds

getTextBounds(string, x, y, &x1, &y1, &w, &h)

init(width, height)

invert-display

set-dursor

set-rotation

Sets the display orientation for subsequent graphics commands; values are 0, 1, 2, or 3.

set-text-color

set-text-size

set-text-wrap

I2C interface

  • I2Cinit
  • I2Cread
  • I2Cwrite
  • I2Cstart
  • I2Crestart
  • I2Cstop