34int pico9918_frame_count = 0;
35int pico9918_dropped_frames_count = 0;
36static bool dropped_frames[16] = {0};
40int pico9918_v_pixels = 192;
41uint32_t pico9918_v_border = 0;
42bool pico9918_valid_writes =
false;
45uint8_t pico9918_v_scale = 2;
46uint16_t pico9918_v_virtual = 240;
48#if PICO9918_DIAG_GPU_FRAME_COUNTER
52uint32_t pico9918_gpu_frame_count = 0;
58#if PICO9918_SINGLE_INSTANCE
61 pico9918_config_reload_fn fn;
64#define CONFIG_RELOAD_CB configReload
66#define CONFIG_RELOAD_CB tms9918->configReload
78PICO9918_SECTION_SCRATCH_Y(buffer) uint32_t pico9918_border_bg;
82 CONFIG_RELOAD_CB.fn = cb;
83 CONFIG_RELOAD_CB.userdata = userdata;
88 if (CONFIG_RELOAD_CB.fn) CONFIG_RELOAD_CB.fn(tms9918, CONFIG_RELOAD_CB.userdata);
104 PICO9918_HOST_ENTER_CRITICAL();
111 currentStatus |= (tempStatus & 0xe0);
115 currentStatus = (currentStatus & 0xe0) | tempStatus;
124 PICO9918_HOST_STATUS_VISIBLE();
127 PICO9918_HOST_EXIT_CRITICAL();
133 tms9918->vram.map.blanking = 1;
134 tms9918->vram.map.scanline = 255;
155 if (tms9918->configDirty)
157 tms9918->configDirty =
false;
173 pico9918_dropped_frames_count += droppedFrame - dropped_frames[pico9918_frame_count & 0xf];
174 dropped_frames[pico9918_frame_count & 0xf] = droppedFrame;
206 const int yScale = display->interlaced ? 1 : 2;
211 display->vPixelScale = yScale - (bool)doubleRows;
212 display->vVirtualPixels = (display->displayPixels / yScale) << (
bool)doubleRows;
216 pico9918_v_scale = display->vPixelScale;
217 pico9918_v_virtual = display->vVirtualPixels;
220 pico9918_v_pixels = baseRows << 3;
221 if (yScale > 1 && (TMS_REGISTER(tms9918, TMS_REG_0) &
TMS_R0_DOUBLE_ROWS)) pico9918_v_pixels <<= 1;
222 pico9918_v_border = (display->vVirtualPixels - pico9918_v_pixels) / 2;
244 ++pico9918_frame_count;
245#if PICO9918_DIAG_GPU_FRAME_COUNTER
246 pico9918_gpu_frame_count += (TMS_STATUS(tms9918,
PICO9918_SR_GPU) & 0x80) != 0;
253 static float tempAccum = 0.0f;
255 if ((pico9918_frame_count & 0x3f) == 0)
259 uint8_t t4 = (uint8_t)(tempAccum * 4.0f + 0.5f);
265 if (!pico9918_valid_writes)
268 if ((pico9918_valid_writes = (TMS_REGISTER(tms9918, TMS_REG_1) &
TMS_R1_DISP_ACTIVE)) != 0)
279 if (tms9918->config[PICO9918_CONF_DIAG] && PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY))
329 const uint32_t halfHBorder = (params->hVirtualPixels -
TMS9918_PIXELS_X * 2) / 4;
332 const uint8_t field = (y >> 12) & 1;
335 uint32_t* dPixels = (uint32_t*)pixels;
339 const bool packedNibbles =
341 pico9918_border_bg = pico9918_palette_lut
342 [(TMS_REGISTER(tms9918, TMS_REG_FG_BG_COLOR) & 0x0f) |
350 PICO9918_FILL32_WAIT(PICO9918_FILL_BORDER);
358 PICO9918_FILL32_SET_COUNT(PICO9918_FILL_BORDER, params->hVirtualPixels / 2);
359 PICO9918_FILL32_TRIGGER(PICO9918_FILL_BORDER, dPixels);
360 tms9918->vram.map.blanking = 1;
367 if (PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY) &&
371 PICO9918_FILL32_WAIT(PICO9918_FILL_BORDER);
379 tms9918->config[PICO9918_CONF_DIAG] =
true;
380 tms9918->config[PICO9918_CONF_DIAG_REGISTERS] =
true;
381 tms9918->config[PICO9918_CONF_DIAG_PERFORMANCE] =
true;
382 tms9918->config[PICO9918_CONF_DIAG_PALETTE] =
true;
383 tms9918->config[PICO9918_CONF_DIAG_ADDRESS] =
true;
403 uint32_t lineStart = PICO9918_HOST_TIME_US();
406 tms9918->vram.map.blanking = 0;
407 tms9918->vram.map.scanline = y;
411 PICO9918_FILL32_SET_COUNT(PICO9918_FILL_BORDER, halfHBorder);
412 PICO9918_FILL32_TRIGGER(PICO9918_FILL_BORDER, dPixels);
418 pico9918_frame_map_line_impl(
PICO9918_INST y, field, params->interlaced, params->interlacedFieldOrder);
421 PICO9918_LINE_CAPTURE_WAIT();
423 uint32_t renderTime = PICO9918_HOST_TIME_US();
425 renderTime = PICO9918_HOST_TIME_US() - renderTime;
428 PICO9918_LINE_CAPTURE(y, pico9918_v_pixels_impl(
PICO9918_INST_ONLY), lineBytes, lineSource);
433 if (tms9918->vram.map.scanline &&
446 PICO9918_FILL32_WAIT(PICO9918_FILL_BORDER);
448 tms9918->vram.map.blanking = 1;
451#if PICO9918_TEXT80_8BPP
453 PICO9918_EXPAND_INDEXED_WIDE(dPixels + halfHBorder, lineSource, lineBytes, pico9918_palette_lut);
456 PICO9918_EXPAND_INDEXED(dPixels + halfHBorder, lineSource,
TMS9918_PIXELS_X, pico9918_palette_lut);
459 PICO9918_FILL32_TRIGGER(PICO9918_FILL_BORDER, dPixels + halfHBorder +
TMS9918_PIXELS_X);
461 if (tms9918->config[PICO9918_CONF_DIAG] && PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY))
463 PICO9918_FILL32_WAIT(PICO9918_FILL_BORDER);
465 params->vVirtualPixels, pixels);
468 PICO9918_LINE_NOTE_TIME(y, PICO9918_HOST_TIME_US() - lineStart);
470 if (tms9918->config[PICO9918_CONF_DIAG_PERFORMANCE])
487 uint32_t* pairs = (uint32_t*)pixels;
488 for (uint32_t i = 0; i < count / 2; ++i) pairs[i] = PICO9918_PIXEL_PAIR_DIM(pairs[i]);
497 const uint32_t scale = pico9918_v_scale ? pico9918_v_scale : 1;
502 if (outputLine % scale == 0)
504 const uint16_t y = (uint16_t)(outputLine / scale);
507 tms9918->config[PICO9918_CONF_DIAG] && PICO9918_HAS(tms9918, PICO9918_FEAT_OVERLAY))
515#if PICO9918_BUILD_RUNTIME_CHIP
517 changed |= pico9918_f18a_badge_render((uint16_t)outputLine,
void pico9918_diag_set_temperature(float tempC)
core temperature, degrees C
void pico9918_diag_update(pico9918_t *tms9918, uint32_t frameCount)
recompute the panel values - call once per frame
void pico9918_diag_config_updated(pico9918_t *tms9918)
rebuild the panel row table - call whenever the PICO9918_CONF_DIAG* bytes change
void pico9918_diag_render(pico9918_t *tms9918, uint16_t y, uint32_t vVirtualPixels, PICO9918_PIXEL_T *pixels)
render the diagnostics panels for border row y
void pico9918_diag_set_frame_rate(float frameRateHz)
Host display timing, Hz.
void pico9918_diag_update_render_time(uint32_t renderTime, uint32_t frameTime)
accumulate one scanline's render and total time, in microseconds
pico9918-core - Diagnostics overlay
uint8_t pico9918_scan_line(pico9918_t *tms9918, uint16_t y)
generate a scanline
#define PICO9918_SR0_5S
more sprites on a line than the limit allows
#define PICO9918_R50_GPU_HSYNC
trigger the GPU every scanline
#define PICO9918_R50_VSCANLINES
F18A only: dim every second raster line.
#define PICO9918_SR1_BLANK
the raster is in blanking
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
#define PICO9918_INST_ONLY
pass the instance as the only argument
#define PICO9918_R24_TILE1_PS
tile layer 1 palette select
#define TMS_R1_DISP_ACTIVE
render the active display
#define PICO9918_R50_GPU_VSYNC
trigger the GPU every frame
#define PICO9918_SR0_INT
status register 0 bits.
@ PICO9918_REG_ENHANCED1
tile layer 2, 30-row mode, ECM levels, real Y
@ PICO9918_REG_ENHANCED2
GPU triggers, per-position attributes, layer priority.
@ PICO9918_REG_HORZ_INT_LINE
scanline the horizontal interrupt fires on
@ PICO9918_REG_PALETTE_SELECT
sub-palette for sprites and each tile layer
@ PICO9918_SR_RASTER_LINE
the line currently being drawn
@ PICO9918_SR_IDENT
chip identity, blanking, and the scanline interrupt flag
@ PICO9918_SR_TEMPERATURE
PICO9918 only: core temperature, as degrees C times four.
@ PICO9918_SR_GPU
GPU running and its status byte.
#define PICO9918_INST_ONLY_ARG
declare the instance as the only parameter
#define TMS9918_PIXELS_X
active display width, every mode
#define TMS_R0_DOUBLE_ROWS
PICO9918 only: twice the rows, drawn interlaced.
#define PICO9918_SR1_HF
status register 1 bits.
#define PICO9918_SR0_COLLISION
two sprites overlapped on an opaque pixel
#define PICO9918_R49_ROW30
30 rows of tiles rather than 24
@ PICO9918_CHIP_F18A
an F18A: unlock, enhanced renderer, GPU
#define PICO9918_INST
pass the instance ahead of other arguments
#define PICO9918_DLLEXPORT
the linkage every public entry point carries - see LINKAGE MODES above
void pico9918_config_apply(pico9918_t *tms9918)
apply the config block's VDP-side effects: registers 50 and 30, the palette unpack,...
bool pico9918_frame_output_line(pico9918_t *tms9918, uint32_t outputLine, pico9918_scanline_params_t *params, PICO9918_PIXEL_T *pixels)
see the header.
void pico9918_frame_porch(pico9918_t *tms9918)
see the header.
bool pico9918_frame_scanline(pico9918_t *tms9918, uint16_t y, const pico9918_scanline_params_t *params, PICO9918_PIXEL_T *pixels)
see the header.
void pico9918_frame_raise_end_of_frame_int(pico9918_t *tms9918)
see the header.
pico9918_frame_geometry_t pico9918_frame_end(pico9918_t *tms9918, float tempC, float frameRateHz, pico9918_frame_display_t *display)
see the header.
void pico9918_frame_end_of_scanline(pico9918_t *tms9918)
see the header.
void pico9918_frame_update_interrupts(pico9918_t *tms9918, uint8_t tempStatus)
merge newly raised status flags into the SR0 latch, publish it, and bring the /INT pin into agreement...
pico9918_frame_geometry_t pico9918_frame_geometry(pico9918_t *tms9918, pico9918_frame_display_t *display)
see the header.
pico9918-core - frame module
#define PICO9918_FRAME_STARTUP_FRAMES
frames of startup grace before an un-enabled display is taken to mean "nothing is driving this VDP",...
void pico9918_frame_set_config_reload_callback(pico9918_t *tms9918, pico9918_config_reload_fn cb, void *userdata)
register the host's late-config-reload hook
pico9918-core - GPU privileged surface
pico9918-core - the private instance layout
PICO9918_INLINE void pico9918_frame_sync_int_impl(pico9918_t *tms9918)
recompute the interrupt state and, only if it changed, drive the pin.
PICO9918_INLINE_HOT void pico9918_set_status_impl(pico9918_t *tms9918, uint8_t status)
set status flag
void pico9918_splash_allow_hide(void)
allow the splash to animate back out - the host calls this once the display has been enabled
void pico9918_splash_render(uint16_t y, uint32_t frameCount, uint32_t vBorder, uint32_t vPixels, uint32_t vVirtualPixels, PICO9918_PIXEL_T *pixels)
render the splash logo into the scanline buffer, if row y falls in the logo band.
pico9918-core - Splash overlay
the host's mutable vertical display parameters, as the end-of-frame geometry sees them
uint16_t vVirtualPixels
(in, and out when yScale > 1)
the vertical geometry the end of frame derives
uint32_t vBorder
top border offset, in virtual lines
uint32_t triggerScanline
vBorder + vPixels
int vPixels
active VDP display lines
the host's per-call display parameters, as the scanline sees them
uint16_t vVirtualPixels
virtual lines per field
uint16_t hVirtualPixels
full scanline width, guard pixels excluded