29#if !PICO9918_SINGLE_INSTANCE
39static void appliedCallback(pico9918_t* tms9918,
void* userdata)
41 appliedSeen.inst = tms9918;
42 appliedSeen.userdata = userdata;
47#if PICO9918_BUILD_RUNTIME_CHIP
60#define FNV64_OFFSET 0xcbf29ce484222325ull
61#define FNV64_PRIME 0x00000100000001b3ull
62#define BADGE_DIGEST 0xc0ce60a2a13c9a2dull
65#define BADGE_BUFFER (PICO9918_F18A_BADGE_WIDTH + BADGE_GUARD)
66#define BADGE_LINE_PIXELS 640
68static uint64_t fnv1aByte(uint64_t h, uint8_t b)
70 return (h ^ b) * FNV64_PRIME;
73static void fillPixels(PICO9918_PIXEL_T* pixels, uint32_t count, PICO9918_PIXEL_T value)
75 for (uint32_t i = 0; i < count; ++i) pixels[i] = value;
81#if PICO9918_SINGLE_INSTANCE
87 printf(
"pico9918_new failed\n");
92#if PICO9918_BUILD_RUNTIME_CHIP
100 printf(
"a new instance is not PICO9918_CHIP_MAX\n");
107 printf(
"the chip did not step down\n");
119 printf(
"a TMS9918A honoured the unlock\n");
129 printf(
"a PICO9918 refused the unlock: VR33 0x%02x\n",
142 printf(
"an F18A reached the config port\n");
151 printf(
"a PICO9918 could not reach the config port\n");
165 printf(
"a TMS9918A with M4 and the text bit is not in 40-column text: mode %d\n",
175 printf(
"a TMS9918A with R0 bit 2 set is not in Graphics I: mode %d\n",
187 printf(
"a TMS9918A with M3 is not in Graphics II: mode %d\n",
214 printf(
"a TMS9918A dropped a VR8 write instead of masking it into VR0: VR0 0x%02x\n",
226 printf(
"a locked F18A refused 80-column text, which TurboForth needs\n");
234 printf(
"a locked F18A in 80 columns masked a VR8 write into VR0: VR0 0x%02x\n",
244 printf(
"a locked F18A with M4 clear dropped a VR8 write: VR0 0x%02x\n",
254 printf(
"a VR57 write was latched into VR1: VR1 0x%02x\n",
269 {0x1100, 0x0240}, {0x1810, 0x1050}, {0x2210, 0x2410}, {0x2211, 0x2411},
270 {0x2240, 0x2480}, {0x3210, 0x2450}, {0x3810, 0x3050},
275 for (
unsigned i = 0; i <
sizeof fourK /
sizeof fourK[0]; ++i)
277 const uint8_t marker = (uint8_t)(0xa0 + i);
285 printf(
"a 4K TMS9918A did not put >%04x at >%04x: found 0x%02x, and 0x%02x at >%04x\n", fourK[i].addr,
300 printf(
"a 16K TMS9918A moved >%04x anyway: found 0x%02x\n", fourK[i].addr,
317 printf(
"an F18A decoded R1 bit 7: >%04x holds 0x%02x and >%04x holds 0x%02x\n", fourK[0].addr,
335 printf(
"asking for PRO gave neither PRO nor the ceiling: chip %d\n",
342 printf(
"a PRO does not answer SR1 as a PICO9918: 0x%02x\n",
349 printf(
"pico9918-core: chip switch honoured, the register file and config port follow it\n");
354 const PICO9918_PIXEL_T sentinel = 0x0123;
356 uint64_t digest = FNV64_OFFSET;
360 fillPixels(badge, BADGE_BUFFER, sentinel);
362 if (!pico9918_f18a_badge_render(line, 0, badge))
364 printf(
"the F18A badge did not draw row %u\n", (
unsigned)line);
370 for (uint32_t g = 0; g < BADGE_GUARD; ++g)
374 printf(
"the F18A badge drew past column %u on row %u\n",
382 digest = fnv1aByte(digest, (uint8_t)(badge[x] & 0xff));
383 digest = fnv1aByte(digest, (uint8_t)((badge[x] >> 8) & 0xff));
390 if (digest != BADGE_DIGEST)
392 printf(
"the F18A badge artwork digest is 0x%016llx, expected 0x%016llx\n",
393 (
unsigned long long)digest, (
unsigned long long)BADGE_DIGEST);
398 pico9918_f18a_badge_render(0, 0, badge);
399 if (badge[0] != PICO9918_PIXEL_FROM_RGB12(0xb202))
401 printf(
"the F18A badge margin is 0x%04x, expected dark green\n", (
unsigned)badge[0]);
404 pico9918_f18a_badge_render(1, 0, badge);
405 if (badge[2] != PICO9918_PIXEL_FROM_RGB12(0xff0f))
407 printf(
"the F18A badge box edge is 0x%04x, expected white\n", (
unsigned)badge[2]);
415 printf(
"the F18A badge stopped one frame early\n");
419 fillPixels(badge, BADGE_BUFFER, sentinel);
421 badge[0] != sentinel)
427 fillPixels(badge, BADGE_BUFFER, sentinel);
429 badge[0] != sentinel)
439 PICO9918_PIXEL_T __aligned(4) out[BADGE_LINE_PIXELS];
452 fillPixels(out, BADGE_LINE_PIXELS, sentinel);
456 printf(
"output line %u reported no change with the F18A badge on it\n",
461 fillPixels(badge, BADGE_BUFFER, sentinel);
462 pico9918_f18a_badge_render((uint16_t)line, 0, badge);
466 if (out[x] != badge[x])
468 printf(
"output line %u pixel %u is 0x%04x, badge row %u has 0x%04x\n",
469 (
unsigned)line, (
unsigned)x, (
unsigned)out[x], (
unsigned)line,
478 fillPixels(out, BADGE_LINE_PIXELS, sentinel);
481 pico9918_f18a_badge_render(1, 0, badge);
482 if (out[2] == badge[2])
484 printf(
"a PICO9918 drew the F18A badge\n");
489 printf(
"pico9918-core: the F18A badge is pixel-exact, one row per output line\n");
504 printf(
"implausible line: %p, %u bytes\n", (
const void*)line, (
unsigned)bytes);
509 printf(
"mode is not Graphics I\n");
513 printf(
"pico9918-core: %u-byte line, status 0x%02x, palette entry 1 0x%08x\n",
522 printf(
"pico9918_config returned NULL\n");
526 config[PICO9918_CONF_DIAG] = 0;
527 config[PICO9918_CONF_DIAG_REGISTERS] = 1;
532 printf(
"config applied but the derived DIAG summary stayed clear\n");
536 config[PICO9918_CONF_DIAG_REGISTERS] = 0;
541 printf(
"every panel is off and the derived DIAG summary is still set\n");
545 printf(
"pico9918-core: config block reachable, %u bytes, applied both ways\n",
556 printf(
"status_value did not read SR0 back: 0x%02x\n",
564 printf(
"status_value ignores its register argument\n");
571 printf(
"status_value cleared what it returned\n");
579 printf(
"read_status left the flags standing\n");
583 printf(
"pico9918-core: status file readable without clearing it\n");
585#if !PICO9918_SINGLE_INSTANCE
592 printf(
"second pico9918_new failed\n");
606 if (firstPixel == secondPixel)
608 printf(
"two instances rendered the same line: 0x%02x\n", firstPixel);
611 printf(
"pico9918-core: two instances, lines 0x%02x and 0x%02x\n", firstPixel, secondPixel);
616 int firstTag = 0, secondTag = 0;
621 if (appliedSeen.calls != 1 || appliedSeen.inst != second || appliedSeen.userdata != &secondTag)
623 printf(
"the second instance's config-applied callback did not fire with its own "
624 "instance and userdata\n");
629 if (appliedSeen.calls != 2 || appliedSeen.inst != tms9918 || appliedSeen.userdata != &firstTag)
631 printf(
"registering on one instance disturbed the other's callback\n");
638 if (appliedSeen.calls != 2)
640 printf(
"a NULL registration still fired\n");
644 printf(
"pico9918-core: host callbacks registered per instance\n");
pico9918_mode_t pico9918_display_mode(pico9918_t *tms9918)
current display mode
void pico9918_destroy(pico9918_t *tms9918)
destroy a TMS9918
void pico9918_set_chip(pico9918_t *tms9918, pico9918_chip_t chip)
select which chip this instance answers as
const uint8_t * pico9918_line_source(pico9918_t *tms9918)
where the scanline just generated actually is.
uint8_t pico9918_status_value(pico9918_t *tms9918, pico9918_status_register_t reg)
return a status register value without the side effects of reading it
pico9918_chip_t pico9918_chip(pico9918_t *tms9918)
which chip this instance answers as
uint8_t pico9918_vram_value(pico9918_t *tms9918, uint16_t addr)
return a value from vram
uint32_t pico9918_line_bytes(pico9918_t *tms9918)
how many bytes of pixels[] this mode fills.
void pico9918_set_status(pico9918_t *tms9918, uint8_t status)
set status flag
uint8_t pico9918_scan_line(pico9918_t *tms9918, uint16_t y)
generate a scanline
void pico9918_reset(pico9918_t *tms9918)
reset the new TMS9918
uint8_t pico9918_read_status(pico9918_t *tms9918)
read from the status register
uint8_t pico9918_reg_value(pico9918_t *tms9918, pico9918_register_t reg)
return a register value - see the header for the locked-device aliasing
void pico9918_write_data(pico9918_t *tms9918, uint8_t data)
write data (mode = 0) to the tms9918
pico9918-core - core interface
pico9918_t * pico9918_new(void)
create a new TMS9918
#define PICO9918_SCANLINE_BUFFER_SIZE
the library's line buffer size - the active pixels plus the eight bytes past them that a fine-h-scrol...
#define TMS_R1_MODE_TEXT
40-column text
#define TMS_R1_RAM_16K
register 1 bits: VRAM size, blanking, interrupt, mode and sprite size
#define TMS_R0_MODE_TEXT_80
80-column text, with R1's text mode.
#define PICO9918_INST_ARG
declare the instance ahead of other parameters
#define PICO9918_INST_ONLY
pass the instance as the only argument
#define TMS_R1_DISP_ACTIVE
render the active display
#define TMS_R0_MODE_GRAPHICS_II
Graphics II - the only mode R0 selects.
#define TMS_R1_RAM_4K
4KB of VRAM
@ PICO9918_REG_CONFIG_INDEX
PICO9918 only: which configuration byte R59 addresses.
@ PICO9918_REG_CONFIG_VALUE
PICO9918 only: the configuration byte R58 selected.
@ PICO9918_REG_STATUS_SELECT
which status register S1 reads back, and the counter controls
@ PICO9918_REG_UNLOCK
0x1c twice unlocks the F18A personality; any other value locks
@ PICO9918_SR_STATUS
the TMS9918A status: interrupt, 5th sprite, collision, sprite number
@ PICO9918_SR_IDENT
chip identity, blanking, and the scanline interrupt flag
#define PICO9918_CHIP_MAX
the highest personality this build can be, and what a new instance is
#define PICO9918_R57_UNLOCK
the value register 57 takes, twice in a row, to unlock
@ PICO9918_CHIP_PICO9918
an F18A plus the PICO9918's own extensions
@ PICO9918_CHIP_TMS9918
a pre-A TMS9918: a TMS9918A without Graphics II
@ PICO9918_CHIP_PICO9918_PRO
a PICO9918 PRO: 8bpp 80-column text, its own splash
@ PICO9918_CHIP_TMS9918A
a TMS9918A: locked, no GPU, no extensions
@ PICO9918_CHIP_F18A
an F18A: unlock, enhanced renderer, GPU
#define PICO9918_INST
pass the instance ahead of other arguments
void pico9918_config_apply(pico9918_t *tms9918)
apply the config block's VDP-side effects: registers 50 and 30, the palette unpack,...
uint8_t * pico9918_config(pico9918_t *tms9918)
the instance's CONFIG_BYTES settings block
void pico9918_config_set_applied_callback(pico9918_t *tms9918, pico9918_config_applied_fn cb, void *userdata)
register the host's config-applied hook
pico9918-core - config byte layout
#define CONFIG_BYTES
size of the config block, in bytes
bool pico9918_frame_output_line(pico9918_t *tms9918, uint32_t outputLine, pico9918_scanline_params_t *params, PICO9918_PIXEL_T *pixels)
see the header.
pico9918-core - frame module
PICO9918_DLLEXPORT_CONST uint32_t pico9918_palette[]
the sixteen TMS9918 colours, each packed as 0xrrggbbaa
PICO9918_DLLEXPORT void pico9918_initialise_gfx_i(pico9918_t *tms9918)
program Graphics I, the default table addresses and black on cyan, then clear VRAM
pico9918-core - utility / helper functions
static void pico9918_set_address_write(pico9918_t *tms9918, uint16_t addr)
point the VRAM address register at addr for writing, ie.
static void pico9918_set_fg_bg_color(pico9918_t *tms9918, pico9918_color_t fg, pico9918_color_t bg)
set register 7, the text-mode foreground colour and the backdrop
static void pico9918_write_register_value(pico9918_t *tms9918, pico9918_register_t reg, uint8_t value)
write a VDP register as a host would, value byte first
pico9918-core - Splash overlay
#define PICO9918_F18A_BADGE_WIDTH
badge columns - must stay a multiple of 8, see splash.c
#define PICO9918_F18A_BADGE_HEIGHT
badge rows, each one OUTPUT line rather than one display line
#define PICO9918_F18A_BADGE_FRAMES
frames the badge is shown for, counting from reset
the host's per-call display parameters, as the scanline sees them