Skip to content

MillerTechnologyPeru/swift-embedded-ipod

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

swift-embedded-ipod

Embedded Swift for the iPod Nano 2G, running as Rockbox plugins. Each example under examples/ compiles to a freestanding ARMv4T object (the Nano 2G's FPU-less ARM940T has no OS, no libc, and no Swift runtime beyond what Embedded Swift itself needs) and pairs it with a thin C host that's the actual Rockbox plugin.

  • examples/hello -- the minimal case: one @_cdecl function, one C caller, proof Swift code executes on-device and can call back into Rockbox. Start here.
  • examples/cube -- a Swift port of Rockbox's own apps/plugins/cube.c demo, a rotating wireframe cube. The bigger example: real per-frame math (rotation, projection, backface culling) running in Swift every frame, not just a one-shot call.

Layout

common/            shared by every example
  plugin/            rockbox_shim.{c,h} (Embedded Swift's runtime floor +
                      soft-float sinf/cosf) and the CRockbox module.modulemap
  source/            Rockbox.swift, the Swift-facing wrapper over the shim
examples/<name>/
  plugin/            the example's own C: <name>.c (plugin_start), SOURCES,
                      <name>.make
  source/            the example's own Swift
  Makefile           sets a few variables, includes mk/common.mk
mk/common.mk         the actual build logic (swiftc, audit, archive, Rockbox
                      sync + link, install) -- shared, not per-example
rockbox/             pinned Rockbox checkout (gitignored, shared build input)
build/<name>/        build output per example (gitignored)

How it fits together

Rockbox plugins are C: the OS calls plugin_start(), and every system service (display, buttons, files) goes through an rb-> function-pointer table passed in at load time. Each example's plugin/<name>.c is that entry point; it calls into the example's Swift (@_cdecl functions) and uses what comes back -- proof the Swift code actually executed on the device, not just linked.

The round trip goes both ways. common/plugin/rockbox_shim.h declares the C surface Swift calls into -- rb_splash (a fixed-arity wrapper around rb->splash, since Embedded Swift can't call a variadic C function directly) plus sinf/cosf for examples doing their own math -- and common/plugin/module.modulemap exposes that header to swiftc's Clang importer as the CRockbox module. common/source/Rockbox.swift wraps the Rockbox-specific part of that (not the plain math) in a small Swift-facing namespace, Rockbox.splash(...), shared by every example. This mirrors the CRockbox module / Rockbox wrapper split celeste-swift's ports/iPod uses for its own (much larger) Rockbox port.

Even a function as trivial as hello's n &+ 42 references a small, fixed set of runtime symbols Embedded Swift always needs on this target regardless of what it calls: a memory allocator, a handful of atomic-refcount primitives (harmless as plain, non-atomic operations here -- everything Swift touches runs on the plugin's single main thread), a couple of EABI memory-clear helpers, and a stack-protector pair. common/plugin/rockbox_shim.c provides all of that -- nothing about any one example's logic drives its size, it's Embedded Swift's baseline floor on armv4t-none-none-eabi.

Building

Requires a Swift toolchain with a prebuilt Embedded Swift stdlib for armv4t-none-none-eabi (swift-6.3.2-RELEASE or newer), and Docker on macOS (the arm-elf-eabi-gcc 9.5 cross toolchain can't be built with an arm64 Darwin host; on native Linux it builds directly). The Rockbox checkout and cross toolchain are shared across every example -- building a second example after the first is much faster.

make list                                 # show available examples
make EXAMPLE=hello toolchain               # once, ~40 min (or natively on Linux)
make EXAMPLE=hello rock                    # -> build/hello/hello.rock
make EXAMPLE=hello install IPOD=/Volumes/<your-ipod>

(EXAMPLE defaults to hello; equivalently cd examples/hello && make rock.)

hello

Plugins → Applications → hello. Flashes "Hello from Swift!" for one second (Swift calling into Rockbox), then "Swift says: 42" for three seconds (Rockbox displaying what Swift returned).

cube

make EXAMPLE=cube rock
make EXAMPLE=cube install IPOD=/Volumes/<your-ipod>

Plugins → Demos → cube. Controls (click wheel):

Button Action
LEFT / RIGHT switch which axis (x/y/z) the wheel controls
scroll wheel adjust that axis's spin speed (or angle, if paused)
MENU (hold) cycle draw mode: solid / hidden-line / wireframe
PLAY pause/resume rotation
SELECT (release) toggle high-speed spin
MENU (release) quit

examples/cube/source/Cube.swift is the entire 3D pipeline -- rotation matrix, perspective projection, backface culling, and (for SOLID) a from-scratch scanline triangle rasterizer calling Rockbox.hline once per row -- running every frame; examples/cube/plugin/cube.c is the button/loop/draw-call host, ported from the original almost line-for-line. Two differences from the original: plain Float + the shared shim's sinf/cosf stand in for its Q14 fixed-point trig (the ARM940T has no hardware float either way, so fixed-point bought the original nothing dtype-safety wouldn't also give here), and SOLID's fill doesn't use Rockbox's lib/mylcd+lib/grey plugin helper libraries the original's does -- pulling those into the build wasn't worth it for what this repo is demonstrating, and a from-scratch rasterizer is a better showcase of Swift calling back into Rockbox per-scanline than a library call would be anyway.

Adding an example

  1. mkdir -p examples/<name>/{plugin,source}
  2. Write source/<Name>.swift with at least one @_cdecl entry point. import CRockbox to call rb_splash/sinf/cosf, or the Rockbox.* wrapper for Rockbox-specific calls (see common/source/Rockbox.swift).
  3. Write plugin/<name>.c (plugin_start, calling into your Swift), plugin/SOURCES (list <name>.c and rockbox_shim.c), and plugin/<name>.make (copy examples/hello/plugin/hello.make, substituting the name).
  4. Write examples/<name>/Makefile (copy examples/hello/Makefile, setting EXAMPLE/MODULE_NAME/CATEGORY/EXAMPLE_SWIFT_SRC).
  5. make EXAMPLE=<name> rock.

No UI-simulator target -- every example here is intentionally device-only. A simulator build needs a host-architecture stand-in for the Swift object (Embedded Swift can't link armv4t code into a host x86_64/arm64 binary); see celeste-swift's ports/iPod, which uses a small C stub for exactly that, for the pattern if an example here needs it.

About

Embedded Swift for iPod Classic

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors