-
-
Notifications
You must be signed in to change notification settings - Fork 26
PICO9918 Programmers 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.
The PICO9918 extends the F18A's row-30 mode with additional row counts, allowing 48-row and 60-row displays.
Set R49 bit 6 (ROW30) to enable 30-row mode (240 active lines). The sprite Y terminator changes from 0xD0 (208) to 0xF0 (240).
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.
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.
Writing any value with bit 7 set to register 50 (R50, VR_RST) performs a soft reset — re-locking the F18A and restoring all registers to their power-on defaults. On the original F18A, the palette is always preserved across this reset.
The PICO9918 extends this behavior using bit 6 of the written value:
| R50 write | Behavior |
|---|---|
0x80 (bit 7 only) |
Reset registers, palette preserved (F18A-compatible) |
0xC0 (bits 7 and 6) |
Reset registers and reset palette to defaults (PICO9918) |
Bit 6 is ignored by the original F18A, so software targeting both devices can safely write 0xC0 when a full reset (including palette) is desired — F18A hardware will reset registers only, while PICO9918 will additionally restore the default palette.
After a reset, the F18A is locked again. The unlock sequence must be repeated to access enhanced features.
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.
Writing to R58 selects which configuration option to access. The current value of the selected option is immediately available in status register 12.
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).
Returns the current value of the configuration option selected by R58. Updated whenever R58 or R59 is written.
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 |
Configuration changes made via R59 take effect immediately but are not automatically persisted to flash. To save the current configuration permanently:
- Select option index 255 by writing
0xFFto R58 - Write
0x01to 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:
- Write the option index to R58
- Read status register 12 (select SR12 via R15, then read the status port)
; 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
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 |
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 |
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 ~4096 programs can store data simultaneously.
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.
- Prepare the VRAM block with the GUID, program name, and block ID hint (
0xFFFFFFFFif first time) - Disable VDP interrupts
- Write to R63 with bit 7 = 0, bit 6 = 0, bits 5-0 = VRAM address MSB
- Poll SR2 (select via R15) until bit 7 clears
- Check SR2 error code (bits 4-2) for success
- Read bytes 0-3 from VRAM to cache the block ID for future operations
- Read your data from bytes 36-255
- 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.
- Prepare the VRAM block with the GUID, program name, block ID hint, and your data at bytes 36-255
- Disable VDP interrupts
- Write to R63 with bit 7 = 1, bit 6 = 0, bits 5-0 = VRAM address MSB
- Poll SR2 until bit 7 clears
- Check SR2 error code for success
- Re-enable VDP interrupts
; 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
| Property | Value |
|---|---|
| Data per program | 220 bytes |
| Maximum programs | ~4096 |
| Flash region | ~1 MB at flash offset 0x100000 |
RetroPIPE demonstrates program data storage for saving high scores.
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 Firmware Update Protocol for the full protocol.
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
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 Memory Map (F18A Mode) for the full address space layout.
Hardware
- Hardware
- Hardware Setup
- Digital AV (HDMI) Dongle
- SCART AV Dongle
- TI-99 4A No-Cut Mod
- F18A to PICO9918 Dongle Adapter
Supported Devices
Firmware
Configurator
Programming
Reference