pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
Loading...
Searching...
No Matches
pico9918_config.c File Reference

pico9918-core - Config semantics More...

#include "impl/pico9918_priv.h"
#include <string.h>
+ Include dependency graph for pico9918_config.c:

Go to the source code of this file.

Macros

#define CONFIG_APPLIED_CB   tms9918->configApplied
 

Functions

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
 
PICO9918_INLINE_HOT uint16_t configStoredVersion (const uint8_t *config)
 
static bool configOutOfRange (const uint8_t *config)
 
void pico9918_config_defaults (uint8_t config[CONFIG_BYTES])
 write a complete, valid settings block: every field at its default
 
static void migrateNewFields (uint8_t *config, uint16_t storedVer)
 
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
 
uint8_t * pico9918_config (pico9918_t *tms9918)
 the instance's CONFIG_BYTES settings block
 
void pico9918_config_set_applied_callback (pico9918_t *tms9918, pico9918_config_applied_fn cb, void *userdata)
 register the host's config-applied hook
 
static void configAppliedFire (pico9918_t *tms9918)
 
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
 

Variables

const pico9918_config_field_t pico9918_config_fields []
 
const size_t pico9918_config_field_count = sizeof(pico9918_config_fields) / sizeof(pico9918_config_fields[0])
 

Detailed Description

pico9918-core - Config semantics

Copyright (c) 2021 Troy Schrapel

This code is licensed under the MIT license

https://github.com/visrealm/pico9918-core

Portable half of the config block: the field descriptor table, the validation / defaults / migration driver, the pending mirror refresh, and the VDP-side apply. Host storage (flash), host detection (SCART, hardware version) and host-side apply effects (VGA scanlines) live in the host.

Definition in file pico9918_config.c.

Macro Definition Documentation

◆ CONFIG_APPLIED_CB

#define CONFIG_APPLIED_CB   tms9918->configApplied

Definition at line 181 of file pico9918_config.c.

Function Documentation

◆ pico9918_config_refresh_pending_mirror()

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.

◆ pico9918_config_pending_capture()

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

Note
record[0], the state, is the caller's - only the field slots are written

Definition at line 51 of file pico9918_config.c.

References pico9918_config_field_t::offset, PENDING_MIRROR_NONE, and pico9918_config_field_t::pendingMirror.

◆ pico9918_config_pending_restore()

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.

◆ configStoredVersion()

PICO9918_INLINE_HOT uint16_t configStoredVersion ( const uint8_t *  config)

Definition at line 71 of file pico9918_config.c.

◆ configOutOfRange()

static bool configOutOfRange ( const uint8_t *  config)
static

Definition at line 76 of file pico9918_config.c.

◆ pico9918_config_defaults()

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().

◆ migrateNewFields()

static void migrateNewFields ( uint8_t *  config,
uint16_t  storedVer 
)
static

Definition at line 105 of file pico9918_config.c.

◆ 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().

◆ pico9918_config_prepare_save()

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.

◆ pico9918_config()

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.

◆ pico9918_config_set_applied_callback()

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.

◆ configAppliedFire()

static void configAppliedFire ( pico9918_t *  tms9918)
inlinestatic

Definition at line 192 of file pico9918_config.c.

◆ pico9918_config_apply()

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().

◆ pico9918_config_schedule_apply()

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.

◆ pico9918_config_apply_now()

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.

Variable Documentation

◆ pico9918_config_fields

const pico9918_config_field_t pico9918_config_fields[]
Initial value:
= {
{PICO9918_CONF_CRT_SCANLINES, 1, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_SCANLINE_SPRITES, 3, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_CLOCK_PRESET_ID, 2, 0, PICO9918_CONF_PENDING_CLOCK_PRESET, 0x1000},
{PICO9918_CONF_SCART_MODE, 1, 0, PICO9918_CONF_PENDING_SCART_MODE, 0x1200},
{PICO9918_CONF_VDP_DEVICE, 3, 0, PENDING_MIRROR_NONE, 0x1101},
{PICO9918_CONF_DISP_DRIVER_PREF, 2, 0, PICO9918_CONF_PENDING_DRIVER_PREF, 0x1200},
{PICO9918_CONF_VGA_MODE, 0, 0, PICO9918_CONF_PENDING_VGA_MODE, 0x1200},
{PICO9918_CONF_DIAG_REGISTERS, 1, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_DIAG_PERFORMANCE, 1, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_DIAG_PALETTE, 1, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_DIAG_ADDRESS, 1, 0, PENDING_MIRROR_NONE, 0x1000},
{PICO9918_CONF_VDP_BASE, 1, PICO9918_BASE_TMS9918, PENDING_MIRROR_NONE, 0x1300},
}
#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

Definition at line 21 of file pico9918_config.c.

◆ pico9918_config_field_count

const size_t pico9918_config_field_count = sizeof(pico9918_config_fields) / sizeof(pico9918_config_fields[0])

Definition at line 39 of file pico9918_config.c.