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];
76static bool configOutOfRange(
const uint8_t* config)
78 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
80 if (config[pico9918_config_fields[i].offset] > pico9918_config_fields[i].max)
return true;
89 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
95 for (
int i = 1; i < 16; ++i)
98 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] = rgb >> 8;
99 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2) + 1] = rgb & 0xff;
105static void migrateNewFields(uint8_t* config, uint16_t storedVer)
107 for (
size_t i = 0; i < pico9918_config_field_count; ++i)
109 if (pico9918_config_fields[i].introducedIn > storedVer)
118 uint16_t storedVer = configStoredVersion(config);
120 if (config[PICO9918_CONF_PICO_MODEL] !=
id.picoModel || config[PICO9918_CONF_PALETTE_IDX_0] != 0x00 ||
121 (config[PICO9918_CONF_PALETTE_IDX_0 + 2] & 0xf0) != 0xf0 ||
122 configOutOfRange(config))
129 config[PICO9918_CONF_PICO_MODEL] =
id.picoModel;
130 config[PICO9918_CONF_HW_VERSION] =
id.hwVersion;
133 config[PICO9918_CONF_SAVE_FORCED] = 0;
134 config[PICO9918_CONF_PENDING_CANCEL] = 0;
135 config[PICO9918_CONF_PENDING_CONFIRM] = 0;
136 config[PICO9918_CONF_SAVE_TO_FLASH] = 0;
138 if (storedVer == (((uint16_t)
id.swVersion << 8) |
id.swPatch))
return false;
140 migrateNewFields(config, storedVer);
142 config[PICO9918_CONF_SW_VERSION] =
id.swVersion;
143 config[PICO9918_CONF_SW_PATCH_VERSION] =
id.swPatch;
146 config[PICO9918_CONF_SAVE_FORCED] = 1;
152 config[PICO9918_CONF_PICO_MODEL] =
id.picoModel;
153 config[PICO9918_CONF_HW_VERSION] =
id.hwVersion;
154 config[PICO9918_CONF_SW_VERSION] =
id.swVersion;
155 config[PICO9918_CONF_SW_PATCH_VERSION] =
id.swPatch;
158 config[PICO9918_CONF_PALETTE_IDX_0] = 0;
159 config[PICO9918_CONF_PALETTE_IDX_0 + 1] = 0;
160 for (
int i = 1; i < 16; ++i)
162 config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] |= 0xf0;
168 return tms9918->config;
173#if PICO9918_SINGLE_INSTANCE
176 pico9918_config_applied_fn fn;
179#define CONFIG_APPLIED_CB configApplied
181#define CONFIG_APPLIED_CB tms9918->configApplied
186 CONFIG_APPLIED_CB.fn = cb;
187 CONFIG_APPLIED_CB.userdata = userdata;
194 if (CONFIG_APPLIED_CB.fn) CONFIG_APPLIED_CB.fn(tms9918, CONFIG_APPLIED_CB.userdata);
200 tms9918->config[PICO9918_CONF_DIAG] =
201 PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY) &&
202 (tms9918->config[PICO9918_CONF_DIAG_ADDRESS] || tms9918->config[PICO9918_CONF_DIAG_PALETTE] ||
203 tms9918->config[PICO9918_CONF_DIAG_PERFORMANCE] || tms9918->config[PICO9918_CONF_DIAG_REGISTERS]);
205 if (!PICO9918_HAS(tms9918, PICO9918_FEAT_CONFIG))
208 tms9918->configVdpDirty =
false;
213 if (tms9918->configVdpDirty)
215 tms9918->configVdpDirty =
false;
217 if (tms9918->config[PICO9918_CONF_CRT_SCANLINES])
223 1 << (tms9918->config[PICO9918_CONF_SCANLINE_SPRITES] + 2);
225 for (
int i = 0; i < 16; ++i)
227 uint16_t rgb = (tms9918->config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2)] << 8) |
228 tms9918->config[PICO9918_CONF_PALETTE_IDX_0 + (i * 2) + 1];
229 tms9918->vram.map.pram[i] = __builtin_bswap16(rgb);
231 tms9918->palDirty = 1;
245 tms9918->configDirty =
true;
246 if (applyVdpEffects) tms9918->configVdpDirty =
true;
251 if (applyVdpEffects) tms9918->configVdpDirty =
true;
255 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_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 CONFIG_BYTES settings block
bool pico9918_config_validate(uint8_t config[CONFIG_BYTES], pico9918_config_host_id_t id)
validate a config block just read from host storage, and stamp id into it
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_set_applied_callback(pico9918_t *tms9918, pico9918_config_applied_fn cb, void *userdata)
register the host's config-applied hook
void pico9918_config_prepare_save(uint8_t config[CONFIG_BYTES], pico9918_config_host_id_t id)
stamp id and the initialised marker into a block about to be persisted
void pico9918_config_pending_restore(uint8_t config[CONFIG_BYTES], const uint8_t *record)
copy a pending record's field slots back over the live config
void pico9918_config_pending_capture(const uint8_t config[CONFIG_BYTES], uint8_t *record)
copy the live tracked fields into a PICO9918_PENDING_RECORD_BYTES record
void pico9918_config_apply_now(pico9918_t *tms9918, bool applyVdpEffects)
apply the block now, and cancel any apply already owed
void pico9918_config_refresh_pending_mirror(uint8_t config[CONFIG_BYTES], uint8_t state)
copy live tracked fields into the in-RAM pending mirror with the given state
void pico9918_config_defaults(uint8_t config[CONFIG_BYTES])
write a complete, valid settings block: every field at its default
#define PICO9918_BASE_V9938
the V9938 base
#define CONFIG_BYTES
size of the config block, in bytes
#define PICO9918_BASE_TMS9918
vdpBase values - the render base selected by PICO9918_CONF_VDP_BASE
#define PENDING_MIRROR_NONE
pendingMirror value for a field outside the confirmation flow
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 PENDING_MIRROR_NONE.
the identity bytes at 0-3, which only the host knows