Skip to content

PICO9918 Programmers Reference

visrealm edited this page Apr 9, 2026 · 9 revisions

PICO9918 Programmer's Reference

This page documents programming features that are specific to the PICO9918 and are not present on the original F18A hardware. All features on this page require the F18A unlock sequence to be active.

For the full F18A-compatible register set, see F18A Programmers Reference.

Extended Row Modes (PICO9918)

The PICO9918 extends the F18A's row-30 mode with additional row counts, allowing 48-row and 60-row displays.

Row 30 (F18A Standard)

Set R49 bit 6 (ROW30) to enable 30-row mode (240 active lines). The sprite Y terminator changes from 0xD0 (208) to 0xF0 (240).

Row 48 and Row 60 (PICO9918)

These extended modes use R0 bit 3 (M5) together with R49 bit 6 (ROW30) to select the row count:

R0 bit 3 (M5) R49 bit 6 (ROW30) Rows Active Lines Vertical Scaling
0 0 24 192 2x
0 1 30 240 2x
1 0 48 384 1x (no doubling)
1 1 60 480 1x (no doubling)

In 48-row and 60-row modes, vertical scaling is disabled — each scanline is rendered once, not doubled. This gives access to the full 480 lines of the VGA output.

Note: In 48 and 60-row modes, R0 bit 3 is repurposed for row selection and is not available as a horizontal interrupt enable.

Opaque Sprites (PICO9918)

In standard TMS9918A and F18A sprite rendering, color 0 pixels within a sprite pattern are transparent. The PICO9918 adds an opaque sprite flag that causes color 0 pixels to be rendered using palette entry 0 instead.

The opaque flag is bit 4 of sprite attribute byte 3 (the color byte), and only applies in 16x16 sprite mode:

Bit Description
7 Early clock (EC)
6 Flip X
5 Flip Y
4 Opaque (PICO9918, 16x16 mode only)
3-0 Color / ECM palette select

When set, transparent pixels within the sprite are drawn as the background color from palette entry 0, making the sprite fully opaque.

Configuration Registers (PICO9918)

The PICO9918 exposes its configuration settings through two F18A extended registers (R58 and R59) and a status register (SR12), allowing software to read and modify configuration at runtime.

Register 58 (0x3A) - Configuration Option Index

Writing to R58 selects which configuration option to access. The current value of the selected option is immediately available in status register 12.

Register 59 (0x3B) - Configuration Option Value

Writing to R59 sets the value of the configuration option currently selected by R58. Only writable for option indices >= 8 (hardware info options 0-6 are read-only).

Status Register 12 - Configuration Mirror

Returns the current value of the configuration option selected by R58. Updated whenever R58 or R59 is written.

Configuration Option Map

Read-only hardware info (indices 0-6):

Index Name Values
0 Pico model 1 = RP2040, 2 = RP2350
1 Hardware version 0x03 = V0.3, 0x10 = V1.0+, 0x20 = V2.0+
2 Firmware version (major.minor) Upper nibble = major, lower = minor
3 Firmware patch version Patch number
4 Last tested clock preset 0-2
5 Display driver 0 = VGA, 1 = NTSC SCART, 2 = PAL SCART
6 Flash status Reserved

User-configurable (indices 8+):

Index Name Range Default
8 CRT scanlines 0-1 0 (off)
9 Scanline sprite limit 0-3 0 (no limit)
10 Clock preset 0-2 0 (252 MHz)
16 Diagnostic overlays master 0-1 0 (off)
17 Diagnostic overlay: registers 0-1 0
18 Diagnostic overlay: performance 0-1 0
19 Diagnostic overlay: palette 0-1 0
20 Diagnostic overlay: address 0-1 0
128-159 Palette entries 0-15 16 x 2 bytes Default TMS9918A palette
255 Save to flash Write 1 to trigger 0

Save / Load Mechanism

Configuration changes made via R59 take effect immediately but are not automatically persisted to flash. To save the current configuration permanently:

  1. Select option index 255 by writing 0xFF to R58
  2. Write 0x01 to R59 to trigger a flash save

The configuration is stored at the top of flash memory (address 0x1FF000) and survives firmware updates.

To read a configuration value:

  1. Write the option index to R58
  2. Read status register 12 (select SR12 via R15, then read the status port)

Example: Toggle CRT Scanlines

; Assumes F18A is unlocked

; Read current scanline setting
write 0x08 to R58          ; select option 8 (CRT scanlines)
write 0x0C to R15          ; select status register 12
value = read status port   ; current value (0 or 1)
write 0x00 to R15          ; restore S0 selection

; Toggle and write back
new_value = value XOR 1
write 0x08 to R58          ; select option 8
write new_value to R59     ; apply new value

; Save to flash
write 0xFF to R58          ; select option 255 (save trigger)
write 0x01 to R59          ; trigger flash save

Register 63 (0x3F) - Flash Control (PICO9918)

R63 provides access to the PICO9918's onboard flash for both program data storage and firmware updates. Writing to R63 triggers a flash operation. The GPU must not be running (R56 must be 0) or the operation is rejected with error code 5 (Busy).

Bit Description
7 Direction (0 = read, 1 = write)
6 Mode (0 = program data, 1 = firmware update)
5-0 VRAM address MSB — the 256-byte data block is at (R63 & 0x3F) << 8 in VRAM

Status Register 2 - Flash Operation Status

All flash operations report progress via SR2. Poll until bit 7 clears, then check the error code.

Bits Field Values
7 Running Operation in progress
6-5 Retry count 0-3
4-2 Error code 0=OK, 1=Header, 2=Sequence/Full, 3=Size, 4=Verify, 5=Busy
1-0 Status 0=Idle, 1=Validating, 2=Erasing, 3=Writing

Program Data Storage (PICO9918)

The PICO9918 provides persistent storage for game saves, high scores, and other program data. Each program identifies itself with a 128-bit GUID, so multiple programs can store data independently. Up to 256 programs can store data simultaneously.

VRAM Block Format (256 bytes)

Before triggering a read or write, prepare a 256-byte block in VRAM:

Offset Size Contents
0-3 4 bytes Block ID hint (little-endian). Set to 0xFFFFFFFF on first use. After a successful read, the firmware writes the actual block index here — cache it for future operations.
4-19 16 bytes 128-bit GUID — unique identifier for this program
20-35 16 bytes Program name (human-readable, padded to 16 bytes)
36-255 220 bytes Program data (arbitrary format)

The GUID is the primary key. Generate a unique GUID for your program (any standard UUID generator will do) and embed it as constant data.

Reading Program Data

  1. Prepare the VRAM block with the GUID, program name, and block ID hint (0xFFFFFFFF if first time)
  2. Disable VDP interrupts
  3. Write to R63 with bit 7 = 0, bit 6 = 0, bits 5-0 = VRAM address MSB
  4. Poll SR2 (select via R15) until bit 7 clears
  5. Check SR2 error code (bits 4-2) for success
  6. Read bytes 0-3 from VRAM to cache the block ID for future operations
  7. Read your data from bytes 36-255
  8. Re-enable VDP interrupts

If the GUID is not found in flash, a new block is allocated and the block ID is written back — the data area will be uninitialized.

Writing Program Data

  1. Prepare the VRAM block with the GUID, program name, block ID hint, and your data at bytes 36-255
  2. Disable VDP interrupts
  3. Write to R63 with bit 7 = 1, bit 6 = 0, bits 5-0 = VRAM address MSB
  4. Poll SR2 until bit 7 clears
  5. Check SR2 error code for success
  6. Re-enable VDP interrupts

Example

; Save 2 bytes of high score data to flash
; VRAM block prepared at address 0x1F00 with GUID, name, and data

; Write program data
write 0x00 to R56          ; ensure GPU is stopped
write 0x9F to R63          ; bit7=1 (write), bit6=0 (program data), 0x1F = VRAM 0x1F00

; Poll SR2
.poll:
write 0x02 to R15          ; select status register 2
value = read status port
write 0x00 to R15          ; restore S0 selection
if (value AND 0x80): goto .poll

; Check error code in bits 4-2
; 0 = success

Storage Limits

Property Value
Data per program 220 bytes
Maximum programs 256
Flash region 64 KB at flash offset 0x100000

Example Game

RetroPIPE demonstrates program data storage for saving high scores.

Firmware Update via VDP (PICO9918)

The PICO9918 supports firmware updates through the VDP register interface, used by the Configurator on platforms with ROM banking support.

To trigger a firmware update operation, write to R63 with bit 6 = 1. See Configurator Technical Reference#Firmware Update Protocol for the full protocol.

Status Register 13 - Temperature (PICO9918)

Contains the RP2040/RP2350 core temperature encoded as (tempC * 4 + 0.5) as a uint8_t. Updated every 64 frames.

To decode: tempC = value / 4.0

Extended GPU RAM (PICO9918)

On the original F18A, the GPU address space has defined regions with gaps between them. On the PICO9918, the full 64KB address space is backed by RAM, making the gaps usable as additional GPU RAM:

Address Range Size Contents
0x6040-0x6FFF ~4 KB GRAM2
0x7002-0xAFFF ~16 KB GRAM3
0xB010-0xFFFF ~20 KB GRAM4

See F18A Programmers Reference#Memory Map (F18A Mode) for the full address space layout.

Clone this wiki locally