29#include "pico/stdlib.h"
30#include "hardware/structs/mpu.h"
31#include "hardware/sync.h"
32#include <hardware/flash.h>
39#if !PICO9918_GPU_BUDGETED
41extern uint16_t run9900(uint8_t* memory, uint16_t pc, uint16_t wp, uint8_t* regx38);
43#if defined(TMS9900_WATCH_WRITES)
44static void gpuDmaWatch(uint8_t* vram, uint32_t addr);
47static uint16_t run9900Budget(uint8_t* mem, uint16_t pc, uint16_t wp, uint8_t* r38,
48 uint32_t budget, uint16_t* st,
bool* outOfBudget,
bool f18aMemory)
51 tms9900_init(&cpu, mem, r38, pc, wp);
52 cpu.f18aMemory = f18aMemory;
53#if defined(TMS9900_WATCH_WRITES)
54 cpu.onWrite = gpuDmaWatch;
57 const uint16_t next = run9900_budget_c(&cpu, budget, outOfBudget);
69static const uint8_t configActionKeys[] = {PICO9918_CONF_SAVE_TO_FLASH, PICO9918_CONF_SAVE_FORCED, PICO9918_CONF_PENDING_CONFIRM,
70 PICO9918_CONF_PENDING_CANCEL};
76#if PICO9918_SINGLE_INSTANCE
79 pico9918_gpu_flash_fn fn;
85 pico9918_gpu_config_save_fn fn;
88#define GPU_FLASH_CB gpuFlash
89#define GPU_CONFIG_SAVE_CB gpuConfigSave
91#define GPU_FLASH_CB tms9918->gpuFlash
92#define GPU_CONFIG_SAVE_CB tms9918->gpuConfigSave
98 GPU_FLASH_CB.userdata = userdata;
103 GPU_CONFIG_SAVE_CB.fn = cb;
104 GPU_CONFIG_SAVE_CB.userdata = userdata;
115 (uint8_t)((TMS_STATUS(tms9918,
PICO9918_SR_GPU) & ~0x9c) | ((result & 7) << 2));
127 GPU_FLASH_CB.fn(tms9918, GPU_FLASH_CB.userdata);
134 if (GPU_CONFIG_SAVE_CB.fn)
135 GPU_CONFIG_SAVE_CB.fn(tms9918, tms9918->config, key, GPU_CONFIG_SAVE_CB.userdata);
142static int didFault = 0;
144void isr_hardfault(
void)
159static PICO9918_NOINLINE
void dmaWrapped(uint8_t* vram, uint32_t src, uint32_t dst,
160 uint32_t width, uint32_t height, int32_t pitch,
161 int32_t srcInc, int32_t dstInc)
163 uint16_t s = (uint16_t)src;
164 uint16_t d = (uint16_t)dst;
165 for (uint32_t y = 0; y < height; ++y)
167 uint16_t rs = s, rd = d;
168 for (uint32_t x = 0; x < width; ++x, rs += srcInc, rd += dstInc) vram[rd] = vram[rs];
169 if (srcInc) s += (uint16_t)pitch;
170 d += (uint16_t)pitch;
174static void triggerGpuDma(uint8_t* vram)
176 const uint32_t srcVramAddr = __builtin_bswap16(*(uint16_t*)(vram + 0x8000));
177 const uint32_t dstVramAddr = __builtin_bswap16(*(uint16_t*)(vram + 0x8002));
180 const uint32_t width = vram[0x8004] ? vram[0x8004] : 256;
181 const uint32_t height = vram[0x8005] ? vram[0x8005] : 256;
182 const uint32_t stride = vram[0x8006];
183 const uint32_t params = vram[0x8007];
185 const int32_t dstInc = (params & 0x02) ? -1 : 1;
186 const int32_t srcInc = (params & 0x01) ? 0 : dstInc;
187 const uint32_t wm1 = width - 1;
193 const uint32_t diffByte = ((dstInc < 0) ? wm1 - stride : stride - wm1) & 0xff;
194 const int32_t diff = (diffByte & 0x80) ? (int32_t)diffByte - 256 : (int32_t)diffByte;
195 const int32_t pitch = (int32_t)wm1 * dstInc + diff;
198 const int32_t row = (int32_t)wm1 * dstInc;
199 const int32_t col = (int32_t)(height - 1) * pitch;
200 const int32_t back = (row < 0 ? row : 0) + (col < 0 ? col : 0);
201 const int32_t fwd = (row > 0 ? row : 0) + (col > 0 ? col : 0);
203 const int32_t dstLo = (int32_t)dstVramAddr + back, dstHi = (int32_t)dstVramAddr + fwd;
204 const int32_t srcLo = (int32_t)srcVramAddr + back, srcHi = (int32_t)srcVramAddr + fwd;
206 if (dstLo < 0 || dstHi > 0xFFFF || (srcInc && (srcLo < 0 || srcHi > 0xFFFF)))
208 dmaWrapped(vram, srcVramAddr, dstVramAddr, width, height, pitch, srcInc, dstInc);
210 else if (srcInc == 0)
213 uint8_t* d = vram + dstVramAddr - (dstInc < 0 ? wm1 : 0);
214 const uint8_t value = vram[srcVramAddr];
215 for (uint32_t y = 0; y < height; ++y, d += pitch) memset(d, value, width);
217 else if (dstLo > srcHi || srcLo > dstHi)
220 uint8_t* s = vram + srcVramAddr - (dstInc < 0 ? wm1 : 0);
221 uint8_t* d = vram + dstVramAddr - (dstInc < 0 ? wm1 : 0);
222 for (uint32_t y = 0; y < height; ++y, s += pitch, d += pitch) memcpy(d, s, width);
226 uint8_t* s = vram + srcVramAddr;
227 uint8_t* d = vram + dstVramAddr;
228 for (uint32_t y = 0; y < height; ++y, s += pitch, d += pitch)
232 for (uint32_t x = 0; x < width; ++x, rs += srcInc, rd += dstInc) *rd = *rs;
236 *(uint16_t*)(vram + 0x8008) = 0;
239#if defined(TMS9900_WATCH_WRITES)
245static void gpuDmaWatch(uint8_t* vram, uint32_t addr)
247 if ((addr & ~(uint32_t)0x1F) == 0x8000 && vram[0x8008]) triggerGpuDma(vram);
260static void PICO9918_IN_FLASH_FUNC(guard)(uint32_t region,
void* a, uint32_t bytes)
262 uintptr_t addr = (uintptr_t)a;
264 uint32_t base = addr & (uint)~0xff;
265 uint32_t first = (addr - base) >> 5;
266 uint32_t last = (addr + bytes - 1 - base) >> 5;
267 uint32_t srd = ~(((1u << (last - first + 1)) - 1u) << first) & 0xffu;
269 mpu_hw->rbar = base | M0PLUS_MPU_RBAR_VALID_BITS | region;
270 mpu_hw->rasr = 1 | (0x07 << 1) | (srd << 8) | 0x15000000;
272 mpu_hw->rnr = region;
273 mpu_hw->rbar = (addr & (uint)~31u) | (2u << M33_MPU_RBAR_AP_LSB) | M33_MPU_RBAR_XN_BITS;
274 mpu_hw->rlar = ((addr + bytes - 1) & (uint)~31u) | M33_MPU_RLAR_EN_BITS;
278static void __not_in_flash_func(guardEnable)(uint32_t region,
bool on)
280 mpu_hw->rnr = region;
283 mpu_hw->rasr |= M0PLUS_MPU_RASR_ENABLE_BITS;
285 mpu_hw->rasr &= ~M0PLUS_MPU_RASR_ENABLE_BITS;
288 mpu_hw->rlar |= M33_MPU_RLAR_EN_BITS;
290 mpu_hw->rlar &= ~M33_MPU_RLAR_EN_BITS;
298 tms9918->palDirty = 1;
300 uint32_t save = save_and_disable_interrupts();
302 guardEnable(1,
false);
303 restore_interrupts(save);
308 if (tms9918->palDirty)
return;
311 guardEnable(1,
true);
312 tms9918->palDirty = 1;
321 bool running =
false;
322 bool outOfBudget =
false;
323 tms9918->restart = 0;
324 if ((tms9918->gpuAddress & 1) == 0)
326 uint16_t lastAddress = tms9918->gpuAddress;
336 mpu_hw->ctrl = M0PLUS_MPU_CTRL_PRIVDEFENA_BITS | M0PLUS_MPU_CTRL_ENABLE_BITS;
338 mpu_hw->ctrl = M33_MPU_CTRL_PRIVDEFENA_BITS | M33_MPU_CTRL_ENABLE_BITS;
342#if PICO9918_GPU_BUDGETED
343 lastAddress = run9900Budget(tms9918->vram.bytes, lastAddress, 0xFFFE,
345 &outOfBudget, !PICO9918_GPU_FLAT_MEM(tms9918));
358 tms9918->gpuAddress = lastAddress;
359 tms9918->restart = 0;
360 running = outOfBudget;
366 if (tms9918->vram.bytes[0x8008])
367 triggerGpuDma(tms9918->vram.bytes);
374 if (running)
return true;
392 mpu_hw->mair[0] = 0x44;
394 guard(0, &(tms9918->vram.bytes[0x8000]), 32);
395 guard(1, tms9918->vram.map.pram, 64 *
sizeof(*tms9918->vram.map.pram));
406static volatile bool reportedBack =
true;
407static volatile uint32_t gpuTimeUs = 0;
414 if (!reportedBack)
return totalTime;
432 if (tms9918->restart)
434 reportedBack =
false;
435 uint32_t gpuStart = PICO9918_HOST_TIME_US();
437 gpuTimeUs += PICO9918_HOST_TIME_US() - gpuStart;
446 for (
int i = 0; i < (int)(
sizeof(configActionKeys) /
sizeof(configActionKeys[0])); ++i)
448 const uint8_t key = configActionKeys[i];
449 if (tms9918->config[key])
451 tms9918->config[key] = 0;
461 return tms9918->gpuAddress;
465#define GPU_WORKSPACE 0xFFFEu
471 return (uint32_t)
sizeof(((pico9918_t*)0)->vram);
481 return ((
const uint8_t*)&tms9918->vram)[addr];
488 const uint32_t at = GPU_WORKSPACE + ((uint32_t)(reg & 0x0f) << 1);
499 return (uint16_t)(tms9918->gpuStatus << 8);
511 bool running =
false;
513 if (tms9918->restart)
515 reportedBack =
false;
516 uint32_t gpuStart = PICO9918_HOST_TIME_US();
518 gpuTimeUs += PICO9918_HOST_TIME_US() - gpuStart;
521 if (running) tms9918->restart = 1;
523 reportedBack = !running;
530 for (
int i = 0; i < (int)(
sizeof(configActionKeys) /
sizeof(configActionKeys[0])); ++i)
532 const uint8_t key = configActionKeys[i];
533 if (tms9918->config[key])
535 tms9918->config[key] = 0;
560#define GPU_SLICE_FROM_IPS(ips, lines, hz) ((uint32_t)((ips) / ((lines) * (hz))) + 1u)
564#if PICO9918_GPU_BUDGETED
565 tms9918->gpuIps = instructionsPerSecond;
566 tms9918->gpuSlice = instructionsPerSecond ? GPU_SLICE_FROM_IPS(instructionsPerSecond, 240u, 60u) : 0u;
570 (void)instructionsPerSecond;
574#if PICO9918_GPU_BUDGETED
580 if (!tms9918->gpuIps || !lines || frameRateHz < 1.0f)
return;
582 tms9918->gpuSlice = GPU_SLICE_FROM_IPS(tms9918->gpuIps, lines, (uint32_t)frameRateHz);
uint16_t pico9918_gpu_status(pico9918_t *tms9918)
see the header.
uint16_t pico9918_gpu_pc(pico9918_t *tms9918)
see the header.
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.
volatile uint8_t pico9918_gpu_palette_guard_off
The palette guard, as much of it as the host has to see.
void pico9918_gpu_init(pico9918_t *tms9918)
Initialize the TMS9900 GPU.
uint32_t pico9918_gpu_mem_size(void)
see the header.
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...
void pico9918_gpu_loop(pico9918_t *tms9918)
GPU main loop - call from a dedicated core/thread.
void pico9918_gpu_rearm_palette_guard(pico9918_t *tms9918)
put the palette guard back, from the core that owns the MPU
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.
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.
uint32_t pico9918_gpu_time(uint32_t totalTime)
Return the GPU's CPU time in microseconds.
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_set_clock(pico9918_t *tms9918, uint32_t instructionsPerSecond)
Hand GPU execution to the library, at this many instructions a second.
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.
pico9918-core - GPU Interface
pico9918_flash_result_t
what a flash operation finished as, reported in status register 2
@ PICO9918_FLASH_ERR_UNSUPPORTED
no host is listening - see pico9918_gpu_set_flash_callback
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
#define PICO9918_INST_ONLY
pass the instance as the only argument
@ PICO9918_REG_GPU_CONTROL
GPU load and trigger.
@ PICO9918_SR_GPU
GPU running and its status byte.
#define PICO9918_INST_ONLY_ARG
declare the instance as the only parameter
#define PICO9918_INST
pass the instance ahead of other arguments
#define PICO9918_DLLEXPORT
the linkage every public entry point carries - see LINKAGE MODES above
pico9918-core - config byte layout
pico9918-core - the private instance layout
pico9918-core - TMS9900 CPU interpreter (portable C)