Agent skill

TeensyROM IO Handler Checklist

by SensoriumEmbedded in SensoriumEmbedded/TeensyROM

Walks through adding a new cartridge or peripheral IO handler to TeensyROM's MinimalBoot firmware and the three files that must stay in sync.

MITAuto-check passedDevelopment

Install TeensyROM IO Handler Checklist

skills CLI
$ npx skills add SensoriumEmbedded/TeensyROM --skill add-io-handler -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install SensoriumEmbedded/TeensyROM add-io-handler --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/SensoriumEmbedded/TeensyROM.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/add-io-handler .claude/skills/add-io-handler && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
add-io-handler
GitHub stars
227
Token cost
~1.8k tokens
SKILL.md length
807 words
Files
1
Skills in repo
4
Repo updated
First seen
Licence
MIT

At a glance

Walks through adding a new cartridge or peripheral IO handler to TeensyROM's MinimalBoot firmware and the three files that must stay in sync.

  • Works in 4 steps: Write the handler file → Register it in three places, same… → Watch for these two things (found… → …
  • Adding a new cartridge or peripheral IO handler to TeensyROM
  • SKILL.md covers 1. Write the handler file, 2. Register it in three…, 3. Watch for these two things… and 4. Other things worth…, plus 1 more section
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

A new piece of hardware plugs into MinimalBoot as a struct of function pointers. The handler file goes in a fixed IO_Handlers folder, best started from an existing handler of the same cartridge class, such as the two reference implementations for freezer-style carts with a control register and a physical freeze button, and any function slot that is not needed stays null rather than being stubbed out.

The handler then has to be registered at the same relative position in three files kept in sync by explicit source comments warning that the order must match: an enum entry, an include line plus an array entry, and, for a CRT-detectable cartridge, a row associating its type constant with the handler. Include order matters when a handler reads another handler's global, since all handler files are textually included into one translation unit. Two conventions found while integrating a real freezer cartridge round out the checklist: a reused global must be reset inside the new handler's own init function, and freeze completion can bypass side effects normal execution would otherwise trigger.

When your agent uses it

  • Adding a new cartridge or peripheral IO handler to TeensyROM
  • Reviewing a contributed handler before it is registered
  • Checking that a handler is wired into all three required files
  • Debugging a freezer-style cartridge handler's init or freeze behavior

Example prompts

  • “Add a new IO handler for this cartridge, modeled on the existing freezer-style handler.”
  • “Review this contributed handler and check it's registered in all three files at the right position.”
  • “Why does my handler's state reset wrong after a freeze completes?”

Requirements

  • The TeensyROM MinimalBoot firmware source tree

Workflow steps

4 steps, taken from the step headings in SKILL.md.

  1. Write the handler file
  2. Register it in three places, same relative order
  3. Watch for these two things (found integrating Final Cartridge III)
  4. Other things worth double-checking, not just copying

What it can do on your machine

Read from SKILL.md and the folder at commit 5c1196f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are c).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

TeensyROM IO Handler Checklist loads about 1.8k tokens when it runs. Until then it costs about 162 tokens; SKILL.md has 807 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~162
When it runs · the whole SKILL.md, loaded when a task matches
~1.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from SensoriumEmbedded/TeensyROM at commit 5c1196f, republished under its MIT licence (© SensoriumEmbedded). 807 words, ~1,820 tokens.

Download SKILL.mdSave it as .claude/skills/add-io-handler/SKILL.md (or your agent's skills folder).
name
add-io-handler
description
Checklist for adding a new cartridge/peripheral IO Handler to TeensyROM's MinimalBoot firmware (a new IOH_<Name>.c under Source/Teensy/MinimalBoot/Common/IO_Handlers/). Use this whenever the user wants to add, wire up, integrate, or review a new IO handler, freezer cartridge, or peripheral emulation for TeensyROM/MinimalBoot — including when reviewing a handler someone else contributed before it's registered. Covers the three files that must be updated in sync, and the two hardware-emulation gotchas (stale reused-globals at init, freeze-completion bypassing side effects) found while integrating the Final Cartridge III handler.

Adding an IO Handler to TeensyROM's MinimalBoot

A new piece of hardware (cartridge, freezer, peripheral) plugs into MinimalBoot as an stcIOHandlers struct of function pointers. Most of the work is mechanical wiring across a few files that must stay in sync; the rest is a handful of hardware-emulation conventions that are easy to get subtly wrong because the compiler won't catch them.

1. Write the handler file

Create Source/Teensy/MinimalBoot/Common/IO_Handlers/IOH_<Name>.c. Use an existing handler of the same cartridge class as your template rather than starting from a blank page — IOH_RetroReplay.c and IOH_SuperSnapshotV5.c are the reference implementations for freezer-style carts with a control register and a physical freeze button.

Shape:

c
void InitHndlr_<Name>();
void IO1Hndlr_<Name>(uint8_t Address, bool R_Wn);
// ...other handler prototypes you need

stcIOHandlers IOHndlr_<Name> =
{
  "<Name>",              // Name of handler, IOHNameLength max
  &InitHndlr_<Name>,     // Called once at handler startup
  &IO1Hndlr_<Name>,      // IO1 R/W handler
  &IO2Hndlr_<Name>,      // IO2 R/W handler
  NULL,                  // ROML Read handler (leave NULL if unused)
  NULL,                  // ROMH Read handler (leave NULL if unused)
  NULL,                  // Polled in main routine (leave NULL if unused)
  &CycleHndlr_<Name>,    // called at the end of EVERY c64 cycle (leave NULL if unused)
};

Any slot you don't need stays NULL — don't stub out empty functions for them.

2. Register it in three places, same relative order

There are explicit "Synch order/qty" comments in the code warning that these must match — add your handler in the same position (relative to neighboring handlers) in all three:

  • Source/Teensy/MinimalBoot/Common/Menu_Regs.h — add IOH_<Name> to the enumIOHandlers enum.
  • Source/Teensy/MinimalBoot/Common/IOHandlers.h — add both the #include "IO_Handlers/IOH_<Name>.c" line and the &IOHndlr_<Name> entry in the IOHandler[] array, at the same position as the enum.
  • Source/Teensy/MinimalBoot/Common/DriveDirLoad.h — if it's a CRT-detectable cartridge type, add a (uint16_t)Cart_<Name>, IOH_<Name>, row to HWID_IOH_Assoc[] (the Cart_<Name> constant is usually already #defined nearby from CRT-format docs).

Include-order matters if your handler references another handler's global (see the reused-global pattern below) — your #include must come after the file that declares that global, since these are all textually included into one translation unit.

If the cartridge needs boot/CRT-type detection help (e.g. distinguishing which freezer cart produced a given CRT file), that logic lives in Source/Teensy/FileParsers.ino's SetTypeFromCRT(), typically behind the same feature-flag #ifdef as the handler registration.

3. Watch for these two things (found integrating Final Cartridge III)

Reused globals must be reset in your Init, not just passed as a parameter

To save RAM, a handler may alias an existing global from another handler that's never active at the same time, e.g. #define FC3_ControlReg RR_StatusReg, with a comment explaining the reuse. This is a legitimate, established pattern — but MinimalBoot's dispatcher (Source/Teensy/IOHandlers.ino and Source/Teensy/MinimalBoot/Min_DriveDirLoad.ino) only calls the new handler's InitHndlr() when switching handlers. It never clears shared globals in between, so whatever the previous handler left in that memory is still there when yours starts.

If your init logic does something like ProcessControlReg(0); // HW power-on reset and that function reads the persisted alias for anything (a status/hidden bit, a mode flag), passing 0 as a function parameter doesn't touch the alias itself — you must explicitly assign it too (FC3_ControlReg = 0;) before or as part of that call. Otherwise the first thing your handler does on load is act on stale garbage from whatever ran before it. Trace every read of a reused global back to where it's written, and make sure your Init path covers all of them, not just the ones a fresh parameter conveniently overwrites.

Show full SKILL.md (326 more words)Show less
Freeze-completion should reuse your control-register function, not hand-set pins

Freezer cartridges share a button state machine: CycleHndlr_<Name>(bool R_Wn) runs every C64 cycle, using shared globals CycleCountdown, CycCntFreeze, CycCntNumWr — a button press sets CycleCountdown = CycCntFreeze, the handler waits for a read (asserting NMI), then counts down CycCntNumWr consecutive writes before completing the transition into the frozen state.

At that completion point, route the transition through your own control-register-processing function with a synthetic value representing the end state — e.g. RetroReplay does ProcessRRControlReg(RR_CR_nGAME | RR_CR_EXROM) — rather than hand-setting individual pins (SetNMIDeassert; SetGameAssert;). The control-register function is where all the side effects of a mode change live (bank switching, ROM pointer updates, and — critically for Ultimax-mode freezers — toggling EmulateVicCycles/disabling IRQ_ENET/IRQ_PIT, which prevents graphics tearing in the freezer menu). Hand-setting a couple of pins reproduces the visible pin state but silently skips everything else that function does, which is exactly the kind of bug that only shows up as a glitch at the exact moment of freezing.

4. Other things worth double-checking, not just copying

  • Bit polarity: hardware pins are typically active-low. The codebase's comment convention for the EXROM/GAME combinations is //rtBin8kHi or None / //rtBin16k or 8kLo — reuse this shorthand so the four combinations stay easy to read across handlers.
  • Bank masking: BankNum is set from control-register bits and indexes CrtChips[BankNum].ChipROM. The bit-mask width must exactly match how many banks the ROM size implies for your cartridge's variants (e.g. a 4-chip 64K variant needs & 3, a 16-chip 256K variant needs & 15) — derive this from the actual ROM size math for your hardware, don't copy a mask from a handler with a different bank count.

Before calling a new handler done

Re-read your InitHndlr_<Name>() against every global it reads (including reused/aliased ones) and confirm none of them can hold a stale value from whatever handler ran previously. Re-read your CycleHndlr_<Name>()'s freeze-completion branch and confirm it transitions state through the same function a real register write would use, not a hand-picked subset of pins.

© SensoriumEmbedded, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/add-io-handler of SensoriumEmbedded/TeensyROM.

Open the folder on GitHubat commit 5c1196f

Compare with similar skills

TeensyROM IO Handler Checklist next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

TeensyROM IO Handler Checklist compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
TeensyROM IO Handler Checklist this skillSensoriumEmbedded/TeensyROM227—~1.8kAutomated safety check: PassMIT
Authoring VPhone Patch SetsLakr233/vphone-cli15k—~4.6kAutomated safety check: PassMIT
Sipeed I2C and SPI Hardware Controlsipeed/picoclaw30k—~578Automated safety check: PassMIT
RuView Hardware Setupruvnet/RuView97k—~1.8kAutomated safety check: NotesMIT
Esp32 Firmware Engineeralxv2016/folloup-sticky1151 repos~3.8kAutomated safety check: PassGPL-3.0
ExecuTorch Binary Size Reductionpytorch/executorch5.1k—~793Automated safety check: PassCustom licence

Similar skills

  • Authoring VPhone Patch Sets

    Lakr233/vphone-cli

    Explains how to declare a firmware patch in a vphone patch set, add a new set, or write a preset, including naming, gating and the checks that catch undeclared patches.

    15k GitHub stars~4.6k tokensUpdated today
    DevelopmentAuto-check passed
  • Reads and controls I2C and SPI peripherals on Sipeed boards such as LicheeRV Nano, MaixCAM and NanoKVM through the i2c and spi tools.

    30k GitHub stars~578 tokensUpdated 13 days ago
    DevelopmentAuto-check passed
  • Brings a RuView CSI sensing node online by building ESP32-S3 or ESP32-C6 firmware, flashing the board, provisioning WiFi and checking the serial output.

    97k GitHub stars~1.8k tokensUpdated today
    DevelopmentAuto-check: notes
  • Esp32 Firmware Engineer

    alxv2016/folloup-sticky

    ESP32 firmware engineering for ESP-IDF projects. An agent skill from alxv2016/folloup-sticky.

    115 GitHub starsUsed in 1 repo~3.8k tokens
    DevelopmentAuto-check passed
  • Measures and shrinks the ExecuTorch runtime binary by building a size test, analyzing it with bloaty and landing each reduction as its own pull request.

    5.1k GitHub stars~793 tokensUpdated today
    DevelopmentAuto-check passed
  • Sets up and runs 60 GHz and 24 GHz mmWave radar sensing on ESP32 boards in RuView, alone or fused with WiFi CSI.

    97k GitHub stars~907 tokensUpdated today
    DevelopmentAuto-check: notes

More from SensoriumEmbedded/TeensyROM

  • TeensyROM Menu Program Adder

    SensoriumEmbedded/TeensyROM

    Adds a Commodore 64 program, cartridge, SID or picture to TeensyROM's built-in menu by converting it to a PROGMEM header and wiring it into MainMenuItems.h.

    227 GitHub stars~1.2k tokensUpdated 2 days ago
    Auto-check passed
  • Beyond Compare Git Diff Opener

    SensoriumEmbedded/TeensyROM

    Opens Beyond Compare on a git diff for the current work, a pull request, a branch, a commit or a range, without touching the checkout.

    227 GitHub stars~2.4k tokensUpdated 2 days ago
    Auto-check passed
  • TeensyROM Build and Flash

    SensoriumEmbedded/TeensyROM

    Explains how to build the C64 menu programs and the dual-boot TeensyROM firmware from the repo and flash it to one or more boards over USB.

    227 GitHub stars~1.3k tokensUpdated 2 days ago
    Auto-check passed

Categories

Questions about TeensyROM IO Handler Checklist

What does TeensyROM IO Handler Checklist do?

Walks through adding a new cartridge or peripheral IO handler to TeensyROM's MinimalBoot firmware and the three files that must stay in sync. A new piece of hardware plugs into MinimalBoot as a struct of function pointers. The handler file goes in a fixed IO_Handlers folder, best started from an existing handler of the same cartridge class, such as the two reference implementations for freezer-style carts with a control register and a physical freeze button, and any function slot that is not needed stays null rather than being stubbed out.

When should I use TeensyROM IO Handler Checklist?

TeensyROM IO Handler Checklist fits situations like: adding a new cartridge or peripheral IO handler to TeensyROM; reviewing a contributed handler before it is registered; checking that a handler is wired into all three required files; debugging a freezer-style cartridge handler's init or freeze behavior.

How do I install TeensyROM IO Handler Checklist in Claude Code?

Run `npx skills add SensoriumEmbedded/TeensyROM --skill add-io-handler -a claude-code`. Or copy the skill folder (.claude/skills/add-io-handler in SensoriumEmbedded/TeensyROM) into .claude/skills/add-io-handler in your project. Claude Code loads it when a task matches its description.

How do I install TeensyROM IO Handler Checklist in Codex?

Run `npx skills add SensoriumEmbedded/TeensyROM --skill add-io-handler -a codex`. Or copy the skill folder (.claude/skills/add-io-handler in SensoriumEmbedded/TeensyROM) into .agents/skills/add-io-handler in your project. Codex loads it when a task matches its description.

Can I use TeensyROM IO Handler Checklist in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SensoriumEmbedded/TeensyROM --skill add-io-handler -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/add-io-handler, .gemini/skills/add-io-handler, .github/skills/add-io-handler and .opencode/skills/add-io-handler in your project.

What does TeensyROM IO Handler Checklist need to run?

SKILL.md names no scripts, command-line tools or credentials: TeensyROM IO Handler Checklist is instructions for the agent only. Our summary lists: The TeensyROM MinimalBoot firmware source tree.

Does TeensyROM IO Handler Checklist access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is TeensyROM IO Handler Checklist safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does TeensyROM IO Handler Checklist use?

TeensyROM IO Handler Checklist is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does TeensyROM IO Handler Checklist use?

About 1.8k tokens (SKILL.md is roughly 7.3k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to TeensyROM IO Handler Checklist?

Skills that share tags, products or a category with TeensyROM IO Handler Checklist: Authoring VPhone Patch Sets (Lakr233/vphone-cli, 15k stars), Sipeed I2C and SPI Hardware Control (sipeed/picoclaw, 30k stars), RuView Hardware Setup (ruvnet/RuView, 97k stars) and Esp32 Firmware Engineer (alxv2016/folloup-sticky, 115 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains TeensyROM IO Handler Checklist?

SensoriumEmbedded (a GitHub user) maintains it in SensoriumEmbedded/TeensyROM, which has 227 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on October 5, 2026.

Source: SensoriumEmbedded/TeensyROM on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.