24 {PICO9918_CONF_CLOCK_PRESET_ID, 2, 0, PICO9918_CONF_PENDING_CLOCK_PRESET, 0x1000},
25 {PICO9918_CONF_SCART_MODE, 1, 0, PICO9918_CONF_PENDING_SCART_MODE, 0x1200},
27 {PICO9918_CONF_DISP_DRIVER_PREF, 2, 0, PICO9918_CONF_PENDING_DRIVER_PREF, 0x1200},
28 {PICO9918_CONF_VGA_MODE, 0, 0, PICO9918_CONF_PENDING_VGA_MODE, 0x1200},
39const size_t pico9918_config_field_count =
sizeof(pico9918_config_fields) /
sizeof(pico9918_config_fields[0]);
43 config[PICO9918_CONF_PENDING_STATE] = state;
44 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
53 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
56 record[pico9918_config_fields[i].
pendingMirror - PICO9918_CONF_PENDING_STATE] =
57 config[pico9918_config_fields[i].
offset];
63 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
66 config[pico9918_config_fields[i].
offset] =
67 record[pico9918_config_fields[i].
pendingMirror - PICO9918_CONF_PENDING_STATE];
71PICO9918_INLINE_HOT uint16_t configStoredVersion(
const uint8_t* config)
73 return ((uint16_t)config[PICO9918_CONF_SW_VERSION] << 8) | config[PICO9918_CONF_SW_PATCH_VERSION];
78#define CONFIG_RUNNING_VERSION (((uint16_t)PICO9918_BUILD_SW_VERSION << 8) | PICO9918_BUILD_SW_PATCH)
82static uint8_t configPicoModel(uint8_t hwVersion)
84 return hwVersion >= 0x20 ? PICO9918_MODEL_RP2350 : PICO9918_MODEL_RP2040;
87static bool configOutOfRange(
const uint8_t* config)
89 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
91 if (config[pico9918_config_fields[i].offset] > pico9918_config_fields[i].max)
return true;
100 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
106 for (
int i = 1; i < 16; ++i)
109 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] = rgb >> 8;
110 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2) + 1] = rgb & 0xff;
116static void migrateNewFields(uint8_t* config, uint16_t storedVer)
118 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
120 if (pico9918_config_fields[i].introducedIn > storedVer)
129 const uint8_t picoModel = configPicoModel(hwVersion);
130 uint16_t storedVer = configStoredVersion(config);
132 if (config[PICO9918_CONF_PALETTE_IDX_0] != 0x00 ||
133 (config[PICO9918_CONF_PALETTE_IDX_0 + 2] & 0xf0) != 0xf0 ||
134 configOutOfRange(config))
141 config[PICO9918_CONF_PICO_MODEL] = picoModel;
142 config[PICO9918_CONF_HW_VERSION] = hwVersion;
145 config[PICO9918_CONF_SAVE_FORCED] = 0;
146 config[PICO9918_CONF_PENDING_CANCEL] = 0;
147 config[PICO9918_CONF_PENDING_CONFIRM] = 0;
148 config[PICO9918_CONF_SAVE_TO_FLASH] = 0;
150 if (storedVer == CONFIG_RUNNING_VERSION)
return false;
152 migrateNewFields(config, storedVer);
154 config[PICO9918_CONF_SW_VERSION] = PICO9918_BUILD_SW_VERSION;
155 config[PICO9918_CONF_SW_PATCH_VERSION] = PICO9918_BUILD_SW_PATCH;
158 config[PICO9918_CONF_SAVE_FORCED] = 1;
164 config[PICO9918_CONF_PICO_MODEL] = configPicoModel(hwVersion);
165 config[PICO9918_CONF_HW_VERSION] = hwVersion;
166 config[PICO9918_CONF_SW_VERSION] = PICO9918_BUILD_SW_VERSION;
167 config[PICO9918_CONF_SW_PATCH_VERSION] = PICO9918_BUILD_SW_PATCH;
170 config[PICO9918_CONF_SAVE_FORCED] = 0;
171 config[PICO9918_CONF_PENDING_CANCEL] = 0;
172 config[PICO9918_CONF_PENDING_CONFIRM] = 0;
173 config[PICO9918_CONF_SAVE_TO_FLASH] = 0;
176 config[PICO9918_CONF_PALETTE_IDX_0] = 0;
177 config[PICO9918_CONF_PALETTE_IDX_0 + 1] = 0;
178 for (
int i = 1; i < 16; ++i)
180 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] |= 0xf0;
186 return tms9918->config;
191#if PICO9918_SINGLE_INSTANCE
194 pico9918_config_applied_fn fn;
197#define CONFIG_APPLIED_CB configApplied
199#define CONFIG_APPLIED_CB tms9918->configApplied
204 CONFIG_APPLIED_CB.fn = cb;
205 CONFIG_APPLIED_CB.userdata = userdata;
212 if (CONFIG_APPLIED_CB.fn) CONFIG_APPLIED_CB.fn(tms9918, CONFIG_APPLIED_CB.userdata);
218 tms9918->config[PICO9918_CONF_DIAG] =
219 PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY) &&
220 (tms9918->config[PICO9918_CONF_DIAG_ADDRESS] || tms9918->config[PICO9918_CONF_DIAG_PALETTE] ||
221 tms9918->config[PICO9918_CONF_DIAG_PERFORMANCE] || tms9918->config[PICO9918_CONF_DIAG_REGISTERS]);
223 if (!PICO9918_HAS(tms9918, PICO9918_FEAT_CONFIG))
226 tms9918->configVdpDirty =
false;
231 if (tms9918->configVdpDirty)
233 tms9918->configVdpDirty =
false;
235 if (tms9918->config[PICO9918_CONF_CRT_SCANLINES])
241 1 << (tms9918->config[PICO9918_CONF_SCANLINE_SPRITES] + 2);
243 for (
int i = 0; i < 16; ++i)
245 uint16_t rgb = (tms9918->config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] << 8) |
246 tms9918->config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2) + 1];
247 tms9918->vram.map.pram[i] = __builtin_bswap16(rgb);
249 tms9918->palDirty = 1;
263 tms9918->configDirty =
true;
264 if (applyVdpEffects) tms9918->configVdpDirty =
true;
269 if (applyVdpEffects) tms9918->configVdpDirty =
true;
273 tms9918->configDirty =
false;
uint16_t pico9918_default_palette(int index)
a default palette entry, 0xargb
#define PICO9918_R50_VSCANLINES
F18A only: dim every second raster line.
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
#define PICO9918_INST_ONLY
pass the instance as the only argument
@ PICO9918_REG_ENHANCED2
GPU triggers, per-position attributes, layer priority.
@ PICO9918_REG_MAX_SCAN_SPRITES
sprites drawn per scanline before the limit bites
#define PICO9918_INST_ONLY_ARG
declare the instance as the only parameter
void pico9918_config_pending_capture(const uint8_t config[PICO9918_CONFIG_BYTES], uint8_t *record)
copy the live tracked fields into a PICO9918_PENDING_RECORD_BYTES record
void pico9918_config_apply(pico9918_t *tms9918)
apply the config block's VDP-side effects: registers 50 and 30, the palette unpack,...
uint8_t * pico9918_config(pico9918_t *tms9918)
the instance's PICO9918_CONFIG_BYTES settings block
void pico9918_config_schedule_apply(pico9918_t *tms9918, bool applyVdpEffects)
ask for the block to be applied at the next end of frame
void pico9918_config_defaults(uint8_t config[PICO9918_CONFIG_BYTES])
write a complete, valid settings block: every field at its default
void pico9918_config_set_applied_callback(pico9918_t *tms9918, pico9918_config_applied_fn cb, void *userdata)
register the host's config-applied hook
bool pico9918_config_validate(uint8_t config[PICO9918_CONFIG_BYTES], uint8_t hwVersion)
validate a config block just read from host storage, and stamp its identity
void pico9918_config_refresh_pending_mirror(uint8_t config[PICO9918_CONFIG_BYTES], uint8_t state)
copy live tracked fields into the in-RAM pending mirror with the given state
void pico9918_config_prepare_save(uint8_t config[PICO9918_CONFIG_BYTES], uint8_t hwVersion)
stamp the identity and the initialised marker into a block about to be persisted
void pico9918_config_apply_now(pico9918_t *tms9918, bool applyVdpEffects)
apply the block now, and cancel any apply already owed
void pico9918_config_pending_restore(uint8_t config[PICO9918_CONFIG_BYTES], const uint8_t *record)
copy a pending record's field slots back over the live config
#define PICO9918_BASE_V9938
the V9938 base
#define PICO9918_PENDING_MIRROR_NONE
pendingMirror value for a field outside the confirmation flow
#define PICO9918_BASE_TMS9918
vdpBase values - the render base selected by PICO9918_CONF_VDP_BASE
#define PICO9918_CONFIG_BYTES
size of the config block, in bytes
pico9918-core - the private instance layout
one config field's descriptor
uint8_t offset
the field's config byte index
uint8_t defaultValue
what a reset or a migration writes
uint8_t pendingMirror
PICO9918_CONF_PENDING_* offset, or PICO9918_PENDING_MIRROR_NONE.