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:
- Web version: the web build in a WebView (terminal, Breakout, Drums, Roguelike).
- SDL2 version: the native binary inside
SDLActivity, with an on-screen console. The examples are copied on first start toAndroid/data/org.lugforth.app/files/examples(never overwritten, so your files stay) and the console runs there.
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/simThere 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.gbaFor 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
- Boot is slow: there is no saved image, so the whole boot chain is interpreted from source at each power-on (16.78 MHz ARM7TDMI, no cache).
- Mode 3 software blits, not the tile and sprite hardware.
- No sound.
FREEis a no-op andRESIZEalways allocates a new block (bump allocator).
Design notes
- Registers and VRAM are memory-mapped, so
builtins.honly has opcodes for what needs C: a debug print,MS-TICKS, the allocator and the blits. - The cell is 4 bytes on the ARM (8 in the simulator), so 16-bit
registers are written with
h!/h@. - The platform file must define
okitself:common/fini.fsruns it last.
Pitfalls
met while porting (more in docs/dev/PITFALLS.md)
crt0.smust copy.datato RAM as well as clear.bss. Missing, newlib's state was garbage and the screen stayed white.- In an emulator without a GPU the demo can take 20-30 seconds to draw; do not conclude too early that the screen is blank.
- A boolean chain needs one
or/andper pair of terms, or a flag stays on the stack. - A
{ local }declared inside ado ... loopbody corrupts the loop: locals use the return stack and are only cleaned at;. - A nested
( paren )inside a( comment )ends the comment early. - A non-Copyright comment before the first
needsof a boot entry file ends up in the boot text before comments exist.