77#define H_VIRTUAL_PIXELS 640
78#define V_DISPLAY_LINES 480
79#define FRAME_RATE_HZ 60.0f
80#define CORE_TEMP_C 30.0f
87#define FRAMES_AT_START 1000
90#define CAPTURE_BYTES (512 * 512)
97static PICO9918_PIXEL_T pixels[1024];
99static uint8_t capture[CAPTURE_BYTES];
100static uint32_t rows = 0;
101static uint32_t width = 0;
102static uint32_t missed = 0;
104static int vPixelScale = 1;
105static int vVirtualPixels = V_DISPLAY_LINES;
106static uint32_t triggerLine = V_DISPLAY_LINES;
109static int viewScale = 2;
115void liveDesktopCaptureRow(uint16_t y, uint16_t height, uint16_t width_,
const uint8_t* indices)
122 if ((uint32_t)y * width_ + width_ > CAPTURE_BYTES)
127 memcpy(capture + (uint32_t)y * width_, indices, width_);
134static SRWLOCK renderLock = SRWLOCK_INIT;
135#define RENDER_LOCK() AcquireSRWLockExclusive(&renderLock)
136#define RENDER_UNLOCK() ReleaseSRWLockExclusive(&renderLock)
138static pthread_mutex_t renderLock = PTHREAD_MUTEX_INITIALIZER;
139#define RENDER_LOCK() pthread_mutex_lock(&renderLock)
140#define RENDER_UNLOCK() pthread_mutex_unlock(&renderLock)
151static void renderFrameUnlocked(
void)
153 viewScale = vPixelScale;
155 for (
int y = 0; y < vVirtualPixels; ++y)
169static void renderFrame(
void)
172 renderFrameUnlocked();
188static uint8_t view[32 + 512 * 480 * 3];
190static size_t buildView(
void)
193 for (
int i = 0; i < 64; ++i)
195 const uint16_t v = __builtin_bswap16(tms9918->vram.map.pram[i]);
196 rgb[i][0] = (uint8_t)(((v >> 8) & 0xF) * 0x11);
197 rgb[i][1] = (uint8_t)(((v >> 4) & 0xF) * 0x11);
198 rgb[i][2] = (uint8_t)((v & 0xF) * 0x11);
208 const int packed = width == 256 && (TMS_REGISTER(tms9918, 0x00) & 0x04);
209 const int lines = (int)rows * viewScale;
210 if (!rows || lines > 480)
return 0;
212 int at = snprintf((
char*)view, 32,
"P6\n512 %d\n255\n", lines);
213 for (
int line = 0; line < lines; ++line)
215 const uint8_t* row = capture + (size_t)(line / viewScale) * width;
216 for (
int x = 0; x < 512; ++x)
222 index = (x & 1) ? (row[x >> 1] & 0x0F) : (uint8_t)(row[x >> 1] >> 4);
225 const uint8_t* c = rgb[index & 0x3F];
254static volatile int gpuBusy = 0;
267#define THREAD_BODY(name) static DWORD WINAPI name(LPVOID unused)
268#define THREAD_DONE return 0
269typedef LPTHREAD_START_ROUTINE thread_body_t;
271static int threadStart(
thread_t* t, thread_body_t body)
273 t->handle = CreateThread(NULL, 0, body, NULL, 0, NULL);
274 t->live = t->handle != NULL;
280 if (!t->live)
return;
281 WaitForSingleObject(t->handle, INFINITE);
282 CloseHandle(t->handle);
286#define THREAD_BODY(name) static void* name(void* unused)
287#define THREAD_DONE return NULL
288typedef void* (*thread_body_t)(
void*);
290static int threadStart(
thread_t* t, thread_body_t body)
292 t->live = pthread_create(&t->id, NULL, body, NULL) == 0;
298 if (!t->live)
return;
299 pthread_join(t->id, NULL);
327THREAD_BODY(rasterBody)
330 while (gpuBusy) renderFrame();
334static void gpuReap(
void)
336 threadJoin(&gpuThread);
337 threadJoin(&rasterThread);
340static void gpuStart(uint16_t addr)
343 tms9918->gpuAddress = addr;
344 tms9918->restart = 1;
347 if (threadStart(&gpuThread, gpuBody))
351 threadStart(&rasterThread, rasterBody);
362static uint8_t* base(
void)
364 return (uint8_t*)tms9918;
367static void reply(
const char* text)
374static int readExactly(
void* into,
size_t n)
376 return fread(into, 1, n, stdin) == n;
379static void writeExactly(
const void* from,
size_t n)
381 fwrite(from, 1, n, stdout);
389 _setmode(_fileno(stdin), _O_BINARY);
390 _setmode(_fileno(stdout), _O_BINARY);
395 for (
int i = 0; i < FRAMES_AT_START; ++i) renderFrame();
398 while (fgets(line,
sizeof(line), stdin))
400 char command[32] = {0};
402 if (sscanf(line,
"%31s%n", command, &consumed) < 1)
continue;
406 char* rest = line + consumed;
407 unsigned long addr = strtoul(rest, &rest, 16);
408 unsigned long count = strtoul(rest, &rest, 10);
410 if (!strcmp(command,
"quit"))
break;
412 if (!strcmp(command,
"off"))
414 printf(
"instance %u\n", (
unsigned)
sizeof(*tms9918));
415 printf(
"vram %u\n", (
unsigned)offsetof(
struct pico9918_s, vram));
416 printf(
"config %u\n", (
unsigned)offsetof(
struct pico9918_s, config));
417 printf(
"isUnlocked %u\n", (
unsigned)offsetof(
struct pico9918_s, isUnlocked));
418 printf(
"lockedMask %u\n", (
unsigned)offsetof(
struct pico9918_s, lockedMask));
419 printf(
"palDirty %u\n", (
unsigned)offsetof(
struct pico9918_s, palDirty));
420 printf(
"configDirty %u\n", (
unsigned)offsetof(
struct pico9918_s, configDirty));
423 else if (!strcmp(command,
"palette"))
425 uint16_t entries[64];
427 printf(
"data %u\n", (
unsigned)
sizeof(entries));
429 writeExactly(entries,
sizeof(entries));
431 else if (!strcmp(command,
"w"))
433 if (addr + count >
sizeof(*tms9918))
435 reply(
"error out of range");
438 if (!readExactly(base() + addr, count))
return 1;
441 else if (!strcmp(command,
"r"))
443 if (addr + count >
sizeof(*tms9918))
445 reply(
"error out of range");
448 printf(
"data %lu\n", count);
450 writeExactly(base() + addr, count);
452 else if (!strcmp(command,
"frames"))
455 unsigned long n = strtoul(line + consumed, NULL, 10);
456 for (
unsigned long i = 0; i < (n ? n : 1); ++i) renderFrame();
459 else if (!strcmp(command,
"view"))
465 renderFrameUnlocked();
466 size_t n = buildView();
470 reply(
"error nothing rendered yet");
473 printf(
"view %u\n", (
unsigned)n);
475 writeExactly(view, n);
477 else if (!strcmp(command,
"gpu"))
479 gpuStart((uint16_t)addr);
482 else if (!strcmp(command,
"gpupoll"))
487 reply(
"gpu running");
495 else if (!strcmp(command,
"gpustop"))
499 TMS_REGISTER(tms9918, 0x38) = 0;
502 else if (!strcmp(command,
"capture"))
511 renderFrameUnlocked();
513 rows = width = missed = 0;
514 renderFrameUnlocked();
518 reply(
"error capture overflowed");
521 printf(
"capture %lu %lu\n", (
unsigned long)rows, (
unsigned long)width);
523 writeExactly(capture, (
size_t)rows * width);
527 reply(
"error unknown command");
void pico9918_gpu_reset_time(void)
Reset the internal GPU time accumulator to 0.
void pico9918_gpu_init(pico9918_t *tms9918)
Initialize the TMS9900 GPU.
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...
uint32_t pico9918_gpu_time(uint32_t totalTime)
Return the GPU's CPU time in microseconds.
pico9918-core - GPU Interface
uint16_t pico9918_default_palette(int index)
a default palette entry, 0xargb
pico9918-core - core interface
void pico9918_frame_porch(pico9918_t *tms9918)
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.
pico9918_frame_geometry_t pico9918_frame_end(pico9918_t *tms9918, float tempC, float frameRateHz, pico9918_frame_display_t *display)
see the header.
void pico9918_frame_end_of_scanline(pico9918_t *tms9918)
see the header.
pico9918-core - frame module
pico9918-core - the private instance layout
the host's mutable vertical display parameters, as the end-of-frame geometry sees them
uint16_t vVirtualPixels
(in, and out when yScale > 1)
uint8_t vPixelScale
(in, and out when yScale > 1)
the vertical geometry the end of frame derives
uint32_t triggerScanline
vBorder + vPixels
the host's per-call display parameters, as the scanline sees them