|
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... | |
| struct | pico9918_config_host_id_t |
| the identity bytes at 0-3, which only the host knows More... | |
Macros | |
| #define | CONFIG_BYTES 256 |
| size of the config block, in bytes | |
| #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 | PENDING_MIRROR_NONE 0xFF |
| pendingMirror value for a field outside the confirmation flow | |
Enumerations | |
| 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 CONFIG_BYTES settings block | |
| void | pico9918_config_defaults (uint8_t config[CONFIG_BYTES]) |
| write a complete, valid settings block: every field at its default | |
| 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_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_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_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_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_apply (pico9918_t *tms9918) |
| apply the config block's VDP-side effects: registers 50 and 30, the palette unpack, and the derived PICO9918_CONF_DIAG summary byte | |
| 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 PICO9918_* macros, no board headers, no SDK includes. Version numbers are host-owned and always arrive as parameters, never as compile-time macros.
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 CONFIG_BYTES 256 |
size of the config block, in bytes
Definition at line 63 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 66 of file pico9918_config.h.
| #define PICO9918_BASE_V9938 0x01 |
the V9938 base
Definition at line 67 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 145 of file pico9918_config.h.
| #define PENDING_MIRROR_NONE 0xFF |
pendingMirror value for a field outside the confirmation flow
Definition at line 158 of file pico9918_config.h.
every claimed config byte, by index.
The values are a frozen ABI
Definition at line 70 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 129 of file pico9918_config.h.
| uint8_t * pico9918_config | ( | pico9918_t * | tms9918 | ) |
the instance's CONFIG_BYTES settings block
The same bytes pico9918_config_validate() checks and pico9918_config_apply() 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 166 of file pico9918_config.c.
| void pico9918_config_defaults | ( | uint8_t | config[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 pico9918_config_apply() 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 a host's own identity is stamped in.
Definition at line 85 of file pico9918_config.c.
References CONFIG_BYTES, pico9918_config_field_t::defaultValue, pico9918_config_field_t::offset, and pico9918_default_palette().
Referenced by pico9918_config_validate().
| 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
Resets the block to defaults if it is not this host's, 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 at id and the command bytes a host persisted are cleared.
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 116 of file pico9918_config.c.
References pico9918_config_defaults().
| 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
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.
Definition at line 150 of file pico9918_config.c.
| 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
Definition at line 41 of file pico9918_config.c.
References pico9918_config_field_t::offset, PENDING_MIRROR_NONE, and pico9918_config_field_t::pendingMirror.
| 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
Definition at line 51 of file pico9918_config.c.
References pico9918_config_field_t::offset, PENDING_MIRROR_NONE, and pico9918_config_field_t::pendingMirror.
| 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
Definition at line 61 of file pico9918_config.c.
References pico9918_config_field_t::offset, PENDING_MIRROR_NONE, and pico9918_config_field_t::pendingMirror.
| void pico9918_config_apply | ( | pico9918_t * | tms9918 | ) |
apply the config block's VDP-side effects: registers 50 and 30, the palette unpack, and the derived PICO9918_CONF_DIAG summary byte
A settings block is a PICO9918 thing, so the effects land only on a personality that has the config port. On an F18A those registers and that palette are the guest's alone, and a block read from host storage must not touch them.
What it writes is a power-on default, not an owner: it runs when the block is loaded and after a reset has cleared the register file, and a later write to register 50 or 30 stands on every personality.
Host-side effects stay with the host.
Definition at line 197 of file pico9918_config.c.
References PICO9918_BASE_TMS9918, PICO9918_BASE_V9938, PICO9918_INST_ONLY, PICO9918_R50_VSCANLINES, PICO9918_REG_ENHANCED2, and PICO9918_REG_MAX_SCAN_SPRITES.
Referenced by pico9918_config_apply_now(), and pico9918_frame_raise_end_of_frame_int().
| 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 243 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.
Definition at line 249 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 pico9918_config_apply(), which the frame module calls 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 184 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 181 of file pico9918_config.h.
| const size_t pico9918_config_field_count |
how many rows pico9918_config_fields has
Definition at line 184 of file pico9918_config.h.