Lugforth

Ports

Platform Directory Notes
Linux, macOS ports/posix main build; macOS universal binary, SDL2 words tested
Windows ports/windows exe built with MSVC (CI) or MinGW (ninja windows-mingw); SDL2 console (--gui), ls, line editor, ed, writhdeck
Web ports/web browser, SDL2 words on a canvas
Android ports/android one APK: web version or native SDL2 version; command line
ESP32 ports/esp32 Arduino sketch
Raspberry Pi Pico ports/pico USB serial, tools/pico_send.py loads a program
pico-ice ports/pico-ice RP2040 with an FPGA
Game Boy Advance ports/gba prototype, with a roguelike
Raspberry Pi 3/4, QEMU ports/rpi bare-metal AArch64, UART console, sdl2 words (screen, text, images, keyboard/mouse, sound); tested under QEMU virt and raspi3b
PC x86-64 (UEFI) ports/efi bare-metal under UEFI (USB stick), shares the screen, console, input and sdl2 code of ports/baremetal with the Pi; no sound, no mouse under QEMU; tested under QEMU + OVMF

lugforth on Android

Run ninja and ninja web once first (they generate out/gen/posix_boot.h and out/web).

APK with both versions (SDK command line tools and NDK, no gradle)

SDL_SRC=/path/to/SDL2-2.32.x ports/android/build_apk.sh [arm64-v8a|x86_64|armeabi-v7a]
adb install -r out/android/lugforth.apk

One package. A start screen offers:

Both have a hamburger bar at the top: arrows, Tab, Ctrl (tap it, then a letter: Ctrl+S, Ctrl+Q...), a ⋮ button for the examples, text size (A- A+) and help, and back to the start screen. Pinch out or in changes the text size. The console has history (up and down), cursor keys and Tab completion of words and, in the SDL2 version, file names.

The console uses a smooth TrueType font (SDL2_ttf is fetched and built by the script, DejaVu Sans Mono is bundled). SDL_image and SDL_mixer are not built yet, so the SDL2 version has no sprites or sound files (the roguelike and piano roll run only in the web version or on the desktop).

Command line binary (NDK)

ports/android/build.sh aarch64        # also x86_64 for the emulator
adb push out/android/lugforth-aarch64 /data/local/tmp/lugforth
adb shell -t /data/local/tmp/lugforth

Or copy the binary into Termux. No SDL2 words in this mode.


lugforth for the Game Boy Advance (prototype)

Same kernel (common/*.h) and boot chain shape as ports/pico-ice/, built with arm-none-eabi-gcc (288K RAM: 256K EWRAM + 32K IWRAM). Hand-written crt0.s and gba.ld, no libgba or devkitARM. Boot files: gba_boot.fs (tile demo), gba_roguelike_boot.fs (game).

Simulator

LUGFORTH_SIM builds an ordinary POSIX program that boots to the ok prompt and runs the demo gradient:

python3 ../../tools/importation.py -i gba_boot.fs -o /tmp/gba/gen/gba_boot.h \
  -I ../.. -I /tmp/gba --name boot --header cpp
g++ -DLUGFORTH_SIM=1 -O2 -I ../.. -I /tmp/gba -std=c++11 main.c -o /tmp/gba/sim
/tmp/gba/sim

There is no key input in the simulator, so the games cannot be played there.

ROM

Tested under mgba 0.10.2 and on a real machine.

arm-none-eabi-as -mcpu=arm7tdmi crt0.s -o /tmp/gba/crt0.o
arm-none-eabi-g++ -c -mcpu=arm7tdmi -marm -O2 -fno-exceptions -std=c++11 \
  -I ../.. -I /tmp/gba main.c -o /tmp/gba/main.o
arm-none-eabi-g++ -mcpu=arm7tdmi -marm -nostartfiles --specs=nosys.specs \
  -T gba.ld /tmp/gba/crt0.o /tmp/gba/main.o -o /tmp/gba/gba.elf
arm-none-eabi-objcopy -O binary /tmp/gba/gba.elf gba.gba
python3 fix_header.py gba.gba
mgba gba.gba

For the roguelike, use gba_roguelike_boot.fs and write roguelike.gba.

Tiles

python3 ports/gba/convert_tiles.py turns examples/games/sdl2_tiles.png into tiles_data.h: plain RGB15 pixels (mode 3 has no palette), 64 per 8x8 tile; $ffff marks a transparent pixel. platform.fs has draw-tile ( tile x y -- ) and tile-pixel ( tile px py -- color ).

Roguelike

roguelike.fs is examples/games/sdl2_roguelike.fs ported, without music: the same game logic, a scrolling 30x18 viewport over the 40x24 map, d-pad and A/B/START for input. Drawing goes through two C opcodes (GBA-BLIT-TILE, GBA-FILL-RECT), because a per-pixel Forth loop took about 20 seconds per redraw. It only redraws when something changed (dirty flag), otherwise it flickers: mode 3 is a single framebuffer.

Known limits

Design notes

Pitfalls met while porting (more in docs/dev/PITFALLS.md)