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

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
 

Detailed Description

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.

Macro Definition Documentation

◆ GPU_FLASH_CB

#define GPU_FLASH_CB   tms9918->gpuFlash

Definition at line 91 of file gpu.c.

◆ GPU_CONFIG_SAVE_CB

#define GPU_CONFIG_SAVE_CB   tms9918->gpuConfigSave

Definition at line 92 of file gpu.c.

◆ GPU_WORKSPACE

#define GPU_WORKSPACE   0xFFFEu

Definition at line 465 of file gpu.c.

◆ GPU_SLICE_FROM_IPS

#define GPU_SLICE_FROM_IPS (   ips,
  lines,
  hz 
)    ((uint32_t)((ips) / ((lines) * (hz))) + 1u)

Definition at line 560 of file gpu.c.

Function Documentation

◆ pico9918_gpu_set_flash_callback()

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.

Definition at line 95 of file gpu.c.

◆ pico9918_gpu_set_config_save_callback()

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.

Definition at line 101 of file gpu.c.

◆ pico9918_gpu_flash_complete()

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.

◆ gpuFlashFire()

static void gpuFlashFire ( pico9918_t *  tms9918)
inlinestatic

Definition at line 119 of file gpu.c.

◆ gpuConfigSaveFire()

static void gpuConfigSaveFire ( pico9918_t *  tms9918,
uint8_t  key 
)
inlinestatic

Definition at line 132 of file gpu.c.

◆ isr_hardfault()

void isr_hardfault ( void  )

Definition at line 144 of file gpu.c.

◆ dmaWrapped()

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

Definition at line 159 of file gpu.c.

◆ triggerGpuDma()

static void triggerGpuDma ( uint8_t *  vram)
static

Definition at line 174 of file gpu.c.

◆ guard()

static void guard ( uint32_t  region,
void *  a,
uint32_t  bytes 
)
static

Definition at line 260 of file gpu.c.

◆ guardEnable()

static void guardEnable ( uint32_t  region,
bool  on 
)
static

Definition at line 278 of file gpu.c.

◆ gpuPaletteFault()

static void gpuPaletteFault ( pico9918_t *  tms9918)
static

Definition at line 296 of file gpu.c.

◆ pico9918_gpu_rearm_palette_guard()

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.

◆ volatileHack()

static PICO9918_NOINLINE bool volatileHack ( pico9918_t *  tms9918,
uint32_t  budget 
)
static

Definition at line 319 of file gpu.c.

◆ pico9918_gpu_init()

void pico9918_gpu_init ( pico9918_t *  tms9918)

Initialize the TMS9900 GPU.

Must be called after pico9918_init() / pico9918_reset().

Definition at line 388 of file gpu.c.

◆ pico9918_gpu_time()

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

◆ pico9918_gpu_reset_time()

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

◆ pico9918_gpu_step()

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

◆ pico9918_gpu_pc()

uint16_t pico9918_gpu_pc ( pico9918_t *  tms9918)

see the header.

Where the GPU is: the address the next slice resumes from.

The address a slice resumes from; odd means not running.

Definition at line 459 of file gpu.c.

◆ pico9918_gpu_mem_size()

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

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

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

◆ pico9918_gpu_status()

uint16_t pico9918_gpu_status ( pico9918_t *  tms9918)

see the header.

the GPU's status register between instructions

The status between instructions, where one paces them.

Definition at line 496 of file gpu.c.

◆ pico9918_gpu_step_n()

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.

◆ pico9918_gpu_loop()

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.

◆ pico9918_gpu_set_clock()

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.

Definition at line 562 of file gpu.c.

Variable Documentation

◆ configActionKeys

const uint8_t configActionKeys[]
static
Initial value:
= {PICO9918_CONF_SAVE_TO_FLASH, PICO9918_CONF_SAVE_FORCED, PICO9918_CONF_PENDING_CONFIRM,
PICO9918_CONF_PENDING_CANCEL}

Definition at line 69 of file gpu.c.

◆ didFault

int didFault = 0
static

Definition at line 142 of file gpu.c.

◆ pico9918_gpu_palette_guard_off

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

◆ reportedBack

volatile bool reportedBack = true
static

Definition at line 406 of file gpu.c.

◆ gpuTimeUs

volatile uint32_t gpuTimeUs = 0
static

Definition at line 407 of file gpu.c.