|
pico9918-core 1.3.0
TMS9918A / F18A video display processor emulation in C99
|
pico9918-core - GPU Interface More...
#include "pico9918.h"
Include dependency graph for gpu.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Macros | |
| #define | PICO9918_GPU_IPS_CLASSIC 7000000u /* PICO9918, RP2040 at 352MHz */ |
| Rough GPU throughput, in TMS9900 instructions a second, for pico9918_gpu_set_clock. | |
| #define | PICO9918_GPU_IPS_PRO 10000000u |
| PICO9918 PRO, RP2350 at 352MHz. | |
| #define | PICO9918_GPU_IPS_F18A 8500000u |
| the F18A itself, ie. a PRO at 302MHz | |
| #define | PICO9918_GPU_ST_LGT 0x8000 |
| ST0, logical greater than. | |
| #define | PICO9918_GPU_ST_AGT 0x4000 |
| ST1, arithmetic greater than. | |
| #define | PICO9918_GPU_ST_EQ 0x2000 |
| ST2, equal. | |
| #define | PICO9918_GPU_ST_C 0x1000 |
| ST3, carry. | |
| #define | PICO9918_GPU_ST_OV 0x0800 |
| ST4, overflow. | |
| #define | PICO9918_GPU_ST_P 0x0400 |
| ST5, odd parity. | |
Enumerations | |
| enum | pico9918_flash_result_t { PICO9918_FLASH_OK = 0 , PICO9918_FLASH_ERR_HEADER = 1 , PICO9918_FLASH_ERR_SEQUENCE = 2 , PICO9918_FLASH_ERR_SIZE = 3 , PICO9918_FLASH_ERR_VERIFY = 4 , PICO9918_FLASH_ERR_UNSUPPORTED = 5 , PICO9918_FLASH_ERR_FULL = 6 } |
| what a flash operation finished as, reported in status register 2 More... | |
Functions | |
| void | pico9918_gpu_init (pico9918_t *tms9918) |
| Initialize the TMS9900 GPU. | |
| void | pico9918_gpu_rearm_palette_guard (pico9918_t *tms9918) |
| put the palette guard back, from the core that owns the MPU | |
| void | pico9918_gpu_loop (pico9918_t *tms9918) |
| GPU main loop - call from a dedicated core/thread. | |
| 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. | |
| void | pico9918_gpu_set_clock (pico9918_t *tms9918, uint32_t instructionsPerSecond) |
| Hand GPU execution to the library, at this many instructions a second. | |
| 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. | |
| uint16_t | pico9918_gpu_pc (pico9918_t *tms9918) |
| Where the GPU is: the address the next slice resumes from. | |
| uint8_t | pico9918_gpu_mem_value (pico9918_t *tms9918, uint32_t addr) |
| a byte of the instance's memory, without disturbing anything | |
| uint32_t | pico9918_gpu_mem_size (void) |
| the size of that space, so a memory view knows where to stop | |
| uint16_t | pico9918_gpu_reg_value (pico9918_t *tms9918, uint8_t reg) |
| a GPU workspace register, R0-R15, without disturbing anything | |
| uint16_t | pico9918_gpu_status (pico9918_t *tms9918) |
| the GPU's status register between instructions | |
| 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_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_flash_complete (pico9918_t *tms9918, pico9918_flash_result_t result) |
| End the flash operation R63 requested, with the result the guest reads back. | |
| 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. | |
Variables | |
| volatile uint8_t | pico9918_gpu_palette_guard_off |
| The palette guard, as much of it as the host has to see. | |
pico9918-core - GPU Interface
Copyright (c) 2021 Troy Schrapel
This code is licensed under the MIT license
https://github.com/visrealm/pico9918-core
Purpose: Library-public interface to the TMS9900 GPU (F18A compatibility layer)
Definition in file gpu.h.
| #define PICO9918_GPU_IPS_CLASSIC 7000000u /* PICO9918, RP2040 at 352MHz */ |
Rough GPU throughput, in TMS9900 instructions a second, for pico9918_gpu_set_clock.
At the top clock preset, not the 252MHz a board boots at. The PRO and F18A figures sit where a measured comparison puts them - a PRO at 352MHz beats an F18A, which lands at a PRO's 302MHz preset within a couple of percent - and the RP2040 where cycle-counting its dispatch does. Read the GPU% row of the diagnostics overlay to do better.
| #define PICO9918_GPU_IPS_PRO 10000000u |
| #define PICO9918_GPU_IPS_F18A 8500000u |
| #define PICO9918_GPU_ST_AGT 0x4000 |
what a flash operation finished as, reported in status register 2
| void pico9918_gpu_init | ( | pico9918_t * | tms9918 | ) |
Initialize the TMS9900 GPU.
Must be called after pico9918_init() / pico9918_reset().
| 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.
| 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_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().
| 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.
| 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.
| uint16_t pico9918_gpu_pc | ( | pico9918_t * | tms9918 | ) |
Where the GPU is: the address the next slice resumes from.
The arming address before it first runs, and the point it reached after a capped slice returns true.
ODD MEANS NOTHING IS ARMED. A reset parks 0xFFFF here and the engine refuses to start from an odd address, so a caller polling this reads odd as "no program", not as a position. An even value is a real address whether or not a program is still running - use pico9918_gpu_step_n()'s return for that.
Where the GPU is: the address the next slice resumes from.
The address a slice resumes from; odd means not running.
| uint8_t pico9918_gpu_mem_value | ( | pico9918_t * | tms9918, |
| uint32_t | addr | ||
| ) |
a byte of the instance's memory, without disturbing anything
The BACKING STATE, not the map a GPU program observes. Those differ: a running personality mirrors 0x4xxx, 0x5xxx, 0x6xxx and 0x7xxx across 4KB each and answers 0 in the holes, where this is every byte exactly once. It is the view a debugger wants, because it re-lays-out nothing when the chip personality changes, and the decoded one is derivable from it.
Not what the host data port sees either: pico9918_vram_value is the guest's view and stops at 0x3FFF, so it cannot reach GRAM at 0x4000, the palette at 0x5000, the register and status windows, or the workspace. Disassembly and memory views want this one, and pico9918_debug.h has the span form of it where a build asks for that.
The space runs past 0xFFFF. The GPU's workspace pointer is 0xFFFE, so R0 is the last word of the 64KB map and R1-R15 spill into an overflow above it. Anything beyond the space reads 0, so a view that walks off the end sees zeroes rather than the instance.
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().
| uint32_t pico9918_gpu_mem_size | ( | void | ) |
the size of that space, so a memory view knows where to stop
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().
| uint16_t pico9918_gpu_reg_value | ( | pico9918_t * | tms9918, |
| uint8_t | reg | ||
| ) |
a GPU workspace register, R0-R15, without disturbing anything
The workspace is fixed at 0xFFFE and a TMS9900 register is a word there, so this is the two bytes at 0xFFFE + 2n read big-endian. Only the low four bits of reg are used. Reachable through pico9918_gpu_mem_value() as well; this is here because the wrap past 0xFFFF is the library's business, not a debugger's.
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 | ) |
the GPU's status register between instructions
In the architectural bit positions, which is where STST stores them and where a disassembler or a flag display expects them - use the PICO9918_GPU_ST_* masks. The cores keep the flags in the low byte internally; this is the only published view and it agrees with the instruction.
TRAP: maintained only where the library paces the GPU itself. The hand-written Thumb cores a board builds run a program to completion and keep the status in a local, so there is no point between instructions for this to describe and it reads whatever it last held. Where pico9918_gpu_step_n() honours its cap - every desktop build - this is the status at the point the slice stopped, which is what a single step wants.
the GPU's status register between instructions
The status between instructions, where one paces them.
| 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_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_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.
| 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.
|
extern |
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().