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",
259 printf(
"an unlocked F18A refused 80-column text\n");
265 printf(
"an unlocked F18A in 80 columns rendered a %u-byte line, expected %u\n",
275 printf(
"a VR57 write was latched into VR1: VR1 0x%02x\n",
290 {0x1100, 0x0240}, {0x1810, 0x1050}, {0x2210, 0x2410}, {0x2211, 0x2411},
291 {0x2240, 0x2480}, {0x3210, 0x2450}, {0x3810, 0x3050},
296 for (
unsigned i = 0; i <
sizeof fourK /
sizeof fourK[0]; ++i)
298 const uint8_t marker = (uint8_t)(0xa0 + i);
306 printf(
"a 4K TMS9918A did not put >%04x at >%04x: found 0x%02x, and 0x%02x at >%04x\n", fourK[i].addr,
321 printf(
"a 16K TMS9918A moved >%04x anyway: found 0x%02x\n", fourK[i].addr,
338 printf(
"an F18A decoded R1 bit 7: >%04x holds 0x%02x and >%04x holds 0x%02x\n", fourK[0].addr,
356 printf(
"asking for PRO gave neither PRO nor the ceiling: chip %d\n",
363 printf(
"a PRO does not answer SR1 as a PICO9918: 0x%02x\n",
370 printf(
"pico9918-core: chip switch honoured, the register file and config port follow it\n");
375 const PICO9918_PIXEL_T sentinel = 0x0123;
377 uint64_t digest = FNV64_OFFSET;
381 fillPixels(badge, BADGE_BUFFER, sentinel);
383 if (!pico9918_f18a_badge_render(line, 0, badge))
385 printf(
"the F18A badge did not draw row %u\n", (
unsigned)line);
391 for (uint32_t g = 0; g < BADGE_GUARD; ++g)
395 printf(
"the F18A badge drew past column %u on row %u\n",
403 digest = fnv1aByte(digest, (uint8_t)(badge[x] & 0xff));
404 digest = fnv1aByte(digest, (uint8_t)((badge[x] >> 8) & 0xff));
411 if (digest != BADGE_DIGEST)
413 printf(
"the F18A badge artwork digest is 0x%016llx, expected 0x%016llx\n",
414 (
unsigned long long)digest, (
unsigned long long)BADGE_DIGEST);
419 pico9918_f18a_badge_render(0, 0, badge);
420 if (badge[0] != PICO9918_PIXEL_FROM_RGB12(0xb202))
422 printf(
"the F18A badge margin is 0x%04x, expected dark green\n", (
unsigned)badge[0]);
425 pico9918_f18a_badge_render(1, 0, badge);
426 if (badge[2] != PICO9918_PIXEL_FROM_RGB12(0xff0f))
428 printf(
"the F18A badge box edge is 0x%04x, expected white\n", (
unsigned)badge[2]);
436 printf(
"the F18A badge stopped one frame early\n");
440 fillPixels(badge, BADGE_BUFFER, sentinel);
442 badge[0] != sentinel)
448 fillPixels(badge, BADGE_BUFFER, sentinel);
450 badge[0] != sentinel)
460 PICO9918_PIXEL_T __aligned(4) out[BADGE_LINE_PIXELS];
473 fillPixels(out, BADGE_LINE_PIXELS, sentinel);
477 printf(
"output line %u reported no change with the F18A badge on it\n",
482 fillPixels(badge, BADGE_BUFFER, sentinel);
483 pico9918_f18a_badge_render((uint16_t)line, 0, badge);
487 if (out[x] != badge[x])
489 printf(
"output line %u pixel %u is 0x%04x, badge row %u has 0x%04x\n",
490 (
unsigned)line, (
unsigned)x, (
unsigned)out[x], (
unsigned)line,
499 fillPixels(out, BADGE_LINE_PIXELS, sentinel);
502 pico9918_f18a_badge_render(1, 0, badge);
503 if (out[2] == badge[2])
505 printf(
"a PICO9918 drew the F18A badge\n");
510 printf(
"pico9918-core: the F18A badge is pixel-exact, one row per output line\n");
525 printf(
"implausible line: %p, %u bytes\n", (
const void*)line, (
unsigned)bytes);
530 printf(
"mode is not Graphics I\n");
534 printf(
"pico9918-core: %u-byte line, status 0x%02x, palette entry 1 0x%08x\n",
543 printf(
"pico9918_config returned NULL\n");
547 config[PICO9918_CONF_DIAG] = 0;
548 config[PICO9918_CONF_DIAG_REGISTERS] = 1;
553 printf(
"config applied but the derived DIAG summary stayed clear\n");
557 config[PICO9918_CONF_DIAG_REGISTERS] = 0;
562 printf(
"every panel is off and the derived DIAG summary is still set\n");
566 printf(
"pico9918-core: config block reachable, %u bytes, applied both ways\n",
577 printf(
"status_value did not read SR0 back: 0x%02x\n",
585 printf(
"status_value ignores its register argument\n");
592 printf(
"status_value cleared what it returned\n");
600 printf(
"read_status left the flags standing\n");
604 printf(
"pico9918-core: status file readable without clearing it\n");
606#if !PICO9918_SINGLE_INSTANCE
613 printf(
"second pico9918_new failed\n");
627 if (firstPixel == secondPixel)
629 printf(
"two instances rendered the same line: 0x%02x\n", firstPixel);
632 printf(
"pico9918-core: two instances, lines 0x%02x and 0x%02x\n", firstPixel, secondPixel);
637 int firstTag = 0, secondTag = 0;
642 if (appliedSeen.calls != 1 || appliedSeen.inst != second || appliedSeen.userdata != &secondTag)
644 printf(
"the second instance's config-applied callback did not fire with its own "
645 "instance and userdata\n");
650 if (appliedSeen.calls != 2 || appliedSeen.inst != tms9918 || appliedSeen.userdata != &firstTag)
652 printf(
"registering on one instance disturbed the other's callback\n");
659 if (appliedSeen.calls != 2)
661 printf(
"a NULL registration still fired\n");
665 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
#define PICO9918_SCANLINE_BYTES_MAX
the widest active line this build renders, in bytes
@ 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
uint8_t * pico9918_config(pico9918_t *tms9918)
the instance's PICO9918_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
void pico9918_config_apply_now(pico9918_t *tms9918, bool applyVdpEffects)
apply the block now, and cancel any apply already owed
pico9918-core - config byte layout
#define PICO9918_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