|
pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
|
pico9918-core - frame module More...
#include "impl/pico9918_priv.h"#include "pico9918_frame.h"#include "impl/pico9918_gpu_priv.h"#include "overlay/diag.h"#include "overlay/splash.h"
Include dependency graph for pico9918_frame.c:Go to the source code of this file.
Macros | |
| #define | CONFIG_RELOAD_CB tms9918->configReload |
Functions | |
| PICO9918_SECTION_SCRATCH_Y (buffer) | |
| static void | configReloadFire (pico9918_t *tms9918) |
| 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. | |
| void | pico9918_frame_porch (pico9918_t *tms9918) |
| see the header. | |
| void | pico9918_frame_raise_end_of_frame_int (pico9918_t *tms9918) |
| see the header. | |
| void | pico9918_frame_end_of_scanline (pico9918_t *tms9918) |
| see the header. | |
| pico9918_frame_geometry_t | pico9918_frame_geometry (pico9918_t *tms9918, pico9918_frame_display_t *display) |
| 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. | |
| bool | pico9918_frame_scanline (pico9918_t *tms9918, uint16_t y, const pico9918_scanline_params_t *params, PICO9918_PIXEL_T *pixels) |
| see the header. | |
| static bool | dimLine (pico9918_t *tms9918, PICO9918_PIXEL_T *pixels, uint32_t count, uint32_t outputLine) |
| bool | pico9918_frame_output_line (pico9918_t *tms9918, uint32_t outputLine, pico9918_scanline_params_t *params, PICO9918_PIXEL_T *pixels) |
| see the header. | |
Variables | |
| int | pico9918_frame_count = 0 |
| int | pico9918_dropped_frames_count = 0 |
| static bool | dropped_frames [16] = {0} |
| int | pico9918_v_pixels = 192 |
| uint32_t | pico9918_v_border = 0 |
| bool | pico9918_valid_writes = false |
| uint8_t | pico9918_v_scale = 2 |
| uint16_t | pico9918_v_virtual = 240 |
pico9918-core - frame module
Copyright (c) 2021 Troy Schrapel
This code is licensed under the MIT license
https://github.com/visrealm/pico9918-core
Per-frame / per-scanline sequencing: the interrupt/status latch merge, the vertical porch, the end-of-frame interrupt and trigger line, the true end of frame, and the vertical geometry it derives.
It lands in a .c rather than on the Impl inline surface because a real function here costs nothing extra: every call site already reaches it with a bl. Inlining it into its three callers - one of them the per-scanline path - would ADD per-scanline code, not remove a call. The entries the host reaches through a VGA function pointer are calls for the same reason.
Definition in file pico9918_frame.c.
| #define CONFIG_RELOAD_CB tms9918->configReload |
Definition at line 66 of file pico9918_frame.c.
| PICO9918_SECTION_SCRATCH_Y | ( | buffer | ) |
Definition at line 78 of file pico9918_frame.c.
|
inlinestatic |
Definition at line 86 of file pico9918_frame.c.
| 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.
merge newly raised status flags into the SR0 latch, publish it, and bring the /INT pin into agreement
See the header for the parameter contract.
The internal sequence is load-bearing at two points and must not be reordered:
Definition at line 102 of file pico9918_frame.c.
References pico9918_frame_sync_int_impl(), PICO9918_INST, PICO9918_INST_ONLY, pico9918_set_status_impl(), PICO9918_SR0_5S, PICO9918_SR0_COLLISION, and PICO9918_SR0_INT.
Referenced by pico9918_frame_raise_end_of_frame_int(), and pico9918_frame_scanline().
| void pico9918_frame_porch | ( | pico9918_t * | tms9918 | ) |
see the header.
vertical porch: blank the display and park the scanline counter at the F18A vsync value
Definition at line 131 of file pico9918_frame.c.
References PICO9918_SR_RASTER_LINE.
| void pico9918_frame_raise_end_of_frame_int | ( | pico9918_t * | tms9918 | ) |
see the header.
raise this frame's end-of-frame interrupt: latch doneInt, set the SR1 vsync bit, trigger the GPU if R50 bit 5 asks for it, consume a pending config change, then merge PICO9918_SR0_INT into the SR0 latch
The configDirty consumption is a CHECK-THEN-CLEAR on a non-volatile flag written from the other core (the register path sets it on core 1, the host's config load on core 0) and consumed here. Deliberately left alone: the worst case is a config apply deferred by one frame.
Definition at line 146 of file pico9918_frame.c.
References pico9918_config_apply(), pico9918_diag_config_updated(), pico9918_frame_update_interrupts(), PICO9918_INST, PICO9918_INST_ONLY, PICO9918_R50_GPU_VSYNC, PICO9918_REG_ENHANCED2, PICO9918_SR0_INT, PICO9918_SR1_BLANK, and PICO9918_SR_IDENT.
Referenced by pico9918_frame_end(), and pico9918_frame_end_of_scanline().
| void pico9918_frame_end_of_scanline | ( | pico9918_t * | tms9918 | ) |
see the header.
end-of-frame trigger line: if this frame has not raised its interrupt yet, account for the dropped frame and raise the interrupt
It takes no display line: the body never reads one.
Definition at line 168 of file pico9918_frame.c.
References pico9918_frame_raise_end_of_frame_int(), PICO9918_INST_ONLY, and PICO9918_SR0_INT.
| pico9918_frame_geometry_t pico9918_frame_geometry | ( | pico9918_t * | tms9918, |
| pico9918_frame_display_t * | display | ||
| ) |
see the header.
recompute the vertical display geometry from R0's double-rows bit and R49's row-30 bit, and publish it as this module's vPixels / vBorder
Three things about this expression are deliberately NOT tidied:
yScale - (bool)doubleRows, << (bool)doubleRows). The golden frame surface's reference deliberately decomposes this into explicit cases so the two do not share the algebra; rewriting it here to look like the reference would destroy that independence;yScale is derived from interlaced rather than from a build-time DISPLAY_YSCALE. A non-SCART build's DISPLAY_YSCALE is always 2 with interlaced false, so the interlace-derived form covers both cases exactly and the library needs no build-time host macro.
Definition at line 204 of file pico9918_frame.c.
References PICO9918_R49_ROW30, PICO9918_REG_ENHANCED1, TMS_R0_DOUBLE_ROWS, pico9918_frame_geometry_t::triggerScanline, pico9918_frame_geometry_t::vBorder, and pico9918_frame_geometry_t::vPixels.
Referenced by pico9918_frame_end().
| pico9918_frame_geometry_t pico9918_frame_end | ( | pico9918_t * | tms9918, |
| float | tempC, | ||
| float | frameRateHz, | ||
| pico9918_frame_display_t * | display | ||
| ) |
see the header.
true end of frame: advance the frame counter, fold in this frame's temperature reading, latch the first display enable, refresh the diagnostics panel, raise a still-owed end-of-frame interrupt, and recompute the geometry
It takes no frame number: the cadence and the thresholds all run off this module's own frame counter, which - unlike the VGA layer's frame number - resets on console reset.
The frame count is re-READ at each use rather than cached in a local. That is not a style choice: the host's tier-1 critical section deliberately does not mask the reset GPIO IRQ, which zeroes the counter, so a console reset landing mid-function is observable and a cached copy would hide it.
Definition at line 241 of file pico9918_frame.c.
References pico9918_diag_set_frame_rate(), pico9918_diag_set_temperature(), pico9918_diag_update(), pico9918_frame_geometry(), pico9918_frame_raise_end_of_frame_int(), PICO9918_FRAME_STARTUP_FRAMES, PICO9918_INST, PICO9918_INST_ONLY, pico9918_splash_allow_hide(), PICO9918_SR_GPU, PICO9918_SR_TEMPERATURE, TMS_R1_DISP_ACTIVE, and pico9918_frame_display_t::vVirtualPixels.
| bool pico9918_frame_scanline | ( | pico9918_t * | tms9918, |
| uint16_t | y, | ||
| const pico9918_scanline_params_t * | params, | ||
| PICO9918_PIXEL_T * | pixels | ||
| ) |
see the header.
generate one display scanline: border fill or active render, the F18A scanline and blanking registers, the R19 line interrupt, the GPU trigger, the splash and the palette LUT maintenance
The per-scanline path - the function every gate in this project exists to protect.
The host's pending-display banner is deliberately not here. It is host code - host flash state, a host trigger byte, centring against the host's own buffer width - and its only ordering constraints are pixel ones: after the border fill and the splash, before the diagnostics overlay. The host's overlay tail already sits there, and what runs between touches no pixels, so the framebuffer is bit-identical either way.
The return value is the border flag, and the host needs it to place that banner. "y is small" is not the same test: in row-30 progressive mode vBorder is 0, so the rows the banner occupies are active ones and a host that guessed would paint over the display.
FIVE THINGS HERE MUST NOT BE TIDIED:
bg store BEFORE the border WAIT. The previous line's right-border fill may still be reading the source word when this line overwrites it. Benign - the value is the same on all but the frame a background register changes - and "fixing" it by waiting first adds a per-scanline stall the goldens cannot see;TRAP: below is a recorded, user-ruled won't-fix;y -= vBorder in BOTH arms. It looks like it belongs after the branch, but the border arm's own body reads the unadjusted y (the bottom-border scanline register, the palette-regenerate trigger and the splash all do), so hoisting it would change all three. Definition at line 326 of file pico9918_frame.c.
References pico9918_diag_render(), pico9918_diag_update_render_time(), PICO9918_FRAME_STARTUP_FRAMES, pico9918_frame_update_interrupts(), PICO9918_INST, PICO9918_INST_ONLY, PICO9918_R24_TILE1_PS, PICO9918_R50_GPU_HSYNC, PICO9918_REG_ENHANCED2, PICO9918_REG_HORZ_INT_LINE, PICO9918_REG_PALETTE_SELECT, pico9918_scan_line(), pico9918_splash_render(), PICO9918_SR1_BLANK, PICO9918_SR1_HF, PICO9918_SR_IDENT, PICO9918_SR_RASTER_LINE, and TMS9918_PIXELS_X.
Referenced by pico9918_frame_output_line().
|
static |
Definition at line 481 of file pico9918_frame.c.
| bool pico9918_frame_output_line | ( | pico9918_t * | tms9918, |
| uint32_t | outputLine, | ||
| pico9918_scanline_params_t * | params, | ||
| PICO9918_PIXEL_T * | pixels | ||
| ) |
see the header.
generate one OUTPUT line - the entry for a host that scans out a fixed frame
A repeat re-reads the host's buffer rather than re-rendering.
Definition at line 494 of file pico9918_frame.c.
References pico9918_scanline_params_t::hVirtualPixels, PICO9918_CHIP_F18A, pico9918_diag_render(), pico9918_frame_scanline(), PICO9918_INST, PICO9918_INST_ONLY, and pico9918_scanline_params_t::vVirtualPixels.
| int pico9918_frame_count = 0 |
Definition at line 34 of file pico9918_frame.c.
| int pico9918_dropped_frames_count = 0 |
Definition at line 35 of file pico9918_frame.c.
|
static |
Definition at line 36 of file pico9918_frame.c.
| int pico9918_v_pixels = 192 |
Definition at line 40 of file pico9918_frame.c.
| uint32_t pico9918_v_border = 0 |
Definition at line 41 of file pico9918_frame.c.
| bool pico9918_valid_writes = false |
Definition at line 42 of file pico9918_frame.c.
| uint8_t pico9918_v_scale = 2 |
Definition at line 45 of file pico9918_frame.c.
| uint16_t pico9918_v_virtual = 240 |
Definition at line 46 of file pico9918_frame.c.