|
pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
|
pico9918-core - config byte layout More...
Include dependency graph for pico9918_config.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Data Structures | |
| struct | pico9918_config_field_t |
| one config field's descriptor More... | |
Macros | |
| #define | PICO9918_CONFIG_BYTES 256 |
| size of the config block, in bytes | |
| #define | PICO9918_CONFIG_FIRST_SETTABLE 8 |
| the first config byte a guest may write through VR58/59 | |
| #define | PICO9918_BASE_TMS9918 0x00 |
| vdpBase values - the render base selected by PICO9918_CONF_VDP_BASE | |
| #define | PICO9918_BASE_V9938 0x01 |
| the V9938 base | |
| #define | PICO9918_PENDING_RECORD_BYTES (PICO9918_CONF_PENDING_CLOCK_PRESET - PICO9918_CONF_PENDING_STATE + 1) |
| bytes in a pending record: the state, then one slot per tracked field | |
| #define | PICO9918_PENDING_MIRROR_NONE 0xFF |
| pendingMirror value for a field outside the confirmation flow | |
Enumerations | |
| enum | pico9918_config_model_t { PICO9918_MODEL_RP2040 = 1 , PICO9918_MODEL_RP2350 = 2 } |
| PICO9918_CONF_PICO_MODEL values, derived from the board revision. More... | |
| enum | pico9918_config_option_t { PICO9918_CONF_PICO_MODEL = 0 , PICO9918_CONF_HW_VERSION = 1 , PICO9918_CONF_SW_VERSION = 2 , PICO9918_CONF_SW_PATCH_VERSION = 3 , PICO9918_CONF_CLOCK_TESTED = 4 , PICO9918_CONF_DISP_DRIVER = 5 , PICO9918_CONF_FLASH_STATUS = 6 , PICO9918_CONF_CRT_SCANLINES = 8 , PICO9918_CONF_SCANLINE_SPRITES = 9 , PICO9918_CONF_CLOCK_PRESET_ID = 10 , PICO9918_CONF_SCART_MODE = 11 , PICO9918_CONF_VDP_DEVICE = 12 , PICO9918_CONF_DISP_DRIVER_PREF = 13 , PICO9918_CONF_VGA_MODE = 14 , PICO9918_CONF_VDP_BASE = 15 , PICO9918_CONF_DIAG = 16 , PICO9918_CONF_DIAG_REGISTERS = 17 , PICO9918_CONF_DIAG_PERFORMANCE = 18 , PICO9918_CONF_DIAG_PALETTE = 19 , PICO9918_CONF_DIAG_ADDRESS = 20 , PICO9918_CONF_PALETTE_IDX_0 = 128 , PICO9918_CONF_PALETTE_IDX_15 = PICO9918_CONF_PALETTE_IDX_0 + 32 , PICO9918_CONF_PENDING_STATE = 200 , PICO9918_CONF_PENDING_DRIVER_PREF = 201 , PICO9918_CONF_PENDING_VGA_MODE = 202 , PICO9918_CONF_PENDING_SCART_MODE = 203 , PICO9918_CONF_PENDING_CLOCK_PRESET = 204 , PICO9918_CONF_SAVE_FORCED = 252 , PICO9918_CONF_PENDING_CANCEL = 253 , PICO9918_CONF_PENDING_CONFIRM = 254 , PICO9918_CONF_SAVE_TO_FLASH = 255 } |
| every claimed config byte, by index. More... | |
| enum | pico9918_pending_state_t { PICO9918_PENDING_STATE_CONFIRMED = 0xC0 , PICO9918_PENDING_STATE_PENDING = 0x9E , PICO9918_PENDING_STATE_ARMED = 0xA0 } |
| PICO9918_CONF_PENDING_STATE values, and the state byte of a host's stored pending record. More... | |
Functions | |
| uint8_t * | pico9918_config (pico9918_t *tms9918) |
| the instance's PICO9918_CONFIG_BYTES settings block | |
| void | pico9918_config_defaults (uint8_t config[PICO9918_CONFIG_BYTES]) |
| write a complete, valid settings block: every field at its default | |
| 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_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_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_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_pending_restore (uint8_t config[PICO9918_CONFIG_BYTES], const uint8_t *record) |
| copy a pending record's field slots back over the live config | |
| 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_apply_now (pico9918_t *tms9918, bool applyVdpEffects) |
| apply the block now, and cancel any apply already owed | |
| void | pico9918_config_set_applied_callback (pico9918_t *tms9918, pico9918_config_applied_fn cb, void *userdata) |
| register the host's config-applied hook | |
Variables | |
| const pico9918_config_field_t | pico9918_config_fields [] |
| the descriptor table. | |
| const size_t | pico9918_config_field_count |
| how many rows pico9918_config_fields has | |
pico9918-core - config byte layout
Copyright (c) 2021 Troy Schrapel
This code is licensed under the MIT license
https://github.com/visrealm/pico9918-core
Purpose: THE single authoritative layout of the 256-byte config block, plus the portable semantics over it (field descriptors, validation, defaults, per-version migration, and the VDP-side apply).
This header owns the config-byte ABI. It is shared by:
Nothing else may declare a PICO9918_CONF_* byte index. If a new byte is needed, it is claimed here and nowhere else.
This header must stay free of host dependencies - no firmware headers, no board headers, no SDK includes. The version a block is stamped with is this library's own, from the generated pico9918_build_config.h, because it is compared against the field table below and the two must come from one build. Only the board revision, which nothing here can measure, arrives as a parameter.
The following bytes are deployed in flash on real units. Their indices are frozen and must NEVER be reassigned to a different meaning - doing so makes existing units come up with corrupted settings:
0-6, 8-14, 16-20, 128-160, 200-204, 252-255
Free for future claims: 7, 15 (claimed below), 21-127, 161-199, 205-245.
Byte 7 is deliberately left unused: it falls in the "not settable via registers" identity/version band (0-6) that the configurator may treat as reserved, and the firmware's version-match load path does not clear it.
The configurator uses 246-255 as menu-sentinel IDs in the SAME numeric space as these config indices (CONF_MENU_OUTPUT = 246 .. CONF_MENU_EMPTY = 255). The overlap at 252-255 is deliberate and already shipping. Do not claim 246-251 for a real config byte without first checking the configurator's sentinel list - a collision there breaks menu dispatch.
Definition in file pico9918_config.h.
| #define PICO9918_CONFIG_BYTES 256 |
size of the config block, in bytes
Definition at line 65 of file pico9918_config.h.
| #define PICO9918_CONFIG_FIRST_SETTABLE 8 |
the first config byte a guest may write through VR58/59
Below it is the identity/version band the host stamps - see the ABI FREEZE note above. The register path enforces this same boundary.
Definition at line 73 of file pico9918_config.h.
| #define PICO9918_BASE_TMS9918 0x00 |
vdpBase values - the render base selected by PICO9918_CONF_VDP_BASE
the TMS9918A base, which the F18A unlock extends
Definition at line 76 of file pico9918_config.h.
| #define PICO9918_BASE_V9938 0x01 |
the V9938 base
Definition at line 77 of file pico9918_config.h.
| #define PICO9918_PENDING_RECORD_BYTES (PICO9918_CONF_PENDING_CLOCK_PRESET - PICO9918_CONF_PENDING_STATE + 1) |
bytes in a pending record: the state, then one slot per tracked field
LOAD-BEARING: a tracked field's slot is its pendingMirror less PICO9918_CONF_PENDING_STATE, so a host's stored record and the in-RAM mirror band are the same layout. pico9918_config_pending_capture() and _restore() are built on it, as is every tool that writes the record, so claiming a new mirror byte means naming it here.
Definition at line 167 of file pico9918_config.h.
| #define PICO9918_PENDING_MIRROR_NONE 0xFF |
pendingMirror value for a field outside the confirmation flow
Definition at line 180 of file pico9918_config.h.
PICO9918_CONF_PICO_MODEL values, derived from the board revision.
A frozen ABI, and load-bearing beyond a label: the configurator selects which firmware image to flash from this byte, so a wrong value offers a unit the wrong image.
Definition at line 85 of file pico9918_config.h.
every claimed config byte, by index.
The values are a frozen ABI
Definition at line 92 of file pico9918_config.h.
PICO9918_CONF_PENDING_STATE values, and the state byte of a host's stored pending record.
A frozen ABI - the configurator reads it out of byte 200
CONFIRMED -> PENDING (host saves) -> ARMED (host boots with it) -> CONFIRMED (the user accepts, or the next boot reverts)
Definition at line 151 of file pico9918_config.h.
| uint8_t * pico9918_config | ( | pico9918_t * | tms9918 | ) |
the instance's PICO9918_CONFIG_BYTES settings block
The same bytes pico9918_config_validate() checks and pico9918_config_apply_now() acts on, so a host reads its stored block into this and applies it. Persistence stays the host's - the library never reaches storage - and so does the decision to write, since these are settings a user chose rather than VDP state.
Definition at line 184 of file pico9918_config.c.
| void pico9918_config_defaults | ( | uint8_t | config[PICO9918_CONFIG_BYTES] | ) |
write a complete, valid settings block: every field at its default
What a host wants when it has nothing stored, and the reason it should not simply zero the block: the field defaults happen to be zero today, but the palette's are not, and applying the block unpacks those bytes into the live palette. A zeroed block therefore renders black. This also sets the initialised marker that pico9918_config_validate() looks for, so a block from here survives it untouched.
The identity bytes at 0-3 are cleared with the rest; pico9918_config_validate() and pico9918_config_prepare_save() are where they are stamped back in.
Definition at line 96 of file pico9918_config.c.
References pico9918_config_field_t::defaultValue, pico9918_config_field_t::offset, PICO9918_CONFIG_BYTES, and pico9918_default_palette().
Referenced by pico9918_config_validate().
| 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
Resets the block to defaults if it is uninitialised or holds an out-of-range field; then defaults the fields introduced since the stored version. Either way the identity bytes end up describing this build and the command bytes a host persisted are cleared.
A block carrying another model's identity is NOT reset. The byte layout is the same on both, so the identity is corrected and the settings kept - which is what lets a host offering a runtime tier switch keep one stored block across it, instead of the user losing their settings on every toggle.
Three of the four identity bytes are this library's own: the version pair is the version it was compiled at, which is the only number the field table's introducedIn can be compared against, and the model follows hwVersion. Passing them in is what let a host one release behind leave the fields added since at whatever the stored block held.
hwVersion is the board revision, which only a host can measure - byte 1 as the configurator decodes it, major in the high nibble and minor in the low (0x03, 0x10, 0x20). A major of 2 or above is the PRO tier, and that is what byte 0 is derived from, so a value outside this encoding stamps the wrong model.
Returns true if the block changed in a way the host should persist. A host running the configurator protocol can ignore that: PICO9918_CONF_SAVE_FORCED is set on the same path, which is the save request its GPU loop already dispatches.
Definition at line 127 of file pico9918_config.c.
References pico9918_config_defaults().
| 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
The marker is how pico9918_config_validate() tells a stored block from an erased one, so a host that persists a block without this gets a factory reset on its next boot. Host storage is untouched - this only prepares the bytes. hwVersion is encoded as pico9918_config_validate() describes.
The command bytes are cleared here too, including the forced-save one pico9918_config_validate() raises: a command is a request to the run that made it, never a stored setting. A host does not need to clear them itself before persisting.
Definition at line 162 of file pico9918_config.c.
| 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
Definition at line 41 of file pico9918_config.c.
References pico9918_config_field_t::offset, pico9918_config_field_t::pendingMirror, and PICO9918_PENDING_MIRROR_NONE.
| 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
Definition at line 51 of file pico9918_config.c.
References pico9918_config_field_t::offset, pico9918_config_field_t::pendingMirror, and PICO9918_PENDING_MIRROR_NONE.
| 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
Definition at line 61 of file pico9918_config.c.
References pico9918_config_field_t::offset, pico9918_config_field_t::pendingMirror, and PICO9918_PENDING_MIRROR_NONE.
| void pico9918_config_schedule_apply | ( | pico9918_t * | tms9918, |
| bool | applyVdpEffects | ||
| ) |
ask for the block to be applied at the next end of frame
The deferred form, and what a device wants: the apply seeds registers and republishes the palette, so doing it mid-frame would show on the line being scanned out. applyVdpEffects asks for that reseeding; without it the apply runs its host-side and derived effects only.
A host that has no frame boundary to wait for wants pico9918_config_apply_now().
Definition at line 261 of file pico9918_config.c.
| void pico9918_config_apply_now | ( | pico9918_t * | tms9918, |
| bool | applyVdpEffects | ||
| ) |
apply the block now, and cancel any apply already owed
For a host that has just written the block itself - a configurator front end, or a consumer stepping the library a frame at a time - and would rather see the effects than wait for a boundary it does not have. The deferred request is cleared, so the next end of frame does not apply the same block a second time.
Also where a host lands after pico9918_set_chip(), which schedules an apply rather than performing one.
Definition at line 267 of file pico9918_config.c.
References pico9918_config_apply(), and PICO9918_INST_ONLY.
| void pico9918_config_set_applied_callback | ( | pico9918_t * | tms9918, |
| pico9918_config_applied_fn | cb, | ||
| void * | userdata | ||
| ) |
register the host's config-applied hook
Fires from the apply itself, which the frame module reaches where the configDirty flag is actually consumed - the end-of-frame interrupt, not the scanline body - so it is per-frame at worst and a function pointer is permitted. It exists so a host's own apply effects stay in lockstep with the library's register and palette effects, instead of the host having to watch configDirty itself.
Called LAST, after the VDP-side effects, and on every personality: a host effect is the host's to gate, and one derived from a register has to read the value this call may just have seeded. NULL (the default) means the host has no such effects and nothing is called.
Registered per instance in a multi-instance build - see pico9918.h for why the two builds take different shapes.
Definition at line 202 of file pico9918_config.c.
| const pico9918_config_field_t pico9918_config_fields[] |
the descriptor table.
The host save path reads pendingMirror/max/ defaultValue from it
Definition at line 203 of file pico9918_config.h.
| const size_t pico9918_config_field_count |
how many rows pico9918_config_fields has
Definition at line 206 of file pico9918_config.h.