|
pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
|
pico9918-core - GPU Implementation More...
#include "gpu.h"#include "impl/pico9918_priv.h"#include "pico9918_config.h"#include <string.h>#include "pico/stdlib.h"#include "hardware/structs/mpu.h"#include "hardware/sync.h"#include <hardware/flash.h>#include "pico.h"#include "tms9900.h"#include "impl/platform.h"
Include dependency graph for gpu.c:Go to the source code of this file.
Macros | |
| #define | GPU_FLASH_CB tms9918->gpuFlash |
| #define | GPU_CONFIG_SAVE_CB tms9918->gpuConfigSave |
| #define | GPU_WORKSPACE 0xFFFEu |
| #define | GPU_SLICE_FROM_IPS(ips, lines, hz) ((uint32_t)((ips) / ((lines) * (hz))) + 1u) |
Functions | |
| uint16_t | run9900 (uint8_t *memory, uint16_t pc, uint16_t wp, uint8_t *regx38) |
| void | pico9918_gpu_set_flash_callback (pico9918_t *tms9918, pico9918_gpu_flash_fn cb, void *userdata) |
| Register a callback that will be invoked when the GPU wants to flash a sector. | |
| void | pico9918_gpu_set_config_save_callback (pico9918_t *tms9918, pico9918_gpu_config_save_fn cb, void *userdata) |
| Register a callback that will be invoked when the GPU loop detects a config action request. | |
| void | pico9918_gpu_flash_complete (pico9918_t *tms9918, pico9918_flash_result_t result) |
| End the flash operation R63 requested, with the result the guest reads back. | |
| static void | gpuFlashFire (pico9918_t *tms9918) |
| static void | gpuConfigSaveFire (pico9918_t *tms9918, uint8_t key) |
| void | isr_hardfault (void) |
| static PICO9918_NOINLINE void | dmaWrapped (uint8_t *vram, uint32_t src, uint32_t dst, uint32_t width, uint32_t height, int32_t pitch, int32_t srcInc, int32_t dstInc) |
| static void | triggerGpuDma (uint8_t *vram) |
| static void | guard (uint32_t region, void *a, uint32_t bytes) |
| static void | guardEnable (uint32_t region, bool on) |
| static void | gpuPaletteFault (pico9918_t *tms9918) |
| void | pico9918_gpu_rearm_palette_guard (pico9918_t *tms9918) |
| put the palette guard back, from the core that owns the MPU | |
| static PICO9918_NOINLINE bool | volatileHack (pico9918_t *tms9918, uint32_t budget) |
| void | pico9918_gpu_init (pico9918_t *tms9918) |
| Initialize the TMS9900 GPU. | |
| uint32_t | pico9918_gpu_time (uint32_t totalTime) |
| Return the GPU's CPU time in microseconds. | |
| void | pico9918_gpu_reset_time (void) |
| Reset the internal GPU time accumulator to 0. | |
| void | pico9918_gpu_step (pico9918_t *tms9918) |
| One pass of that loop: run a pending trigger to completion, then dispatch any flash and config-action requests. | |
| uint16_t | pico9918_gpu_pc (pico9918_t *tms9918) |
| see the header. | |
| uint32_t | pico9918_gpu_mem_size (void) |
| see the header. | |
| uint8_t | pico9918_gpu_mem_value (pico9918_t *tms9918, uint32_t addr) |
| see the header. | |
| uint16_t | pico9918_gpu_reg_value (pico9918_t *tms9918, uint8_t reg) |
| see the header. | |
| uint16_t | pico9918_gpu_status (pico9918_t *tms9918) |
| see the header. | |
| bool | pico9918_gpu_step_n (pico9918_t *tms9918, uint32_t instructions) |
The same pass, capped at instructions, returning true while the program still has work left. | |
| void | pico9918_gpu_loop (pico9918_t *tms9918) |
| GPU main loop - call from a dedicated core/thread. | |
| void | pico9918_gpu_set_clock (pico9918_t *tms9918, uint32_t instructionsPerSecond) |
| Hand GPU execution to the library, at this many instructions a second. | |
Variables | |
| static const uint8_t | configActionKeys [] |
| static int | didFault = 0 |
| volatile uint8_t | pico9918_gpu_palette_guard_off = 0 |
| The palette guard, as much of it as the host has to see. | |
| static volatile bool | reportedBack = true |
| static volatile uint32_t | gpuTimeUs = 0 |
pico9918-core - GPU Implementation
Copyright (c) 2021 Troy Schrapel
This code is licensed under the MIT license
https://github.com/visrealm/pico9918-core
Purpose: TMS9900 GPU glue code (adapted from pico9918/src/gpu/gpu.c)
Credits: JasonACT (AtariAge)
Definition in file gpu.c.
| #define GPU_SLICE_FROM_IPS | ( | ips, | |
| lines, | |||
| hz | |||
| ) | ((uint32_t)((ips) / ((lines) * (hz))) + 1u) |
| void pico9918_gpu_set_flash_callback | ( | pico9918_t * | tms9918, |
| pico9918_gpu_flash_fn | cb, | ||
| void * | userdata | ||
| ) |
Register a callback that will be invoked when the GPU wants to flash a sector.
Pass NULL to disable.
Registered per instance in a multi-instance build - see pico9918.h for why the two builds take different shapes.
| void pico9918_gpu_set_config_save_callback | ( | pico9918_t * | tms9918, |
| pico9918_gpu_config_save_fn | cb, | ||
| void * | userdata | ||
| ) |
Register a callback that will be invoked when the GPU loop detects a config action request.
The callback receives the config array pointer and the config key that fired (save / forced save / pending confirm / pending cancel - semantics are owned by the host). The key is cleared before the callback is invoked. Pass NULL to disable.
Registered per instance in a multi-instance build - see pico9918.h for why the two builds take different shapes.
| void pico9918_gpu_flash_complete | ( | pico9918_t * | tms9918, |
| pico9918_flash_result_t | result | ||
| ) |
End the flash operation R63 requested, with the result the guest reads back.
The busy flag is the engine's and a host has no other way to clear it: the palette rebuild is forced while SR2 bit 7 is set, so an operation left un-ended rebuilds the lookup table on every active scanline. Call this once per request, from the callback or later - a host that hands the work to another thread ends it when that finishes, and the guest polls SR2 until then.
The pending request itself is already taken before the callback is entered, so a request arriving during a long erase re-arms rather than being lost here.
Bits 6-5 (the retry count) and 1-0 (the progress code) are left as they were found.
Definition at line 112 of file gpu.c.
References PICO9918_REG_GPU_CONTROL, and PICO9918_SR_GPU.
|
inlinestatic |
|
static |
|
static |
| void pico9918_gpu_rearm_palette_guard | ( | pico9918_t * | tms9918 | ) |
put the palette guard back, from the core that owns the MPU
Definition at line 306 of file gpu.c.
References pico9918_gpu_palette_guard_off.
|
static |
| void pico9918_gpu_init | ( | pico9918_t * | tms9918 | ) |
Initialize the TMS9900 GPU.
Must be called after pico9918_init() / pico9918_reset().
| uint32_t pico9918_gpu_time | ( | uint32_t | totalTime | ) |
Return the GPU's CPU time in microseconds.
If the GPU is still running (hasn't reported back), returns totalTime.
CROSS-CORE: the accumulator and its reported-back flag are written by pico9918_gpu_loop - core 0 on Pico - while these two calls are made from the frame/overlay side on core 1. Both are volatile and neither call is guarded: the worst case is one sample window's update being lost, which is acceptable for a statistics readout and cheaper than a critical section per frame.
Definition at line 412 of file gpu.c.
Referenced by pico9918_diag_update().
| void pico9918_gpu_reset_time | ( | void | ) |
Reset the internal GPU time accumulator to 0.
Cross-core, unguarded - see pico9918_gpu_time.
Definition at line 421 of file gpu.c.
Referenced by pico9918_diag_update().
| void pico9918_gpu_step | ( | pico9918_t * | tms9918 | ) |
One pass of that loop: run a pending trigger to completion, then dispatch any flash and config-action requests.
Returns.
It is the loop's body rather than a second copy of it, so a program run this way is run by the same code the device runs it with, and it is timed into the same accumulator pico9918_gpu_time reads.
How long it takes is the program's business: run9900 returns on IDLE or when the program clears its own run flag (TMS register 0x38 bit 0), and a program that does neither does not return.
Which makes this the wrong entry for a host with one thread, however much it looks like the right one. A program may WAIT on the display - the scanline being scanned out is readable at >7000 - and the caller that would advance the raster is the one blocked in here. Use pico9918_gpu_step_n for that, or give the GPU a thread and render on the one you have.
Definition at line 430 of file gpu.c.
References PICO9918_INST, and PICO9918_INST_ONLY.
Referenced by pico9918_gpu_loop().
| uint16_t pico9918_gpu_pc | ( | pico9918_t * | tms9918 | ) |
| uint32_t pico9918_gpu_mem_size | ( | void | ) |
see the header.
the size of that space, so a memory view knows where to stop
The whole map the GPU addresses, workspace overflow included.
Definition at line 469 of file gpu.c.
Referenced by pico9918_debug_read(), and pico9918_gpu_mem_value().
| uint8_t pico9918_gpu_mem_value | ( | pico9918_t * | tms9918, |
| uint32_t | addr | ||
| ) |
see the header.
a byte of the instance's memory, without disturbing anything
A byte of that map, or 0 past the end of it.
Definition at line 476 of file gpu.c.
References pico9918_gpu_mem_size().
Referenced by pico9918_gpu_reg_value().
| uint16_t pico9918_gpu_reg_value | ( | pico9918_t * | tms9918, |
| uint8_t | reg | ||
| ) |
see the header.
a GPU workspace register, R0-R15, without disturbing anything
R0-R15 as words at the fixed workspace.
Definition at line 486 of file gpu.c.
References pico9918_gpu_mem_value(), and PICO9918_INST.
| uint16_t pico9918_gpu_status | ( | pico9918_t * | tms9918 | ) |
| bool pico9918_gpu_step_n | ( | pico9918_t * | tms9918, |
| uint32_t | instructions | ||
| ) |
The same pass, capped at instructions, returning true while the program still has work left.
Zero means no cap, which is pico9918_gpu_step().
This is the entry for a host with one thread. pico9918_gpu_step() cannot come back until the program stops itself, so a program that waits on the scanline at >7000 - to page a bitmap in the vertical blank, say - would wait forever: the caller that would advance the raster is the one blocked inside it. Capped, the caller gets control back with the PC kept, renders, and calls again:
while (pico9918_gpu_step_n(PICO9918_INST 20000)) renderOneScanline();
A host with a thread to spare wants pico9918_gpu_loop() on it instead, which is what the firmware does. Both shapes are real; this one asks nothing of the host but a loop.
Only the portable C core counts instructions. On a board built with the hand-written Thumb core the cap is ignored and this runs to completion - which costs that build nothing, because it has a core to give the GPU.
Definition at line 509 of file gpu.c.
References PICO9918_INST, and PICO9918_INST_ONLY.
| void pico9918_gpu_loop | ( | pico9918_t * | tms9918 | ) |
GPU main loop - call from a dedicated core/thread.
Runs indefinitely; processes GPU programs, flash requests, and config saves.
Definition at line 546 of file gpu.c.
References pico9918_gpu_step(), and PICO9918_INST_ONLY.
| void pico9918_gpu_set_clock | ( | pico9918_t * | tms9918, |
| uint32_t | instructionsPerSecond | ||
| ) |
Hand GPU execution to the library, at this many instructions a second.
Zero - the default - leaves the GPU to whoever else drives it: a board's second core, or a host thread running pico9918_gpu_loop(). Set a rate and the library runs it instead, from the register write that arms a program and once per scanline after, and a host that sets one calls no other GPU entry point. Arming matters: software probing for an F18A reads its result back a few cycles later, so a GPU serviced once a scanline has not run yet and the probe intermittently sees no F18A at all.
The rate becomes a per-scanline slice, re-derived each frame, so a mode change needs nothing from the host. Ignored where pico9918_gpu_step_n's cap is - a hand-written Thumb core runs to completion - and it charges GPU time to the calling thread.
|
static |
| volatile uint8_t pico9918_gpu_palette_guard_off = 0 |
The palette guard, as much of it as the host has to see.
A GPU palette write has no other way of announcing itself, so an MPU region faults on it, marks the palette dirty and takes itself out of the way until the renderer has taken the flag. Putting it back is the host's to schedule, because the MPU belongs to the core running the GPU, and the only place that core is reliably idle is its scanline interrupt: read the flag from there, and call the re-arm.
Definition at line 256 of file gpu.c.
Referenced by pico9918_gpu_rearm_palette_guard().