Skip to content

Releases: adafruit/Adafruit_Arcade_Machines

v2.2.0 — src/arch folders, and the TLV320 configured via the Adafruit library

Choose a tag to compare

@jedgarpark jedgarpark released this 09 Sep 17:54
4c9731e

Every .uf2 differs from v2.1.0 — the DAC configuration was reimplemented on top of Adafruit's library, worth about +9.4 KB per game. The audio itself is unchanged, and that was checked on hardware rather than assumed: five games, five distinct synthesis paths, each measured against its own pre-change capture and listened to.

This completes a review of the project's hardware dependencies. v2.1.0 covered storage and portability; this one covers audio and the architecture layout.

src/arch/ — architecture, separated from board

Two layers that had been tangled together:

example
arch the silicon fast-RAM placement, timer registers, PIO, DMA
board the product which pins, which DAC and its address, which display
src/arch/arch.h                     dispatcher and the contract
src/arch/rp2040/rp2040.h            placement and time sources
src/arch/rp2040/arch_audio_i2s.*    the PIO + DMA I2S transport
src/arch/esp32/esp32.h              stub — untested, and labelled as such

A Fruit Jam and a Feather RP2350 are two boards on one architecture; an ESP32 board is a different architecture as well. Adding one is a folder plus a single #include — nothing in src/cpu/ or src/machines/ should need to change.

This is groundwork, not ESP32 support. A working ESP32 target still needs a board backend, and the video path is the hard part: the Fruit Jam drives DVI over the RP2350's HSTX with the second core parked in hal_video_run(), which has no ESP32 equivalent.

The TLV320 is configured through Adafruit's library

Roughly 45 hand-written register pokes became typed calls into Adafruit_TLV320_I2S. The I2S transport — a PIO state machine and a chained DMA pair — stays hand-written and moved to src/arch/rp2040/, because no library replaces it: that library is I2C configuration only, with no data path. Which is the good outcome, since it means none of the interrupt-facing code was touched.

The configuration is a translation, not a redesign: every step is the same operation in the same order, checked against the library's own example.

Verified on hardware

game audio type
Lunar Rescue WAV samples + bit-banged speaker ISR 151→150 µs, 0 samples dropped
Burger Time two emulated AY-3-8910 PSGs 0 audio underruns
Galaga Namco WSG + 54XX — the busiest mix ISR average identical; worst 295→289 µs
Pac-Man Namco WSG starve 0, queue floor unchanged
Space Invaders WAV with the looping UFO channel work 5,331→5,299 µs

All five confirmed by ear as well — every sound present, volume correct. Serial can only show the DAC was configured without complaint, not that it was configured correctly.

Also: 13 of 13 examples build, all 8 host harnesses build, REUSE 182/182, arduino-lint --library-manager submit reports 0 errors.

Flashing

Hold BOOT while connecting USB (or hold BOOT and tap RESET), then copy the .uf2 onto the RP2350 drive. The binaries contain no ROM data — you still need a microSD card with legally-obtained ROMs, laid out as each game's README describes.

v2.1.0 — storage on SdFat, and src/cpu + src/machines de-picoed

Choose a tag to compare

@jedgarpark jedgarpark released this 09 Sep 05:56
4067965

The firmware really is different this time. Unlike v2.0.0, which was a pure packaging release, every .uf2 here differs from the previous one — the SD card layer was replaced. Behaviour should be identical, and was verified on hardware across five games, but these are new binaries rather than the same bytes under a new name.

Two changes, both answering a review of the project's hardware dependencies.

Storage now uses SdFat

The vendored copy of FatFs and a hand-rolled SPI SD driver are gone — 24,102 lines deleted, 79 added. SdFat - Adafruit Fork is declared in library.properties, so Library Manager installs it for you.

Nothing above the board layer changed: game code still sees the same six storage functions, and still touches the card exactly once, at boot.

Three things were kept deliberately rather than re-derived:

  • 12.5 MHz SPI, the speed the old driver settled on — a number this project has actually run cards at.
  • Long filenames. The ROM loaders sort the names the card reports, so quietly switching to 8.3 names would have changed which ROM lands at which address — a booting machine running scrambled ROM, which looks like a corrupt dump rather than a storage bug.
  • The two-slot file pool, so no dynamic allocation.

The card-detect pin is no longer used; the old driver configured it and then never read it, and a missing card already surfaces as a failed mount. This library's private SD pin defines are also gone in favour of the ones the arduino-pico Fruit Jam variant already exports.

Cost: about +12 KB of program space and +1.7 KB of RAM per game. The tightest, Burger Time, goes from 62.0% to 62.4% of RAM with 197 KB still free.

src/cpu and src/machines are no longer Pico-specific

A CPU interpreter and a game's memory map have no business naming a vendor SDK. Both layers now contain zero executable references to the Pico SDK; a single new header, src/arcade_portability.h, is the only file outside src/boards/ that knows which chip this is. It provides code placement and two time sources, with arms for RP2040/RP2350, ESP32 and a portable fallback.

Adding a platform is now an arm in that header plus a board backend, rather than a hunt through two layers. The ESP32 arm is written but untested — no ESP32 port exists yet.

This is a refactor with no functional effect: every game is byte-identical across it.

Verified

Five games flashed and confirmed on real Fruit Jam hardware, each exercising a different storage path: Donkey Kong (ROMs, PROMs and the sound-CPU ROM), Galaga (its own 3-CPU ROM checksum passing, and a 14-file directory listing), Ms. Pac-Man (four different filename shapes in one sorted listing, plus the encrypted aux-board decode), Space Invaders (two directories and 88 KB of WAV samples — the only large sequential reads here), and Burger Time (the RAM ceiling, under gameplay, with its AY-3-8910 audio correct).

Also: 13/13 examples build, all 8 host harnesses build, reuse lint 178/178, arduino-lint --library-manager submit reports 0 errors, and invaders_host's 6000-frame scripted-play digest is bit-identical to the stored reference.

Also recorded

extras/DEVNOTES.md #103 documents a measurement that came out of this work and contradicted an existing assumption: moving a CPU interpreter into SRAM, listed in PORTING.md as the project's biggest performance lever, made Space Invaders 5.0% faster and Lunar Rescue 2.5% slower — same core, same one-line change, both results above 8 sigma. It was reverted, and PORTING.md no longer states that lever as an unqualified win. A technique proven on one machine here is not thereby proven for the next.

Flashing

Hold BOOT while connecting USB (or hold BOOT and tap RESET), then copy the .uf2 onto the RP2350 drive. The binaries contain no ROM data — you still need a microSD card with legally-obtained ROMs, laid out as each game's README describes.

v2.0.0 — one Arduino library, renamed Adafruit Arcade Machines

Choose a tag to compare

@jedgarpark jedgarpark released this 08 Sep 17:31
9480ee9

A packaging release. The firmware is unchanged from v1.2.0 — six of the seven .uf2 files are byte-identical in size to the v1.2.0 assets, and dkong_fruitjam is 512 bytes smaller (one UF2 block, from shorter __FILE__ paths after the source moved). No new games, no gameplay or timing changes. If v1.2.0 is running well for you, there is nothing here you need.

What changed is how the project is packaged, in response to a review of the old layout.

It is now one Arduino library

The repo used to be an Arduino sketchbook — 13 top-level sketch folders, and an install step telling you to point Preferences → Sketchbook location at the checkout. That could not be installed, could not go to Library Manager, and never appeared under File → Examples.

src/Adafruit_Arcade_Machines.h    umbrella header
src/hal/  src/cpu/  src/machines/  src/boards/
examples/Games/       the seven games
examples/SelfTest/    per-subsystem bring-up sketches
extras/               development notes, host harnesses, release staging

Install it like any other library and open File → Examples → Adafruit Arcade Machines → Games. Its one dependency, PicoDVI - Adafruit Fork, is declared in library.properties and Library Manager pulls it in for you — along with the eleven libraries it depends on in turn.

Renamed

The repo and the library are now Adafruit Arcade Machines (adafruit/Adafruit_Arcade_Machines). The old URL redirects. #include <ArcadeArduino.h> is now #include <Adafruit_Arcade_Machines.h> — the reason this is 2.0.0 rather than 1.3.0, since it breaks anything that included the old header. The C API underneath (hal_*, arcade_hal_*) is untouched.

Upgrading from an earlier checkout

Reset Preferences → Sketchbook location to your normal sketchbook. Leaving it pointed at this repo now produces fatal error: Adafruit_Arcade_Machines.h: No such file or directory, because the repo is the library and cannot live inside its own libraries/ folder. The README covers this at the install step, along with a symlink workflow for editing a checkout in place.

Also in this release

  • A build-time guard for the optimisation level. At the core's default -Os every game here overruns the 16.66 ms frame budget and the screen goes solid red — which looks exactly like a missing SD card and has cost real debugging time. Building at -Os now fails with an explanatory error instead. Set Tools → Optimize to -O2 or -O3; each example says which it needs.
  • PORTING.md — the method behind seven ports, the traps ranked by how much time they actually cost, and a section on adding a new board.
  • The i8080 core takes its port I/O through per-instance callbacks, matching the Z80, 6502 and MCS-48 cores. This is what let all seven machines coexist in one library.
  • 13 broken symlinks that had been committed into libraries/ are gone.

Verified

All seven games flashed and confirmed on real Fruit Jam hardware after the restructure — Donkey Kong, Pac-Man, Ms. Pac-Man and Galaga in all four screen rotations, with zero DVI starvation. RAM usage is byte-identical to before the restructure on all 13 examples. arduino-lint --library-manager submit reports 0 errors.

Flashing

Hold BOOT while connecting USB (or hold BOOT and tap RESET), then copy the .uf2 onto the RP2350 drive. The binaries contain no ROM data — you still need a microSD card with legally-obtained ROMs, laid out as each game's README describes.

v1.2.0 — all four screen rotations, with aspect-ratio correction

Choose a tag to compare

@jedgarpark jedgarpark released this 04 Sep 21:23
afc82b5

All seven games now run clean in all four screen rotations, with optional aspect-ratio correction — verified on physical Fruit Jam hardware, in gameplay rather than attract mode, game by game.

No new games since v1.1.0. This release is the display work: the red lines are gone in every orientation, and the picture can now be shown in the 4:3 proportion these cabinets actually used.

Flashing

Hold BOOT while connecting USB (or hold BOOT and tap RESET), then copy the .uf2 onto the RP2350 drive that appears.

The binaries contain no ROM data — you still need a microSD card with legally-obtained ROMs, laid out as each game's README describes.

Display controls

Three front-panel buttons, the same on every game:

Button
1 Aspect-ratio correction on/off
2 Cycle rotation (upright / 90° CCW "tate" / 180° / 90° CW)
3 Horizontal mirror (for Pepper's-Ghost cabinets)

Button 1 depends on your monitor, not on the game. These cabinets had 4:3 tubes, so a correct picture fills the screen in that proportion. A 16:9 panel rotated for tate already stretches the image on its own and the correction would over-correct it; a wide panel forced to 4:3, or a genuine 4:3 panel, needs it. Try both and keep what looks right.

It is off by default, except Pac-Man and Ms. Pac-Man whose raster is already close to 4:3.

Known exception: Galaga does not apply the correction in landscape — it is the most expensive machine here (three Z80s) and the correction costs more than the frame budget allows, so its landscape stays at the 1:1 layout and Button 1 does nothing in rotations 0 and 2.

None of the three settings survives a power cycle yet. Persisting them is planned; see DISPLAY_GEOMETRY.md section 8.

What changed under the hood

  • One coordinate space for every renderer — the canvas is 320×240 square logical pixels, not 640×480
  • A shared geometry module replacing 32 hand-derived rotation cases across seven renderers
  • Scanline queue raised from 8 buffers to 32, which removed the last of the starvation
  • A wide-store emit that made the aspect correction affordable (1,712 µs → 66 µs on Burger Time)
  • Column renderers for Donkey Kong (also freeing 94 KB of SRAM), Pac-Man, Ms. Pac-Man, Galaga and Burger Time
  • Merged downsampling for Space Invaders and Lunar Rescue, whose landscape text was losing whole letter strokes

DISPLAY_GEOMETRY.md has the derivation and the per-game measurements; DEVNOTES.md #75–#101 has the full account, including the wrong turns.

Verified

REUSE compliant (198/198 files), tools/geom_test passing, all seven sketches and all six host harnesses building, and every game flashed and played in all four rotations.

v1.1.0 — seven games on the Fruit Jam

Choose a tag to compare

@jedgarpark jedgarpark released this 02 Sep 21:10

Seven classic arcade games running on the Adafruit Fruit Jam
(RP2350B) under the Arduino framework, built on the SAMP (Single Arcade
Machine Port) framework: a CPU-core axis, a game-machine axis, and a
board axis, composed into a thin per-game sketch.

All seven are confirmed playing on real hardware.

What's new since v1.0.0

  • Burger Time (Data East, 1982) — the seventh game, running at a flat
    60 fps with its sound checked by ear against a real cabinet.
  • ArcadeCPU_M6502, the project's fourth CPU family, verified against
    Klaus Dormann's 6502 functional and decimal test suites plus AllSuiteA.
    It adds an optional encrypted-opcode hook (for the DECO CPU-7) and an
    optional direct-read page table that was worth 2.1 ms of a 16.66 ms
    frame and applies to the other three cores too.
  • hal_video_take_starve_count() in the ArcadeHAL video contract. DVI
    starvation is a within-frame event, so it was invisible to the existing
    whole-frame timing instruments; three separate visual symptoms in the
    Burger Time port could not be measured until this existed. All seven
    sketches were rebuilt against it, which is why every binary here differs
    slightly from its v1.0.0 counterpart.
  • One known divergence is documented rather than fixed: a close-range
    pepper hit in Burger Time loses its "boing". See DEVNOTES.md #74.

Games

Game Hardware notes
Space Invaders The original port. i8080, "8080bw" board family, sample-based sound.
Lunar Rescue Same 8080bw family, plus one genuinely synthesized (bit-banged) audio channel.
Pac-Man First Z80 port and first tile+sprite video hardware, with fully synthesized Namco WSG sound.
Galaga First multi-CPU machine — three Z80s sharing RAM — plus a Namco 06XX/51XX/54XX I/O chain and the 05XX starfield.
Ms. Pac-Man Pac-Man hardware plus the aux daughterboard: first banked address space and first encrypted ROM decode.
Donkey Kong First Nintendo board, first DMA-driven sprites (i8257), first NMI, first resistor-network palette, and an emulated 8035 sound CPU.
Burger Time First 6502 — a DECO CPU-7, whose opcode fetches are encrypted, so the ROM cannot be pre-decrypted. First machine with no vblank interrupt (it polls a bit wired into a DIP-switch port), a palette held in RAM rather than a PROM, and a general-purpose AY-3-8910 PSG behind an emulated discrete filter network.

Flashing

Download the .uf2 for the game you want. Hold BOOT while connecting
USB (or hold BOOT and tap RESET) — a drive named RP2350 appears.
Copy the .uf2 onto it and the board reboots into the game.

These are RP2350 ARM-S builds (UF2 family ID 0xe48bff57) and will not
load on an RP2040 board.

You still need a microSD card with ROMs

These binaries contain no ROM data. Each game loads its ROM and sample
files from a microSD card at boot. See that game's README in the repository
for its exact /rom/ and /samples/ layout, and use legally-obtained
ROMs.

Format the card FAT32 with an MBR partition scheme — not GPT, which
macOS Disk Utility defaults to on "Erase". The bundled FatFs is configured
for simple single-partition mounting and does not understand a GPT disk's
protective MBR.

If the screen comes up solid red, that is either a missing/unreadable
SD card or a starved video pipeline. Check the card first.

Build details

Each binary is built at the optimization level its own sketch.yaml pins,
verified against the compiler command line. These are deliberately not
normalized to one level — Invaders is the only game that runs comfortably
at -O2; the other six need -O3 to fit the 16.67 ms frame budget.

Asset opt= Flash RAM
invaders_fruitjam.uf2 Optimize2 (-O2) 97,644 B 218,484 B
lrescue_fruitjam.uf2 Optimize3 (-O3) 111,220 B 352,992 B
pacman_fruitjam.uf2 Optimize3 (-O3) 92,788 B 249,992 B
mspacman_fruitjam.uf2 Optimize3 (-O3) 96,692 B 332,056 B
dkong_fruitjam.uf2 Optimize3 (-O3) 99,564 B 284,484 B
galaga_fruitjam.uf2 Optimize3 (-O3) 90,084 B 295,680 B
btime_fruitjam.uf2 Optimize3 (-O3) 96,252 B 263,480 B

All seven binaries were rebuilt from this tag in one pass with
--clean, so each one matches the source here exactly. (In v1.0.0 the
Burger Time binary was added after the fact and postdated its tag; that is
what this release exists to tidy up.)

Built with arduino-cli against Earle Philhower's rp2040 core. To build
from source, see the repository README — arduino-cli reads each sketch's
sketch.yaml automatically, so the right opt= level is applied without
passing --fqbn. The Arduino IDE often does not honour it; set
Tools > Optimize > Optimize3 by hand there.

DEVNOTES.md in the repository records every hardware bug found during
these ports and why each fix works — including why the optimization level
differs per game (problems #11 and #19).

v1.0.0 — six games on the Fruit Jam

Choose a tag to compare

@jedgarpark jedgarpark released this 02 Sep 04:20

Six classic arcade games running on the Adafruit Fruit Jam
(RP2350B) under the Arduino framework, built on the SAMP (Single Arcade
Machine Port) framework: a CPU-core axis, a game-machine axis, and a
board axis, composed into a thin per-game sketch.

All six are confirmed playing on real hardware.

Games

Game Hardware notes
Space Invaders The original port. i8080, "8080bw" board family, sample-based sound.
Lunar Rescue Same 8080bw family, plus one genuinely synthesized (bit-banged) audio channel.
Pac-Man First Z80 port and first tile+sprite video hardware, with fully synthesized Namco WSG sound.
Galaga First multi-CPU machine — three Z80s sharing RAM — plus a Namco 06XX/51XX/54XX I/O chain and the 05XX starfield.
Ms. Pac-Man Pac-Man hardware plus the aux daughterboard: first banked address space and first encrypted ROM decode.
Donkey Kong First Nintendo board, first DMA-driven sprites (i8257), first NMI, first resistor-network palette, and an emulated 8035 sound CPU.

Flashing

Download the .uf2 for the game you want. Hold BOOT while connecting
USB (or hold BOOT and tap RESET) — a drive named RP2350 appears.
Copy the .uf2 onto it and the board reboots into the game.

These are RP2350 ARM-S builds (UF2 family ID 0xe48bff57) and will not
load on an RP2040 board.

You still need a microSD card with ROMs

These binaries contain no ROM data. Each game loads its ROM and sample
files from a microSD card at boot. See that game's README in the repository
for its exact /rom/ and /samples/ layout, and use legally-obtained
ROMs.

Format the card FAT32 with an MBR partition scheme — not GPT, which
macOS Disk Utility defaults to on "Erase". The bundled FatFs is configured
for simple single-partition mounting and does not understand a GPT disk's
protective MBR.

If the screen comes up solid red, that is either a missing/unreadable
SD card or a starved video pipeline. Check the card first.

Build details

Each binary is built at the optimization level its own sketch.yaml pins,
verified against the compiler command line. These are deliberately not
normalized to one level — Invaders is the only game that runs comfortably
at -O2; the other five need -O3 to fit the 16.67 ms frame budget.

Asset opt= Flash RAM
invaders_fruitjam.uf2 Optimize2 (-O2) 97,604 B 218,480 B
lrescue_fruitjam.uf2 Optimize3 (-O3) 111,180 B 352,988 B
pacman_fruitjam.uf2 Optimize3 (-O3) 92,748 B 249,988 B
mspacman_fruitjam.uf2 Optimize3 (-O3) 96,652 B 332,052 B
dkong_fruitjam.uf2 Optimize3 (-O3) 99,524 B 284,480 B
galaga_fruitjam.uf2 Optimize3 (-O3) 90,044 B 295,676 B

Built with arduino-cli against Earle Philhower's rp2040 core. To build
from source, see the repository README — arduino-cli reads each sketch's
sketch.yaml automatically, so the right opt= level is applied without
passing --fqbn. The Arduino IDE often does not honour it; set
Tools > Optimize > Optimize3 by hand there.

DEVNOTES.md in the repository records every hardware bug found during
these ports and why each fix works — including why the optimization level
differs per game (problems #11 and #19).