Skip to content

MicroPython and BASIC

Ruby is the language Family mruby is built around, but it is not the only one that runs on it. There are four: Ruby, MicroPython, BASIC and Lua.

They are not modes you switch into. A .py file and a .bas file sit in the launcher next to the Ruby apps, start the same way, and run at the same time as each other.

Three windows on one desktop: a Ruby demo, a Lua demo and a Python demo, all running
Three VMs at once — Ruby, Lua and MicroPython, each in its own window and its own heap
Extension Runs on Notes
.rb PicoRuby The main language. Everything in the API Reference
.py MicroPython The same app framework as Ruby: windows, drawing, sprites, sound. One Python app at a time
.bas FMRuby BASIC Family BASIC compatible. Its own text screen and sprites
.lua Lua 5.4

MicroPython

A .py file is an app like any other. It uses the same framework Ruby apps use — subclass FmrbApp, override the lifecycle methods, start it:

class PythonDemoApp(FmrbApp):
    def on_create(self):
        Log.info("started on " + self.platform)
        self.draw_window_frame()

    def on_update(self):
        return 500          # ms until the next turn

    def on_event(self, ev):
        super().on_event(ev)
        if ev.get("type") == "mouse_up" and ev.get("button") == 1:
            self.next_page()

app = PythonDemoApp()
app.start()

Windows, events and drawing are reachable through the built-in _fmrb module, wrapped in the framework classes an app subclasses. FmrbApp, FmrbGfx, FmrbAudio, SpriteImage, SpriteInstance and Log are already in the app's namespace — there is nothing to import for them.

The demos are /app/python/python.app.py (the twin of the PicoRuby demo, page by page) and /app/game/breakout/breakout.app.py, a whole game: sprites for the things that move, tiles for the things that do not, Japanese text, a tune on the main sound chip with effects on the other. Robo Explorer has a Python pilot as well.

Where it differs from Ruby

Ruby Python
Time Machine.board_millis ticks_ms()
String length String#length counts characters len() counts bytes
Another file require "/app/..." import mymodule — beside the app, or /usr/lib/python
The framework, from that file Visible Not visible. Pass what it needs as arguments
A timer's callback A block A function: self.set_timer(500, self.blink)

Splitting an app across files works, but the framework classes live in the app's namespace and not in the module's:

# in the app
import mypanel
mypanel.draw(self, state)

# in mypanel.py
def draw(app, state):
    app.gfx.draw_text(...)   # reached through the app, not named directly

One file is limited to 64 KB.

Sound

The sound chip is reached through FmrbAudio, the same shape as in Ruby. A tune goes on the main instance and short effects on the other, so an effect does not stop the music:

audio = FmrbAudio(self)
audio.load_fmsq_file(1, "/cache/app/mygame/bgm.fmsq")   # sync_file it across first
audio.play_slot(1, FmrbAudio.MAIN)
audio.note_on(FmrbAudio.CH_PULSE2, 988, 12, 2, 0)       # an effect, on the other

Time an effect's end by ticks_ms(), not by counting frames: a heavy frame stretches the sound.

Limitations

MicroPython's design pushes back in a few places, and these are worth knowing before you start:

One Python app at a time. MicroPython keeps its entire VM state in globals, so unlike mruby and Lua it cannot be instantiated twice. Starting a second one is refused at spawn with "Another Python app is already running." Ruby, Lua and BASIC apps are unaffected and can run alongside it.

Built-in modules only. array, builtins, collections, gc, io, math, micropython, struct, sys and random are in. time, json, os, re, binascii, hashlib, heapq and deflate are not — they live in MicroPython's extmod/, which this build does not carry. For waiting, return a delay from on_update rather than sleeping. (random is seeded from the clock, so a run differs from the last; call random.seed(n) to repeat one.)

Files can be read, not written. open() exists but raises OSError — failing out loud beats being silently absent. _fmrb.read_file(path) returns the whole file as bytes (up to 64 KB) and _fmrb.file_size(path) its size. io.StringIO and the rest of the in-memory objects work.

Strings are bytes. This build has no Unicode strings, so len("日本語") is 9 and indexing is by byte. For a width in pixels use FmrbGfx.text_width, which walks the UTF-8.

No REPL and no threads. Creating tasks is the system's job, not a guest VM's; use a generator for concurrency inside an app.

256 KB of heap per app, fixed. Running out raises MemoryError, and an uncaught one ends the app with a traceback in the log, exactly as running out of memory does in Ruby.

A force-stop skips on_destroy. Stopping an app that is inside a long Python loop unwinds the bytecode, so neither destroy nor on_destroy runs. Resources are reclaimed by the C side either way, but an app cannot rely on on_destroy to save anything — do that at a boundary in on_update. Lua behaves the same way.

Not provided: tile map classes (draw_tile is there — lay them out yourself), image masks, GfxBlock and the other drawing optimisations, arcs, get_pixel, extra canvases, the p5 layer, the microphone and MIDI out.


FMRuby BASIC

A BASIC interpreter built to be compatible with Family BASIC — the BASIC that shipped for the Famicom — down to its screen, its sprites and its sound statements.

This is not Ruby with a BASIC syntax on top: it is a separate interpreter written in C++, with Family BASIC's semantics, its 28 x 24 character screen, and its PLAY / BEEP sound.

Running a BASIC program

Write it in the editor and press F5

The quickest route. Open the Editor, type the program, press F5.

  • If the file has no name yet you will be asked for one. Save it under /home or /app — programs elsewhere will not run
  • Ctrl + Q returns you to the editor from a running program, even a fullscreen one. Then F5 runs it again

Put it in the launcher

Drop a .toml next to the .bas with the same name:

/app/basic/mygame.app.bas
/app/basic/mygame.app.toml
app_handle_name = "mygame"
app_screen_name = "My Game"
app_screen_name_ja = "マイゲーム"
# .bas starts fullscreen unless you ask for a window:
#default_window_mode = "window"

The launcher builds its list when the desktop starts, so right-click inside the launcher to rescan after adding a file.

A .bas without a .toml still runs from F5 or from the shell; it just uses the filename as its name.

The screen

Family BASIC's screen is fixed at 28 characters by 24 lines (224 x 192 pixels). Started fullscreen, it is centred and the surrounding area is filled with black — the same shape the original had.

The maze sample running fullscreen: a 28x24 character screen centred on black
The maze sample from /app/basic, drawn entirely out of characters

What is in it

The language core, the text screen, sprites with automatic movement, controller input, PLAY and BEEP, character tables and palette selection, error handling and SAVE are all implemented. Three sample programs ship in /app/basic — shoot, maze and music — beside the BASIC app demo, which is a BASIC program launched as an ordinary app.

Compatibility

Every known difference from Family BASIC V3 is written down and classified — resolved, deliberate difference, waiting on measured data, or out of scope. Some deliberate choices worth knowing:

  • IF expr THEN stmt skips the : statements after it when the condition is false (the Microsoft-family behaviour)
  • PLAY is asynchronous, so music continues while the program runs
  • LOAD / LOAD? do nothing inside a program — on the original they were direct-mode commands. SAVE is implemented
  • Ctrl + Q stops a running program. The original had no way out of a fullscreen program

Its MML is not the MIDI MML

BASIC's PLAY uses Family BASIC's MML syntax. The MIDI layer has its own MML for Ruby apps. They are separate implementations and the dialects differ — do not copy a string from one to the other and expect it to play.


Which one to reach for

  • Ruby for anything that needs the full API — networking, MIDI, sprites, the peripheral bus. This is the language the system is designed around
  • MicroPython if Python is what you know. Expect a smaller standard library than you are used to, and one Python app at a time
  • BASIC if you want the Family BASIC experience, or you are following a listing from a magazine of the era
  • Lua for a small, fast script